Home | History | Annotate | Download | only in wtf
      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