diff options
Diffstat (limited to 'Source/WTF/wtf/StackStats.cpp')
-rw-r--r-- | Source/WTF/wtf/StackStats.cpp | 302 |
1 files changed, 302 insertions, 0 deletions
diff --git a/Source/WTF/wtf/StackStats.cpp b/Source/WTF/wtf/StackStats.cpp new file mode 100644 index 000000000..064f2f052 --- /dev/null +++ b/Source/WTF/wtf/StackStats.cpp @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "StackStats.h" + +#if ENABLE(STACK_STATS) + +#include "Assertions.h" +#include "DataLog.h" +#include "WTFThreadData.h" + +// Define the following flag if you want to collect stats on every single +// checkpoint. By default, we only log checkpoints that establish new +// max values. + +#define ENABLE_VERBOSE_STACK_STATS 1 + + +namespace WTF { + +// CheckPoint management: +StaticLock StackStats::s_sharedMutex; +StackStats::CheckPoint* StackStats::s_topCheckPoint = 0; +StackStats::LayoutCheckPoint* StackStats::s_firstLayoutCheckPoint = 0; +StackStats::LayoutCheckPoint* StackStats::s_topLayoutCheckPoint = 0; + +// High watermark stats: +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; + + +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<StaticLock> 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 there was no previous checkpoint, measure from the start of the stack: + if (!last) + last = t.m_stackStart; + + // Update the reentry depth stats: + t.m_reentryDepth++; + if (t.m_reentryDepth > StackStats::s_maxReentryDepth) { + StackStats::s_maxReentryDepth = t.m_reentryDepth; + needToLog = true; + } + + // Update the stack height stats: + int height = t.m_stackStart - current; + if (!isGrowingDownward) + height = -height; + if (height > StackStats::s_maxStackHeight) { + StackStats::s_maxStackHeight = height; + needToLog = true; + } + + // Update the checkpoint diff stats: + int diff = last - current; + if (!isGrowingDownward) + diff = -diff; + if (diff > StackStats::s_maxCheckPointDiff) { + StackStats::s_maxCheckPointDiff = diff; + needToLog = true; + } + + // Push this checkpoint: + m_prev = t.m_currentCheckPoint; + t.m_currentCheckPoint = this; + +#if ENABLE(VERBOSE_STACK_STATS) + needToLog = true; // always log. +#endif + + // Log this checkpoint if needed: + 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<StaticLock> lock(StackStats::s_sharedMutex); + WTFThreadData* threadData = const_cast<WTFThreadData*>(&wtfThreadData()); + StackStats::PerThreadStats& t = threadData->stackStats(); + + // Pop to previous checkpoint: + t.m_currentCheckPoint = m_prev; + --t.m_reentryDepth; + + // Log this checkpoint if needed: +#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<StaticLock> 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 there was no previous checkpoint, measure from the start of the stack: + if (!last) + last = t.m_stackStart; + + // We did not reach another checkpoint yet. Hence, we do not touch the + // reentry stats. + + // Update the stack height stats: + int height = t.m_stackStart - current; + if (!isGrowingDownward) + height = -height; + if (height > StackStats::s_maxStackHeight) { + StackStats::s_maxStackHeight = height; + needToLog = true; + } + + // Update the checkpoint diff stats: + 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; // always log. +#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() +{ + // While a layout checkpoint is not necessarily a checkpoint where we + // we will do a recursion check, it is a convenient spot for doing a + // probe to measure the height of stack usage. + // + // We'll do this probe before we commence with the layout checkpoint. + // This is because the probe also locks the sharedLock. By calling the + // probe first, we can avoid re-entering the lock. + StackStats::probe(); + + std::lock_guard<StaticLock> lock(StackStats::s_sharedMutex); + WTFThreadData* threadData = const_cast<WTFThreadData*>(&wtfThreadData()); + StackStats::PerThreadStats& t = threadData->stackStats(); + const StackBounds& stack = threadData->stack(); + + bool isGrowingDownward = stack.isGrowingDownward(); + + // Push this checkpoint: + 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; + + // Update the stack height stats: + int height = t.m_stackStart - current; + if (!isGrowingDownward) + height = -height; + if (height > StackStats::s_maxStackHeight) { + StackStats::s_maxStackHeight = height; + needToLog = true; + } + + // Update the layout checkpoint diff stats: + if (!isGrowingDownward) + diff = -diff; + if (diff > StackStats::s_maxLayoutCheckPointDiff) { + StackStats::s_maxLayoutCheckPointDiff = diff; + needToLog = true; + } + + // Update the total layout checkpoint diff stats: + if (!isGrowingDownward) + totalDiff = -totalDiff; + if (totalDiff > StackStats::s_maxTotalLayoutCheckPointDiff) { + StackStats::s_maxTotalLayoutCheckPointDiff = totalDiff; + needToLog = true; + } + +#if ENABLE(VERBOSE_STACK_STATS) + needToLog = true; // always log. +#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<StaticLock> lock(StackStats::s_sharedMutex); + + // Pop to the previous layout checkpoint: + StackStats::s_topLayoutCheckPoint = m_prev; + if (!m_depth) + StackStats::s_firstLayoutCheckPoint = 0; +} + +} // namespace WTF + +#endif // ENABLE(STACK_STATS) + |