Home | History | Annotate | Download | only in bytecode
      1 /*
      2  * Copyright (C) 2008 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  *
      8  * 1.  Redistributions of source code must retain the above copyright
      9  *     notice, this list of conditions and the following disclaimer.
     10  * 2.  Redistributions in binary form must reproduce the above copyright
     11  *     notice, this list of conditions and the following disclaimer in the
     12  *     documentation and/or other materials provided with the distribution.
     13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     14  *     its contributors may be used to endorse or promote products derived
     15  *     from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #ifndef SamplingTool_h
     30 #define SamplingTool_h
     31 
     32 #include <wtf/Assertions.h>
     33 #include <wtf/HashMap.h>
     34 #include <wtf/Threading.h>
     35 
     36 #include "Nodes.h"
     37 #include "Opcode.h"
     38 
     39 namespace JSC {
     40 
     41     class ScriptExecutable;
     42 
     43     class SamplingFlags {
     44         friend class JIT;
     45     public:
     46         static void start();
     47         static void stop();
     48 
     49 #if ENABLE(SAMPLING_FLAGS)
     50         static void setFlag(unsigned flag)
     51         {
     52             ASSERT(flag >= 1);
     53             ASSERT(flag <= 32);
     54             s_flags |= 1u << (flag - 1);
     55         }
     56 
     57         static void clearFlag(unsigned flag)
     58         {
     59             ASSERT(flag >= 1);
     60             ASSERT(flag <= 32);
     61             s_flags &= ~(1u << (flag - 1));
     62         }
     63 
     64         static void sample();
     65 
     66         class ScopedFlag {
     67         public:
     68             ScopedFlag(int flag)
     69                 : m_flag(flag)
     70             {
     71                 setFlag(flag);
     72             }
     73 
     74             ~ScopedFlag()
     75             {
     76                 clearFlag(m_flag);
     77             }
     78 
     79         private:
     80             int m_flag;
     81         };
     82 
     83 #endif
     84     private:
     85         static uint32_t s_flags;
     86 #if ENABLE(SAMPLING_FLAGS)
     87         static uint64_t s_flagCounts[33];
     88 #endif
     89     };
     90 
     91     class CodeBlock;
     92     class ExecState;
     93     class Interpreter;
     94     class ScopeNode;
     95     struct Instruction;
     96 
     97     struct ScriptSampleRecord {
     98         ScriptSampleRecord(ScriptExecutable* executable)
     99             : m_executable(executable)
    100             , m_codeBlock(0)
    101             , m_sampleCount(0)
    102             , m_opcodeSampleCount(0)
    103             , m_samples(0)
    104             , m_size(0)
    105         {
    106         }
    107 
    108         ~ScriptSampleRecord()
    109         {
    110             if (m_samples)
    111                 free(m_samples);
    112         }
    113 
    114         void sample(CodeBlock*, Instruction*);
    115 
    116         RefPtr<ScriptExecutable> m_executable;
    117         CodeBlock* m_codeBlock;
    118         int m_sampleCount;
    119         int m_opcodeSampleCount;
    120         int* m_samples;
    121         unsigned m_size;
    122     };
    123 
    124     typedef WTF::HashMap<ScriptExecutable*, ScriptSampleRecord*> ScriptSampleRecordMap;
    125 
    126     class SamplingThread {
    127     public:
    128         // Sampling thread state.
    129         static bool s_running;
    130         static unsigned s_hertz;
    131         static ThreadIdentifier s_samplingThread;
    132 
    133         static void start(unsigned hertz=10000);
    134         static void stop();
    135 
    136         static void* threadStartFunc(void*);
    137     };
    138 
    139     class SamplingTool {
    140     public:
    141         friend struct CallRecord;
    142         friend class HostCallRecord;
    143 
    144 #if ENABLE(OPCODE_SAMPLING)
    145         class CallRecord : public Noncopyable {
    146         public:
    147             CallRecord(SamplingTool* samplingTool)
    148                 : m_samplingTool(samplingTool)
    149                 , m_savedSample(samplingTool->m_sample)
    150                 , m_savedCodeBlock(samplingTool->m_codeBlock)
    151             {
    152             }
    153 
    154             ~CallRecord()
    155             {
    156                 m_samplingTool->m_sample = m_savedSample;
    157                 m_samplingTool->m_codeBlock = m_savedCodeBlock;
    158             }
    159 
    160         private:
    161             SamplingTool* m_samplingTool;
    162             intptr_t m_savedSample;
    163             CodeBlock* m_savedCodeBlock;
    164         };
    165 
    166         class HostCallRecord : public CallRecord {
    167         public:
    168             HostCallRecord(SamplingTool* samplingTool)
    169                 : CallRecord(samplingTool)
    170             {
    171                 samplingTool->m_sample |= 0x1;
    172             }
    173         };
    174 #else
    175         class CallRecord : public Noncopyable {
    176         public:
    177             CallRecord(SamplingTool*)
    178             {
    179             }
    180         };
    181 
    182         class HostCallRecord : public CallRecord {
    183         public:
    184             HostCallRecord(SamplingTool* samplingTool)
    185                 : CallRecord(samplingTool)
    186             {
    187             }
    188         };
    189 #endif
    190 
    191         SamplingTool(Interpreter* interpreter)
    192             : m_interpreter(interpreter)
    193             , m_codeBlock(0)
    194             , m_sample(0)
    195             , m_sampleCount(0)
    196             , m_opcodeSampleCount(0)
    197 #if ENABLE(CODEBLOCK_SAMPLING)
    198             , m_scopeSampleMap(new ScriptSampleRecordMap())
    199 #endif
    200         {
    201             memset(m_opcodeSamples, 0, sizeof(m_opcodeSamples));
    202             memset(m_opcodeSamplesInCTIFunctions, 0, sizeof(m_opcodeSamplesInCTIFunctions));
    203         }
    204 
    205         ~SamplingTool()
    206         {
    207 #if ENABLE(CODEBLOCK_SAMPLING)
    208             deleteAllValues(*m_scopeSampleMap);
    209 #endif
    210         }
    211 
    212         void setup();
    213         void dump(ExecState*);
    214 
    215         void notifyOfScope(ScriptExecutable* scope);
    216 
    217         void sample(CodeBlock* codeBlock, Instruction* vPC)
    218         {
    219             ASSERT(!(reinterpret_cast<intptr_t>(vPC) & 0x3));
    220             m_codeBlock = codeBlock;
    221             m_sample = reinterpret_cast<intptr_t>(vPC);
    222         }
    223 
    224         CodeBlock** codeBlockSlot() { return &m_codeBlock; }
    225         intptr_t* sampleSlot() { return &m_sample; }
    226 
    227         void* encodeSample(Instruction* vPC, bool inCTIFunction = false, bool inHostFunction = false)
    228         {
    229             ASSERT(!(reinterpret_cast<intptr_t>(vPC) & 0x3));
    230             return reinterpret_cast<void*>(reinterpret_cast<intptr_t>(vPC) | (static_cast<intptr_t>(inCTIFunction) << 1) | static_cast<intptr_t>(inHostFunction));
    231         }
    232 
    233         static void sample();
    234 
    235     private:
    236         class Sample {
    237         public:
    238             Sample(volatile intptr_t sample, CodeBlock* volatile codeBlock)
    239                 : m_sample(sample)
    240                 , m_codeBlock(codeBlock)
    241             {
    242             }
    243 
    244             bool isNull() { return !m_sample; }
    245             CodeBlock* codeBlock() { return m_codeBlock; }
    246             Instruction* vPC() { return reinterpret_cast<Instruction*>(m_sample & ~0x3); }
    247             bool inHostFunction() { return m_sample & 0x1; }
    248             bool inCTIFunction() { return m_sample & 0x2; }
    249 
    250         private:
    251             intptr_t m_sample;
    252             CodeBlock* m_codeBlock;
    253         };
    254 
    255         void doRun();
    256         static SamplingTool* s_samplingTool;
    257 
    258         Interpreter* m_interpreter;
    259 
    260         // State tracked by the main thread, used by the sampling thread.
    261         CodeBlock* m_codeBlock;
    262         intptr_t m_sample;
    263 
    264         // Gathered sample data.
    265         long long m_sampleCount;
    266         long long m_opcodeSampleCount;
    267         unsigned m_opcodeSamples[numOpcodeIDs];
    268         unsigned m_opcodeSamplesInCTIFunctions[numOpcodeIDs];
    269 
    270 #if ENABLE(CODEBLOCK_SAMPLING)
    271         Mutex m_scriptSampleMapMutex;
    272         OwnPtr<ScriptSampleRecordMap> m_scopeSampleMap;
    273 #endif
    274     };
    275 
    276     // AbstractSamplingCounter:
    277     //
    278     // Implements a named set of counters, printed on exit if ENABLE(SAMPLING_COUNTERS).
    279     // See subclasses below, SamplingCounter, GlobalSamplingCounter and DeletableSamplingCounter.
    280     class AbstractSamplingCounter {
    281         friend class JIT;
    282         friend class DeletableSamplingCounter;
    283     public:
    284         void count(uint32_t count = 1)
    285         {
    286             m_counter += count;
    287         }
    288 
    289         static void dump();
    290 
    291     protected:
    292         // Effectively the contructor, however called lazily in the case of GlobalSamplingCounter.
    293         void init(const char* name)
    294         {
    295             m_counter = 0;
    296             m_name = name;
    297 
    298             // Set m_next to point to the head of the chain, and inform whatever is
    299             // currently at the head that this node will now hold the pointer to it.
    300             m_next = s_abstractSamplingCounterChain;
    301             s_abstractSamplingCounterChain->m_referer = &m_next;
    302             // Add this node to the head of the list.
    303             s_abstractSamplingCounterChain = this;
    304             m_referer = &s_abstractSamplingCounterChain;
    305         }
    306 
    307         int64_t m_counter;
    308         const char* m_name;
    309         AbstractSamplingCounter* m_next;
    310         // This is a pointer to the pointer to this node in the chain; used to
    311         // allow fast linked list deletion.
    312         AbstractSamplingCounter** m_referer;
    313         // Null object used to detect end of static chain.
    314         static AbstractSamplingCounter s_abstractSamplingCounterChainEnd;
    315         static AbstractSamplingCounter* s_abstractSamplingCounterChain;
    316         static bool s_completed;
    317     };
    318 
    319 #if ENABLE(SAMPLING_COUNTERS)
    320     // SamplingCounter:
    321     //
    322     // This class is suitable and (hopefully!) convenient for cases where a counter is
    323     // required within the scope of a single function.  It can be instantiated as a
    324     // static variable since it contains a constructor but not a destructor (static
    325     // variables in WebKit cannot have destructors).
    326     //
    327     // For example:
    328     //
    329     // void someFunction()
    330     // {
    331     //     static SamplingCounter countMe("This is my counter.  There are many like it, but this one is mine.");
    332     //     countMe.count();
    333     //     // ...
    334     // }
    335     //
    336     class SamplingCounter : public AbstractSamplingCounter {
    337     public:
    338         SamplingCounter(const char* name) { init(name); }
    339     };
    340 
    341     // GlobalSamplingCounter:
    342     //
    343     // This class is suitable for use where a counter is to be declared globally,
    344     // since it contains neither a constructor nor destructor.  Instead, ensure
    345     // that 'name()' is called to provide the counter with a name (and also to
    346     // allow it to be printed out on exit).
    347     //
    348     // GlobalSamplingCounter globalCounter;
    349     //
    350     // void firstFunction()
    351     // {
    352     //     // Put this within a function that is definitely called!
    353     //     // (Or alternatively alongside all calls to 'count()').
    354     //     globalCounter.name("I Name You Destroyer.");
    355     //     globalCounter.count();
    356     //     // ...
    357     // }
    358     //
    359     // void secondFunction()
    360     // {
    361     //     globalCounter.count();
    362     //     // ...
    363     // }
    364     //
    365     class GlobalSamplingCounter : public AbstractSamplingCounter {
    366     public:
    367         void name(const char* name)
    368         {
    369             // Global objects should be mapped in zero filled memory, so this should
    370             // be a safe (albeit not necessarily threadsafe) check for 'first call'.
    371             if (!m_next)
    372                 init(name);
    373         }
    374     };
    375 
    376     // DeletableSamplingCounter:
    377     //
    378     // The above classes (SamplingCounter, GlobalSamplingCounter), are intended for
    379     // use within a global or static scope, and as such cannot have a destructor.
    380     // This means there is no convenient way for them to remove themselves from the
    381     // static list of counters, and should an instance of either class be freed
    382     // before 'dump()' has walked over the list it will potentially walk over an
    383     // invalid pointer.
    384     //
    385     // This class is intended for use where the counter may possibly be deleted before
    386     // the program exits.  Should this occur, the counter will print it's value to
    387     // stderr, and remove itself from the static list.  Example:
    388     //
    389     // DeletableSamplingCounter* counter = new DeletableSamplingCounter("The Counter With No Name");
    390     // counter->count();
    391     // delete counter;
    392     //
    393     class DeletableSamplingCounter : public AbstractSamplingCounter {
    394     public:
    395         DeletableSamplingCounter(const char* name) { init(name); }
    396 
    397         ~DeletableSamplingCounter()
    398         {
    399             if (!s_completed)
    400                 fprintf(stderr, "DeletableSamplingCounter \"%s\" deleted early (with count %lld)\n", m_name, m_counter);
    401             // Our m_referer pointer should know where the pointer to this node is,
    402             // and m_next should know that this node is the previous node in the list.
    403             ASSERT(*m_referer == this);
    404             ASSERT(m_next->m_referer == &m_next);
    405             // Remove this node from the list, and inform m_next that we have done so.
    406             m_next->m_referer = m_referer;
    407             *m_referer = m_next;
    408         }
    409     };
    410 #endif
    411 
    412 } // namespace JSC
    413 
    414 #endif // SamplingTool_h
    415