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 needToLog = false; 85 char* current = reinterpret_cast<char*>(this); 86 char* last = reinterpret_cast<char*>(t.m_currentCheckPoint); 87 88 // If there was no previous checkpoint, measure from the start of the stack: 89 if (!last) 90 last = t.m_stackStart; 91 92 // Update the reentry depth stats: 93 t.m_reentryDepth++; 94 if (t.m_reentryDepth > StackStats::s_maxReentryDepth) { 95 StackStats::s_maxReentryDepth = t.m_reentryDepth; 96 needToLog = true; 97 } 98 99 // Update the stack height stats: 100 int height = t.m_stackStart - current; 101 if (height > StackStats::s_maxStackHeight) { 102 StackStats::s_maxStackHeight = height; 103 needToLog = true; 104 } 105 106 // Update the checkpoint diff stats: 107 int diff = last - current; 108 if (diff > StackStats::s_maxCheckPointDiff) { 109 StackStats::s_maxCheckPointDiff = diff; 110 needToLog = true; 111 } 112 113 // Push this checkpoint: 114 m_prev = t.m_currentCheckPoint; 115 t.m_currentCheckPoint = this; 116 117 #if ENABLE(VERBOSE_STACK_STATS) 118 needToLog = true; // always log. 119 #endif 120 121 // Log this checkpoint if needed: 122 if (needToLog) 123 dataLogF(" CHECKPOINT %p diff %d/%.1fk/max %.1fk | reentry %d/max %d | height %.1fk/max %.1fk | stack %p size %.1fk\n", 124 this, diff, diff / 1024.0, StackStats::s_maxCheckPointDiff / 1024.0, 125 t.m_reentryDepth, StackStats::s_maxReentryDepth, 126 height / 1024.0, StackStats::s_maxStackHeight / 1024.0, 127 stack.origin(), stack.size() / 1024.0); 128 } 129 130 StackStats::CheckPoint::~CheckPoint() 131 { 132 MutexLocker locker(*StackStats::s_sharedLock); 133 WTFThreadData* threadData = const_cast<WTFThreadData*>(&wtfThreadData()); 134 StackStats::PerThreadStats& t = threadData->stackStats(); 135 136 // Pop to previous checkpoint: 137 t.m_currentCheckPoint = m_prev; 138 --t.m_reentryDepth; 139 140 // Log this checkpoint if needed: 141 #if ENABLE(VERBOSE_STACK_STATS) 142 if (!m_prev) { 143 const StackBounds& stack = threadData->stack(); 144 145 char* current = reinterpret_cast<char*>(this); 146 int height = t.m_stackStart - current; 147 148 dataLogF(" POP to %p diff max %.1fk | reentry %d/%d max | height %.1fk/max %.1fk | stack %p size %.1fk)\n", 149 this, StackStats::s_maxCheckPointDiff / 1024.0, 150 t.m_reentryDepth, StackStats::s_maxReentryDepth, 151 height / 1024.0, StackStats::s_maxStackHeight / 1024.0, 152 stack.origin(), stack.size() / 1024.0); 153 } 154 #endif 155 } 156 157 void StackStats::probe() 158 { 159 MutexLocker locker(*StackStats::s_sharedLock); 160 WTFThreadData* threadData = const_cast<WTFThreadData*>(&wtfThreadData()); 161 StackStats::PerThreadStats& t = threadData->stackStats(); 162 const StackBounds& stack = threadData->stack(); 163 164 bool needToLog = false; 165 166 int dummy; 167 char* current = reinterpret_cast<char*>(&dummy); 168 char* last = reinterpret_cast<char*>(t.m_currentCheckPoint); 169 170 // If there was no previous checkpoint, measure from the start of the stack: 171 if (!last) 172 last = t.m_stackStart; 173 174 // We did not reach another checkpoint yet. Hence, we do not touch the 175 // reentry stats. 176 177 // Update the stack height stats: 178 int height = t.m_stackStart - current; 179 if (height > StackStats::s_maxStackHeight) { 180 StackStats::s_maxStackHeight = height; 181 needToLog = true; 182 } 183 184 // Update the checkpoint diff stats: 185 int diff = last - current; 186 if (diff > StackStats::s_maxCheckPointDiff) { 187 StackStats::s_maxCheckPointDiff = diff; 188 needToLog = true; 189 } 190 191 #if ENABLE(VERBOSE_STACK_STATS) 192 needToLog = true; // always log. 193 #endif 194 195 if (needToLog) 196 dataLogF(" PROBE %p diff %d/%.1fk/max %.1fk | reentry %d/max %d | height %.1fk/max %.1fk | stack %p size %.1fk\n", 197 current, diff, diff / 1024.0, StackStats::s_maxCheckPointDiff / 1024.0, 198 t.m_reentryDepth, StackStats::s_maxReentryDepth, 199 height / 1024.0, StackStats::s_maxStackHeight / 1024.0, 200 stack.origin(), stack.size() / 1024.0); 201 } 202 203 StackStats::LayoutCheckPoint::LayoutCheckPoint() 204 { 205 // While a layout checkpoint is not necessarily a checkpoint where we 206 // we will do a recursion check, it is a convenient spot for doing a 207 // probe to measure the height of stack usage. 208 // 209 // We'll do this probe before we commence with the layout checkpoint. 210 // This is because the probe also locks the sharedLock. By calling the 211 // probe first, we can avoid re-entering the lock. 212 StackStats::probe(); 213 214 MutexLocker locker(*StackStats::s_sharedLock); 215 WTFThreadData* threadData = const_cast<WTFThreadData*>(&wtfThreadData()); 216 StackStats::PerThreadStats& t = threadData->stackStats(); 217 const StackBounds& stack = threadData->stack(); 218 219 // Push this checkpoint: 220 m_prev = StackStats::s_topLayoutCheckPoint; 221 if (m_prev) 222 m_depth = m_prev->m_depth + 1; 223 else { 224 StackStats::s_firstLayoutCheckPoint = this; 225 m_depth = 0; 226 } 227 StackStats::s_topLayoutCheckPoint = this; 228 229 // 230 char* current = reinterpret_cast<char*>(this); 231 char* last = reinterpret_cast<char*>(m_prev); 232 char* root = reinterpret_cast<char*>(StackStats::s_firstLayoutCheckPoint); 233 bool needToLog = false; 234 235 int diff = last - current; 236 if (!last) 237 diff = 0; 238 int totalDiff = root - current; 239 if (!root) 240 totalDiff = 0; 241 242 // Update the stack height stats: 243 int height = t.m_stackStart - current; 244 if (height > StackStats::s_maxStackHeight) { 245 StackStats::s_maxStackHeight = height; 246 needToLog = true; 247 } 248 249 // Update the layout checkpoint diff stats: 250 if (diff > StackStats::s_maxLayoutCheckPointDiff) { 251 StackStats::s_maxLayoutCheckPointDiff = diff; 252 needToLog = true; 253 } 254 255 // Update the total layout checkpoint diff stats: 256 if (totalDiff > StackStats::s_maxTotalLayoutCheckPointDiff) { 257 StackStats::s_maxTotalLayoutCheckPointDiff = totalDiff; 258 needToLog = true; 259 } 260 261 #if ENABLE(VERBOSE_STACK_STATS) 262 needToLog = true; // always log. 263 #endif 264 265 if (needToLog) 266 dataLogF(" LAYOUT %p diff %d/%.1fk/max %.1fk | reentry %d/max %d | height %.1fk/max %.1fk | stack %p size %.1fk\n", 267 current, diff, diff / 1024.0, StackStats::s_maxLayoutCheckPointDiff / 1024.0, 268 m_depth, StackStats::s_maxLayoutReentryDepth, 269 totalDiff / 1024.0, StackStats::s_maxTotalLayoutCheckPointDiff / 1024.0, 270 stack.origin(), stack.size() / 1024.0); 271 } 272 273 StackStats::LayoutCheckPoint::~LayoutCheckPoint() 274 { 275 MutexLocker locker(*StackStats::s_sharedLock); 276 277 // Pop to the previous layout checkpoint: 278 StackStats::s_topLayoutCheckPoint = m_prev; 279 if (!m_depth) 280 StackStats::s_firstLayoutCheckPoint = 0; 281 } 282 283 } // namespace WTF 284 285 #endif // ENABLE(STACK_STATS) 286 287