2 * Copyright (C) 2012 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "StackStats.h"
29 #if ENABLE(STACK_STATS)
31 #include "Assertions.h"
33 #include "WTFThreadData.h"
35 // Define the following flag if you want to collect stats on every single
36 // checkpoint. By default, we only log checkpoints that establish new
39 // #define ENABLE_VERBOSE_STACK_STATS 1
44 // CheckPoint management:
45 Mutex* StackStats::s_sharedLock = 0;
46 StackStats::CheckPoint* StackStats::s_topCheckPoint = 0;
47 StackStats::LayoutCheckPoint* StackStats::s_firstLayoutCheckPoint = 0;
48 StackStats::LayoutCheckPoint* StackStats::s_topLayoutCheckPoint = 0;
50 // High watermark stats:
51 int StackStats::s_maxCheckPointDiff = 0;
52 int StackStats::s_maxStackHeight = 0;
53 int StackStats::s_maxReentryDepth = 0;
55 int StackStats::s_maxLayoutCheckPointDiff = 0;
56 int StackStats::s_maxTotalLayoutCheckPointDiff = 0;
57 int StackStats::s_maxLayoutReentryDepth = 0;
60 // Initializes locks and the log. Should only be called once.
61 void StackStats::initialize()
63 s_sharedLock = new Mutex();
64 dataLogF(" === LOG new stack stats ========\n");
67 StackStats::PerThreadStats::PerThreadStats()
69 const StackBounds& stack = wtfThreadData().stack();
71 m_stackStart = (char*)stack.origin();
72 m_currentCheckPoint = 0;
74 dataLogF(" === THREAD new stackStart %p ========\n", m_stackStart);
77 StackStats::CheckPoint::CheckPoint()
79 MutexLocker locker(*StackStats::s_sharedLock);
80 WTFThreadData* threadData = const_cast<WTFThreadData*>(&wtfThreadData());
81 StackStats::PerThreadStats& t = threadData->stackStats();
82 const StackBounds& stack = threadData->stack();
84 bool isGrowingDownward = stack.isGrowingDownward();
85 bool needToLog = false;
86 char* current = reinterpret_cast<char*>(this);
87 char* last = reinterpret_cast<char*>(t.m_currentCheckPoint);
89 // If there was no previous checkpoint, measure from the start of the stack:
91 last = t.m_stackStart;
93 // Update the reentry depth stats:
95 if (t.m_reentryDepth > StackStats::s_maxReentryDepth) {
96 StackStats::s_maxReentryDepth = t.m_reentryDepth;
100 // Update the stack height stats:
101 int height = t.m_stackStart - current;
102 if (!isGrowingDownward)
104 if (height > StackStats::s_maxStackHeight) {
105 StackStats::s_maxStackHeight = height;
109 // Update the checkpoint diff stats:
110 int diff = last - current;
111 if (!isGrowingDownward)
113 if (diff > StackStats::s_maxCheckPointDiff) {
114 StackStats::s_maxCheckPointDiff = diff;
118 // Push this checkpoint:
119 m_prev = t.m_currentCheckPoint;
120 t.m_currentCheckPoint = this;
122 #if ENABLE(VERBOSE_STACK_STATS)
123 needToLog = true; // always log.
126 // Log this checkpoint if needed:
128 dataLogF(" CHECKPOINT %p diff %d/%.1fk/max %.1fk | reentry %d/max %d | height %.1fk/max %.1fk | stack %p size %.1fk\n",
129 this, diff, diff / 1024.0, StackStats::s_maxCheckPointDiff / 1024.0,
130 t.m_reentryDepth, StackStats::s_maxReentryDepth,
131 height / 1024.0, StackStats::s_maxStackHeight / 1024.0,
132 stack.origin(), stack.size() / 1024.0);
135 StackStats::CheckPoint::~CheckPoint()
137 MutexLocker locker(*StackStats::s_sharedLock);
138 WTFThreadData* threadData = const_cast<WTFThreadData*>(&wtfThreadData());
139 StackStats::PerThreadStats& t = threadData->stackStats();
141 // Pop to previous checkpoint:
142 t.m_currentCheckPoint = m_prev;
145 // Log this checkpoint if needed:
146 #if ENABLE(VERBOSE_STACK_STATS)
148 const StackBounds& stack = threadData->stack();
149 bool isGrowingDownward = stack.isGrowingDownward();
151 char* current = reinterpret_cast<char*>(this);
152 int height = t.m_stackStart - current;
154 if (!isGrowingDownward)
157 dataLogF(" POP to %p diff max %.1fk | reentry %d/%d max | height %.1fk/max %.1fk | stack %p size %.1fk)\n",
158 this, StackStats::s_maxCheckPointDiff / 1024.0,
159 t.m_reentryDepth, StackStats::s_maxReentryDepth,
160 height / 1024.0, StackStats::s_maxStackHeight / 1024.0,
161 stack.origin(), stack.size() / 1024.0);
166 void StackStats::probe()
168 MutexLocker locker(*StackStats::s_sharedLock);
169 WTFThreadData* threadData = const_cast<WTFThreadData*>(&wtfThreadData());
170 StackStats::PerThreadStats& t = threadData->stackStats();
171 const StackBounds& stack = threadData->stack();
173 bool isGrowingDownward = stack.isGrowingDownward();
175 bool needToLog = false;
178 char* current = reinterpret_cast<char*>(&dummy);
179 char* last = reinterpret_cast<char*>(t.m_currentCheckPoint);
181 // If there was no previous checkpoint, measure from the start of the stack:
183 last = t.m_stackStart;
185 // We did not reach another checkpoint yet. Hence, we do not touch the
188 // Update the stack height stats:
189 int height = t.m_stackStart - current;
190 if (!isGrowingDownward)
192 if (height > StackStats::s_maxStackHeight) {
193 StackStats::s_maxStackHeight = height;
197 // Update the checkpoint diff stats:
198 int diff = last - current;
199 if (!isGrowingDownward)
201 if (diff > StackStats::s_maxCheckPointDiff) {
202 StackStats::s_maxCheckPointDiff = diff;
206 #if ENABLE(VERBOSE_STACK_STATS)
207 needToLog = true; // always log.
211 dataLogF(" PROBE %p diff %d/%.1fk/max %.1fk | reentry %d/max %d | height %.1fk/max %.1fk | stack %p size %.1fk\n",
212 current, diff, diff / 1024.0, StackStats::s_maxCheckPointDiff / 1024.0,
213 t.m_reentryDepth, StackStats::s_maxReentryDepth,
214 height / 1024.0, StackStats::s_maxStackHeight / 1024.0,
215 stack.origin(), stack.size() / 1024.0);
218 StackStats::LayoutCheckPoint::LayoutCheckPoint()
220 // While a layout checkpoint is not necessarily a checkpoint where we
221 // we will do a recursion check, it is a convenient spot for doing a
222 // probe to measure the height of stack usage.
224 // We'll do this probe before we commence with the layout checkpoint.
225 // This is because the probe also locks the sharedLock. By calling the
226 // probe first, we can avoid re-entering the lock.
229 MutexLocker locker(*StackStats::s_sharedLock);
230 WTFThreadData* threadData = const_cast<WTFThreadData*>(&wtfThreadData());
231 StackStats::PerThreadStats& t = threadData->stackStats();
232 const StackBounds& stack = threadData->stack();
234 bool isGrowingDownward = stack.isGrowingDownward();
236 // Push this checkpoint:
237 m_prev = StackStats::s_topLayoutCheckPoint;
239 m_depth = m_prev->m_depth + 1;
241 StackStats::s_firstLayoutCheckPoint = this;
244 StackStats::s_topLayoutCheckPoint = this;
247 char* current = reinterpret_cast<char*>(this);
248 char* last = reinterpret_cast<char*>(m_prev);
249 char* root = reinterpret_cast<char*>(StackStats::s_firstLayoutCheckPoint);
250 bool needToLog = false;
252 int diff = last - current;
255 int totalDiff = root - current;
259 // Update the stack height stats:
260 int height = t.m_stackStart - current;
261 if (!isGrowingDownward)
263 if (height > StackStats::s_maxStackHeight) {
264 StackStats::s_maxStackHeight = height;
268 // Update the layout checkpoint diff stats:
269 if (!isGrowingDownward)
271 if (diff > StackStats::s_maxLayoutCheckPointDiff) {
272 StackStats::s_maxLayoutCheckPointDiff = diff;
276 // Update the total layout checkpoint diff stats:
277 if (!isGrowingDownward)
278 totalDiff = -totalDiff;
279 if (totalDiff > StackStats::s_maxTotalLayoutCheckPointDiff) {
280 StackStats::s_maxTotalLayoutCheckPointDiff = totalDiff;
284 #if ENABLE(VERBOSE_STACK_STATS)
285 needToLog = true; // always log.
289 dataLogF(" LAYOUT %p diff %d/%.1fk/max %.1fk | reentry %d/max %d | height %.1fk/max %.1fk | stack %p size %.1fk\n",
290 current, diff, diff / 1024.0, StackStats::s_maxLayoutCheckPointDiff / 1024.0,
291 m_depth, StackStats::s_maxLayoutReentryDepth,
292 totalDiff / 1024.0, StackStats::s_maxTotalLayoutCheckPointDiff / 1024.0,
293 stack.origin(), stack.size() / 1024.0);
296 StackStats::LayoutCheckPoint::~LayoutCheckPoint()
298 MutexLocker locker(*StackStats::s_sharedLock);
300 // Pop to the previous layout checkpoint:
301 StackStats::s_topLayoutCheckPoint = m_prev;
303 StackStats::s_firstLayoutCheckPoint = 0;
308 #endif // ENABLE(STACK_STATS)