#include "config.h"
#include "StackStats.h"
#if ENABLE(STACK_STATS)
#include "Assertions.h"
#include "DataLog.h"
#include "WTFThreadData.h"
#define ENABLE_VERBOSE_STACK_STATS 1
namespace WTF {
std::mutex* StackStats::s_sharedMutex = 0;
StackStats::CheckPoint* StackStats::s_topCheckPoint = 0;
StackStats::LayoutCheckPoint* StackStats::s_firstLayoutCheckPoint = 0;
StackStats::LayoutCheckPoint* StackStats::s_topLayoutCheckPoint = 0;
int StackStats::s_maxCheckPointDiff = 0;
int StackStats::s_maxStackHeight = 0;
int StackStats::s_maxReentryDepth = 0;
int StackStats::s_maxLayoutCheckPointDiff = 0;
int StackStats::s_maxTotalLayoutCheckPointDiff = 0;
int StackStats::s_maxLayoutReentryDepth = 0;
void StackStats::initialize()
{
s_sharedMutex = std::make_unique<std::mutex>().release();
dataLogF(" === LOG new stack stats ========\n");
}
StackStats::PerThreadStats::PerThreadStats()
{
const StackBounds& stack = wtfThreadData().stack();
m_reentryDepth = 0;
m_stackStart = (char*)stack.origin();
m_currentCheckPoint = 0;
dataLogF(" === THREAD new stackStart %p ========\n", m_stackStart);
}
StackStats::CheckPoint::CheckPoint()
{
std::lock_guard<std::mutex> lock(*StackStats::s_sharedMutex);
WTFThreadData* threadData = const_cast<WTFThreadData*>(&wtfThreadData());
StackStats::PerThreadStats& t = threadData->stackStats();
const StackBounds& stack = threadData->stack();
bool isGrowingDownward = stack.isGrowingDownward();
bool needToLog = false;
char* current = reinterpret_cast<char*>(this);
char* last = reinterpret_cast<char*>(t.m_currentCheckPoint);
if (!last)
last = t.m_stackStart;
t.m_reentryDepth++;
if (t.m_reentryDepth > StackStats::s_maxReentryDepth) {
StackStats::s_maxReentryDepth = t.m_reentryDepth;
needToLog = true;
}
int height = t.m_stackStart - current;
if (!isGrowingDownward)
height = -height;
if (height > StackStats::s_maxStackHeight) {
StackStats::s_maxStackHeight = height;
needToLog = true;
}
int diff = last - current;
if (!isGrowingDownward)
diff = -diff;
if (diff > StackStats::s_maxCheckPointDiff) {
StackStats::s_maxCheckPointDiff = diff;
needToLog = true;
}
m_prev = t.m_currentCheckPoint;
t.m_currentCheckPoint = this;
#if ENABLE(VERBOSE_STACK_STATS)
needToLog = true; #endif
if (needToLog)
dataLogF(" CHECKPOINT %p diff %d/%.1fk/max %.1fk | reentry %d/max %d | height %.1fk/max %.1fk | stack %p size %.1fk\n",
this, diff, diff / 1024.0, StackStats::s_maxCheckPointDiff / 1024.0,
t.m_reentryDepth, StackStats::s_maxReentryDepth,
height / 1024.0, StackStats::s_maxStackHeight / 1024.0,
stack.origin(), stack.size() / 1024.0);
}
StackStats::CheckPoint::~CheckPoint()
{
std::lock_guard<std::mutex> lock(*StackStats::s_sharedMutex);
WTFThreadData* threadData = const_cast<WTFThreadData*>(&wtfThreadData());
StackStats::PerThreadStats& t = threadData->stackStats();
t.m_currentCheckPoint = m_prev;
--t.m_reentryDepth;
#if ENABLE(VERBOSE_STACK_STATS)
if (!m_prev) {
const StackBounds& stack = threadData->stack();
bool isGrowingDownward = stack.isGrowingDownward();
char* current = reinterpret_cast<char*>(this);
int height = t.m_stackStart - current;
if (!isGrowingDownward)
height = -height;
dataLogF(" POP to %p diff max %.1fk | reentry %d/%d max | height %.1fk/max %.1fk | stack %p size %.1fk)\n",
this, StackStats::s_maxCheckPointDiff / 1024.0,
t.m_reentryDepth, StackStats::s_maxReentryDepth,
height / 1024.0, StackStats::s_maxStackHeight / 1024.0,
stack.origin(), stack.size() / 1024.0);
}
#endif
}
void StackStats::probe()
{
std::lock_guard<std::mutex> lock(*StackStats::s_sharedMutex);
WTFThreadData* threadData = const_cast<WTFThreadData*>(&wtfThreadData());
StackStats::PerThreadStats& t = threadData->stackStats();
const StackBounds& stack = threadData->stack();
bool isGrowingDownward = stack.isGrowingDownward();
bool needToLog = false;
int dummy;
char* current = reinterpret_cast<char*>(&dummy);
char* last = reinterpret_cast<char*>(t.m_currentCheckPoint);
if (!last)
last = t.m_stackStart;
int height = t.m_stackStart - current;
if (!isGrowingDownward)
height = -height;
if (height > StackStats::s_maxStackHeight) {
StackStats::s_maxStackHeight = height;
needToLog = true;
}
int diff = last - current;
if (!isGrowingDownward)
diff = -diff;
if (diff > StackStats::s_maxCheckPointDiff) {
StackStats::s_maxCheckPointDiff = diff;
needToLog = true;
}
#if ENABLE(VERBOSE_STACK_STATS)
needToLog = true; #endif
if (needToLog)
dataLogF(" PROBE %p diff %d/%.1fk/max %.1fk | reentry %d/max %d | height %.1fk/max %.1fk | stack %p size %.1fk\n",
current, diff, diff / 1024.0, StackStats::s_maxCheckPointDiff / 1024.0,
t.m_reentryDepth, StackStats::s_maxReentryDepth,
height / 1024.0, StackStats::s_maxStackHeight / 1024.0,
stack.origin(), stack.size() / 1024.0);
}
StackStats::LayoutCheckPoint::LayoutCheckPoint()
{
StackStats::probe();
std::lock_guard<std::mutex> lock(*StackStats::s_sharedMutex);
WTFThreadData* threadData = const_cast<WTFThreadData*>(&wtfThreadData());
StackStats::PerThreadStats& t = threadData->stackStats();
const StackBounds& stack = threadData->stack();
bool isGrowingDownward = stack.isGrowingDownward();
m_prev = StackStats::s_topLayoutCheckPoint;
if (m_prev)
m_depth = m_prev->m_depth + 1;
else {
StackStats::s_firstLayoutCheckPoint = this;
m_depth = 0;
}
StackStats::s_topLayoutCheckPoint = this;
char* current = reinterpret_cast<char*>(this);
char* last = reinterpret_cast<char*>(m_prev);
char* root = reinterpret_cast<char*>(StackStats::s_firstLayoutCheckPoint);
bool needToLog = false;
int diff = last - current;
if (!last)
diff = 0;
int totalDiff = root - current;
if (!root)
totalDiff = 0;
int height = t.m_stackStart - current;
if (!isGrowingDownward)
height = -height;
if (height > StackStats::s_maxStackHeight) {
StackStats::s_maxStackHeight = height;
needToLog = true;
}
if (!isGrowingDownward)
diff = -diff;
if (diff > StackStats::s_maxLayoutCheckPointDiff) {
StackStats::s_maxLayoutCheckPointDiff = diff;
needToLog = true;
}
if (!isGrowingDownward)
totalDiff = -totalDiff;
if (totalDiff > StackStats::s_maxTotalLayoutCheckPointDiff) {
StackStats::s_maxTotalLayoutCheckPointDiff = totalDiff;
needToLog = true;
}
#if ENABLE(VERBOSE_STACK_STATS)
needToLog = true; #endif
if (needToLog)
dataLogF(" LAYOUT %p diff %d/%.1fk/max %.1fk | reentry %d/max %d | height %.1fk/max %.1fk | stack %p size %.1fk\n",
current, diff, diff / 1024.0, StackStats::s_maxLayoutCheckPointDiff / 1024.0,
m_depth, StackStats::s_maxLayoutReentryDepth,
totalDiff / 1024.0, StackStats::s_maxTotalLayoutCheckPointDiff / 1024.0,
stack.origin(), stack.size() / 1024.0);
}
StackStats::LayoutCheckPoint::~LayoutCheckPoint()
{
std::lock_guard<std::mutex> lock(*StackStats::s_sharedMutex);
StackStats::s_topLayoutCheckPoint = m_prev;
if (!m_depth)
StackStats::s_firstLayoutCheckPoint = 0;
}
}
#endif // ENABLE(STACK_STATS)