| OLD | NEW | 
|---|
|  | (Empty) | 
| 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 | h
     eight %.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/m
     ax %.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 | heigh
     t %.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 / 102
     4.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 |  | 
| OLD | NEW | 
|---|