Rename dataLog() and dataLogV() to dataLogF() and dataLogFV()
[WebKit-https.git] / Source / WTF / wtf / StackStats.cpp
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 "StackStats.h"
28
29 #if ENABLE(STACK_STATS) 
30
31 #include "Assertions.h"
32 #include "DataLog.h"
33 #include "WTFThreadData.h"
34
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
37 // max values.
38
39 // #define ENABLE_VERBOSE_STACK_STATS 1
40
41
42 namespace WTF {
43
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;
49
50 // High watermark stats:
51 int StackStats::s_maxCheckPointDiff = 0;
52 int StackStats::s_maxStackHeight = 0;
53 int StackStats::s_maxReentryDepth = 0;
54
55 int StackStats::s_maxLayoutCheckPointDiff = 0;
56 int StackStats::s_maxTotalLayoutCheckPointDiff = 0;
57 int StackStats::s_maxLayoutReentryDepth = 0;
58
59
60 // Initializes locks and the log. Should only be called once.
61 void StackStats::initialize()
62 {
63     s_sharedLock = new Mutex();
64     dataLogF(" === LOG new stack stats ========\n");
65 }
66
67 StackStats::PerThreadStats::PerThreadStats()
68 {
69     const StackBounds& stack = wtfThreadData().stack();
70     m_reentryDepth = 0;
71     m_stackStart = (char*)stack.origin();
72     m_currentCheckPoint = 0;
73
74     dataLogF(" === THREAD new stackStart %p ========\n", m_stackStart);
75 }
76
77 StackStats::CheckPoint::CheckPoint()
78 {
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();
83
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);
88
89     // If there was no previous checkpoint, measure from the start of the stack:
90     if (!last)
91         last = t.m_stackStart;
92
93     // Update the reentry depth stats:
94     t.m_reentryDepth++;
95     if (t.m_reentryDepth > StackStats::s_maxReentryDepth) {
96         StackStats::s_maxReentryDepth = t.m_reentryDepth;
97         needToLog = true;
98     }
99
100     // Update the stack height stats:
101     int height = t.m_stackStart - current;
102     if (!isGrowingDownward)
103         height = -height;
104     if (height > StackStats::s_maxStackHeight) {
105         StackStats::s_maxStackHeight = height;
106         needToLog = true;
107     }
108
109     // Update the checkpoint diff stats:
110     int diff = last - current;
111     if (!isGrowingDownward)
112         diff = -diff;
113     if (diff > StackStats::s_maxCheckPointDiff) {
114         StackStats::s_maxCheckPointDiff = diff;
115         needToLog = true;
116     }
117
118     // Push this checkpoint:
119     m_prev = t.m_currentCheckPoint;
120     t.m_currentCheckPoint = this;
121
122 #if ENABLE(VERBOSE_STACK_STATS)
123     needToLog = true; // always log.
124 #endif
125
126     // Log this checkpoint if needed:
127     if (needToLog)
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);
133 }
134
135 StackStats::CheckPoint::~CheckPoint()
136 {
137     MutexLocker locker(*StackStats::s_sharedLock);
138     WTFThreadData* threadData = const_cast<WTFThreadData*>(&wtfThreadData());
139     StackStats::PerThreadStats& t = threadData->stackStats();
140
141     // Pop to previous checkpoint:
142     t.m_currentCheckPoint = m_prev;
143     --t.m_reentryDepth;
144
145     // Log this checkpoint if needed:
146 #if ENABLE(VERBOSE_STACK_STATS)
147     if (!m_prev) {
148         const StackBounds& stack = threadData->stack();
149         bool isGrowingDownward = stack.isGrowingDownward();
150
151         char* current = reinterpret_cast<char*>(this);
152         int height = t.m_stackStart - current;
153
154         if (!isGrowingDownward)
155             height = -height;
156
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);
162     }
163 #endif
164 }
165
166 void StackStats::probe()
167 {
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();
172
173     bool isGrowingDownward = stack.isGrowingDownward();
174
175     bool needToLog = false;
176
177     int dummy;
178     char* current = reinterpret_cast<char*>(&dummy);
179     char* last = reinterpret_cast<char*>(t.m_currentCheckPoint);
180
181     // If there was no previous checkpoint, measure from the start of the stack:
182     if (!last)
183         last = t.m_stackStart;
184
185     // We did not reach another checkpoint yet. Hence, we do not touch the
186     // reentry stats.
187
188     // Update the stack height stats:
189     int height = t.m_stackStart - current;
190     if (!isGrowingDownward)
191         height = -height;
192     if (height > StackStats::s_maxStackHeight) {
193         StackStats::s_maxStackHeight = height;
194         needToLog = true;
195     }
196
197     // Update the checkpoint diff stats:
198     int diff = last - current;
199     if (!isGrowingDownward)
200         diff = -diff;
201     if (diff > StackStats::s_maxCheckPointDiff) {
202         StackStats::s_maxCheckPointDiff = diff;
203         needToLog = true;
204     }
205
206 #if ENABLE(VERBOSE_STACK_STATS)
207     needToLog = true; // always log.
208 #endif
209
210     if (needToLog)
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);
216 }
217
218 StackStats::LayoutCheckPoint::LayoutCheckPoint()
219 {
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.
223     //
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.
227     StackStats::probe();
228
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();
233
234     bool isGrowingDownward = stack.isGrowingDownward();
235
236     // Push this checkpoint:
237     m_prev = StackStats::s_topLayoutCheckPoint;
238     if (m_prev)
239         m_depth = m_prev->m_depth + 1;
240     else {
241         StackStats::s_firstLayoutCheckPoint = this;
242         m_depth = 0;
243     }
244     StackStats::s_topLayoutCheckPoint = this;
245
246     // 
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;
251
252     int diff = last - current;
253     if (!last)
254         diff = 0;
255     int totalDiff = root - current;
256     if (!root)
257         totalDiff = 0;
258
259     // Update the stack height stats:
260     int height = t.m_stackStart - current;
261     if (!isGrowingDownward)
262         height = -height;
263     if (height > StackStats::s_maxStackHeight) {
264         StackStats::s_maxStackHeight = height;
265         needToLog = true;
266     }
267
268     // Update the layout checkpoint diff stats:
269     if (!isGrowingDownward)
270         diff = -diff;
271     if (diff > StackStats::s_maxLayoutCheckPointDiff) {
272         StackStats::s_maxLayoutCheckPointDiff = diff;
273         needToLog = true;
274     }
275
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;
281         needToLog = true;
282     }
283
284 #if ENABLE(VERBOSE_STACK_STATS)
285     needToLog = true; // always log.
286 #endif
287
288     if (needToLog)
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);
294 }
295
296 StackStats::LayoutCheckPoint::~LayoutCheckPoint()
297 {
298     MutexLocker locker(*StackStats::s_sharedLock);
299
300     // Pop to the previous layout checkpoint:
301     StackStats::s_topLayoutCheckPoint = m_prev;
302     if (!m_depth)
303         StackStats::s_firstLayoutCheckPoint = 0;
304 }
305
306 } // namespace WTF
307
308 #endif // ENABLE(STACK_STATS)
309