1/*
2 * Copyright (C) 2012 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
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.
12 *
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.
24 */
25
26#include "config.h"
27#include <wtf/StackStats.h>
28
29#if ENABLE(STACK_STATS)
30
31#include <wtf/Assertions.h>
32#include <wtf/DataLog.h>
33
34// Define the following flag if you want to collect stats on every single
35// checkpoint. By default, we only log checkpoints that establish new
36// max values.
37
38#define ENABLE_VERBOSE_STACK_STATS 1
39
40
41namespace WTF {
42
43// CheckPoint management:
44Lock StackStats::s_sharedMutex;
45StackStats::CheckPoint* StackStats::s_topCheckPoint = 0;
46StackStats::LayoutCheckPoint* StackStats::s_firstLayoutCheckPoint = 0;
47StackStats::LayoutCheckPoint* StackStats::s_topLayoutCheckPoint = 0;
48
49// High watermark stats:
50int StackStats::s_maxCheckPointDiff = 0;
51int StackStats::s_maxStackHeight = 0;
52int StackStats::s_maxReentryDepth = 0;
53
54int StackStats::s_maxLayoutCheckPointDiff = 0;
55int StackStats::s_maxTotalLayoutCheckPointDiff = 0;
56int StackStats::s_maxLayoutReentryDepth = 0;
57
58
59StackStats::PerThreadStats::PerThreadStats()
60{
61 const StackBounds& stack = Thread::current().stack();
62 m_reentryDepth = 0;
63 m_stackStart = (char*)stack.origin();
64 m_currentCheckPoint = 0;
65
66 dataLogF(" === THREAD new stackStart %p ========\n", m_stackStart);
67}
68
69StackStats::CheckPoint::CheckPoint()
70{
71 std::lock_guard<Lock> lock(StackStats::s_sharedMutex);
72 Thread& thread = Thread::current();
73 StackStats::PerThreadStats& t = thread.stackStats();
74 const StackBounds& stack = thread.stack();
75
76 bool isGrowingDownward = stack.isGrowingDownward();
77 bool needToLog = false;
78 char* current = reinterpret_cast<char*>(this);
79 char* last = reinterpret_cast<char*>(t.m_currentCheckPoint);
80
81 // If there was no previous checkpoint, measure from the start of the stack:
82 if (!last)
83 last = t.m_stackStart;
84
85 // Update the reentry depth stats:
86 t.m_reentryDepth++;
87 if (t.m_reentryDepth > StackStats::s_maxReentryDepth) {
88 StackStats::s_maxReentryDepth = t.m_reentryDepth;
89 needToLog = true;
90 }
91
92 // Update the stack height stats:
93 int height = t.m_stackStart - current;
94 if (!isGrowingDownward)
95 height = -height;
96 if (height > StackStats::s_maxStackHeight) {
97 StackStats::s_maxStackHeight = height;
98 needToLog = true;
99 }
100
101 // Update the checkpoint diff stats:
102 int diff = last - current;
103 if (!isGrowingDownward)
104 diff = -diff;
105 if (diff > StackStats::s_maxCheckPointDiff) {
106 StackStats::s_maxCheckPointDiff = diff;
107 needToLog = true;
108 }
109
110 // Push this checkpoint:
111 m_prev = t.m_currentCheckPoint;
112 t.m_currentCheckPoint = this;
113
114#if ENABLE(VERBOSE_STACK_STATS)
115 needToLog = true; // always log.
116#endif
117
118 // Log this checkpoint if needed:
119 if (needToLog)
120 dataLogF(" CHECKPOINT %p diff %d/%.1fk/max %.1fk | reentry %d/max %d | height %.1fk/max %.1fk | stack %p size %.1fk\n",
121 this, diff, diff / 1024.0, StackStats::s_maxCheckPointDiff / 1024.0,
122 t.m_reentryDepth, StackStats::s_maxReentryDepth,
123 height / 1024.0, StackStats::s_maxStackHeight / 1024.0,
124 stack.origin(), stack.size() / 1024.0);
125}
126
127StackStats::CheckPoint::~CheckPoint()
128{
129 std::lock_guard<Lock> lock(StackStats::s_sharedMutex);
130 Thread& thread = Thread::current();
131 StackStats::PerThreadStats& t = thread.stackStats();
132
133 // Pop to previous checkpoint:
134 t.m_currentCheckPoint = m_prev;
135 --t.m_reentryDepth;
136
137 // Log this checkpoint if needed:
138#if ENABLE(VERBOSE_STACK_STATS)
139 if (!m_prev) {
140 const StackBounds& stack = thread.stack();
141 bool isGrowingDownward = stack.isGrowingDownward();
142
143 char* current = reinterpret_cast<char*>(this);
144 int height = t.m_stackStart - current;
145
146 if (!isGrowingDownward)
147 height = -height;
148
149 dataLogF(" POP to %p diff max %.1fk | reentry %d/%d max | height %.1fk/max %.1fk | stack %p size %.1fk)\n",
150 this, StackStats::s_maxCheckPointDiff / 1024.0,
151 t.m_reentryDepth, StackStats::s_maxReentryDepth,
152 height / 1024.0, StackStats::s_maxStackHeight / 1024.0,
153 stack.origin(), stack.size() / 1024.0);
154 }
155#endif
156}
157
158void StackStats::probe()
159{
160 std::lock_guard<Lock> lock(StackStats::s_sharedMutex);
161 Thread& thread = Thread::current();
162 StackStats::PerThreadStats& t = thread.stackStats();
163 const StackBounds& stack = thread.stack();
164
165 bool isGrowingDownward = stack.isGrowingDownward();
166
167 bool needToLog = false;
168
169 int dummy;
170 char* current = reinterpret_cast<char*>(&dummy);
171 char* last = reinterpret_cast<char*>(t.m_currentCheckPoint);
172
173 // If there was no previous checkpoint, measure from the start of the stack:
174 if (!last)
175 last = t.m_stackStart;
176
177 // We did not reach another checkpoint yet. Hence, we do not touch the
178 // reentry stats.
179
180 // Update the stack height stats:
181 int height = t.m_stackStart - current;
182 if (!isGrowingDownward)
183 height = -height;
184 if (height > StackStats::s_maxStackHeight) {
185 StackStats::s_maxStackHeight = height;
186 needToLog = true;
187 }
188
189 // Update the checkpoint diff stats:
190 int diff = last - current;
191 if (!isGrowingDownward)
192 diff = -diff;
193 if (diff > StackStats::s_maxCheckPointDiff) {
194 StackStats::s_maxCheckPointDiff = diff;
195 needToLog = true;
196 }
197
198#if ENABLE(VERBOSE_STACK_STATS)
199 needToLog = true; // always log.
200#endif
201
202 if (needToLog)
203 dataLogF(" PROBE %p diff %d/%.1fk/max %.1fk | reentry %d/max %d | height %.1fk/max %.1fk | stack %p size %.1fk\n",
204 current, diff, diff / 1024.0, StackStats::s_maxCheckPointDiff / 1024.0,
205 t.m_reentryDepth, StackStats::s_maxReentryDepth,
206 height / 1024.0, StackStats::s_maxStackHeight / 1024.0,
207 stack.origin(), stack.size() / 1024.0);
208}
209
210StackStats::LayoutCheckPoint::LayoutCheckPoint()
211{
212 // While a layout checkpoint is not necessarily a checkpoint where we
213 // we will do a recursion check, it is a convenient spot for doing a
214 // probe to measure the height of stack usage.
215 //
216 // We'll do this probe before we commence with the layout checkpoint.
217 // This is because the probe also locks the sharedLock. By calling the
218 // probe first, we can avoid re-entering the lock.
219 StackStats::probe();
220
221 std::lock_guard<Lock> lock(StackStats::s_sharedMutex);
222 Thread& thread = Thread::current();
223 StackStats::PerThreadStats& t = thread.stackStats();
224 const StackBounds& stack = thread.stack();
225
226 bool isGrowingDownward = stack.isGrowingDownward();
227
228 // Push this checkpoint:
229 m_prev = StackStats::s_topLayoutCheckPoint;
230 if (m_prev)
231 m_depth = m_prev->m_depth + 1;
232 else {
233 StackStats::s_firstLayoutCheckPoint = this;
234 m_depth = 0;
235 }
236 StackStats::s_topLayoutCheckPoint = this;
237
238 //
239 char* current = reinterpret_cast<char*>(this);
240 char* last = reinterpret_cast<char*>(m_prev);
241 char* root = reinterpret_cast<char*>(StackStats::s_firstLayoutCheckPoint);
242 bool needToLog = false;
243
244 int diff = last - current;
245 if (!last)
246 diff = 0;
247 int totalDiff = root - current;
248 if (!root)
249 totalDiff = 0;
250
251 // Update the stack height stats:
252 int height = t.m_stackStart - current;
253 if (!isGrowingDownward)
254 height = -height;
255 if (height > StackStats::s_maxStackHeight) {
256 StackStats::s_maxStackHeight = height;
257 needToLog = true;
258 }
259
260 // Update the layout checkpoint diff stats:
261 if (!isGrowingDownward)
262 diff = -diff;
263 if (diff > StackStats::s_maxLayoutCheckPointDiff) {
264 StackStats::s_maxLayoutCheckPointDiff = diff;
265 needToLog = true;
266 }
267
268 // Update the total layout checkpoint diff stats:
269 if (!isGrowingDownward)
270 totalDiff = -totalDiff;
271 if (totalDiff > StackStats::s_maxTotalLayoutCheckPointDiff) {
272 StackStats::s_maxTotalLayoutCheckPointDiff = totalDiff;
273 needToLog = true;
274 }
275
276#if ENABLE(VERBOSE_STACK_STATS)
277 needToLog = true; // always log.
278#endif
279
280 if (needToLog)
281 dataLogF(" LAYOUT %p diff %d/%.1fk/max %.1fk | reentry %d/max %d | height %.1fk/max %.1fk | stack %p size %.1fk\n",
282 current, diff, diff / 1024.0, StackStats::s_maxLayoutCheckPointDiff / 1024.0,
283 m_depth, StackStats::s_maxLayoutReentryDepth,
284 totalDiff / 1024.0, StackStats::s_maxTotalLayoutCheckPointDiff / 1024.0,
285 stack.origin(), stack.size() / 1024.0);
286}
287
288StackStats::LayoutCheckPoint::~LayoutCheckPoint()
289{
290 std::lock_guard<Lock> lock(StackStats::s_sharedMutex);
291
292 // Pop to the previous layout checkpoint:
293 StackStats::s_topLayoutCheckPoint = m_prev;
294 if (!m_depth)
295 StackStats::s_firstLayoutCheckPoint = 0;
296}
297
298} // namespace WTF
299
300#endif // ENABLE(STACK_STATS)
301
302