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 "Strong.h"
     33 #include "Nodes.h"
     34 #include "Opcode.h"
     35 #include <wtf/Assertions.h>
     36 #include <wtf/HashMap.h>
     37 #include <wtf/Threading.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(JSGlobalData& globalData, ScriptExecutable* executable)
     99             : m_executable(globalData, 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         Strong<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 {
    146             WTF_MAKE_NONCOPYABLE(CallRecord);
    147         public:
    148             CallRecord(SamplingTool* samplingTool)
    149                 : m_samplingTool(samplingTool)
    150                 , m_savedSample(samplingTool->m_sample)
    151                 , m_savedCodeBlock(samplingTool->m_codeBlock)
    152             {
    153             }
    154 
    155             ~CallRecord()
    156             {
    157                 m_samplingTool->m_sample = m_savedSample;
    158                 m_samplingTool->m_codeBlock = m_savedCodeBlock;
    159             }
    160 
    161         private:
    162             SamplingTool* m_samplingTool;
    163             intptr_t m_savedSample;
    164             CodeBlock* m_savedCodeBlock;
    165         };
    166 
    167         class HostCallRecord : public CallRecord {
    168         public:
    169             HostCallRecord(SamplingTool* samplingTool)
    170                 : CallRecord(samplingTool)
    171             {
    172                 samplingTool->m_sample |= 0x1;
    173             }
    174         };
    175 #else
    176         class CallRecord {
    177             WTF_MAKE_NONCOPYABLE(CallRecord);
    178         public:
    179             CallRecord(SamplingTool*)
    180             {
    181             }
    182         };
    183 
    184         class HostCallRecord : public CallRecord {
    185         public:
    186             HostCallRecord(SamplingTool* samplingTool)
    187                 : CallRecord(samplingTool)
    188             {
    189             }
    190         };
    191 #endif
    192 
    193         SamplingTool(Interpreter* interpreter)
    194             : m_interpreter(interpreter)
    195             , m_codeBlock(0)
    196             , m_sample(0)
    197             , m_sampleCount(0)
    198             , m_opcodeSampleCount(0)
    199 #if ENABLE(CODEBLOCK_SAMPLING)
    200             , m_scopeSampleMap(new ScriptSampleRecordMap())
    201 #endif
    202         {
    203             memset(m_opcodeSamples, 0, sizeof(m_opcodeSamples));
    204             memset(m_opcodeSamplesInCTIFunctions, 0, sizeof(m_opcodeSamplesInCTIFunctions));
    205         }
    206 
    207         ~SamplingTool()
    208         {
    209 #if ENABLE(CODEBLOCK_SAMPLING)
    210             deleteAllValues(*m_scopeSampleMap);
    211 #endif
    212         }
    213 
    214         void setup();
    215         void dump(ExecState*);
    216 
    217         void notifyOfScope(ScriptExecutable* scope);
    218 
    219         void sample(CodeBlock* codeBlock, Instruction* vPC)
    220         {
    221             ASSERT(!(reinterpret_cast<intptr_t>(vPC) & 0x3));
    222             m_codeBlock = codeBlock;
    223             m_sample = reinterpret_cast<intptr_t>(vPC);
    224         }
    225 
    226         CodeBlock** codeBlockSlot() { return &m_codeBlock; }
    227         intptr_t* sampleSlot() { return &m_sample; }
    228 
    229         void* encodeSample(Instruction* vPC, bool inCTIFunction = false, bool inHostFunction = false)
    230         {
    231             ASSERT(!(reinterpret_cast<intptr_t>(vPC) & 0x3));
    232             return reinterpret_cast<void*>(reinterpret_cast<intptr_t>(vPC) | (static_cast<intptr_t>(inCTIFunction) << 1) | static_cast<intptr_t>(inHostFunction));
    233         }
    234 
    235         static void sample();
    236 
    237     private:
    238         class Sample {
    239         public:
    240             Sample(volatile intptr_t sample, CodeBlock* volatile codeBlock)
    241                 : m_sample(sample)
    242                 , m_codeBlock(codeBlock)
    243             {
    244             }
    245 
    246             bool isNull() { return !m_sample; }
    247             CodeBlock* codeBlock() { return m_codeBlock; }
    248             Instruction* vPC() { return reinterpret_cast<Instruction*>(m_sample & ~0x3); }
    249             bool inHostFunction() { return m_sample & 0x1; }
    250             bool inCTIFunction() { return m_sample & 0x2; }
    251 
    252         private:
    253             intptr_t m_sample;
    254             CodeBlock* m_codeBlock;
    255         };
    256 
    257         void doRun();
    258         static SamplingTool* s_samplingTool;
    259 
    260         Interpreter* m_interpreter;
    261 
    262         // State tracked by the main thread, used by the sampling thread.
    263         CodeBlock* m_codeBlock;
    264         intptr_t m_sample;
    265 
    266         // Gathered sample data.
    267         long long m_sampleCount;
    268         long long m_opcodeSampleCount;
    269         unsigned m_opcodeSamples[numOpcodeIDs];
    270         unsigned m_opcodeSamplesInCTIFunctions[numOpcodeIDs];
    271 
    272 #if ENABLE(CODEBLOCK_SAMPLING)
    273         Mutex m_scriptSampleMapMutex;
    274         OwnPtr<ScriptSampleRecordMap> m_scopeSampleMap;
    275 #endif
    276     };
    277 
    278     // AbstractSamplingCounter:
    279     //
    280     // Implements a named set of counters, printed on exit if ENABLE(SAMPLING_COUNTERS).
    281     // See subclasses below, SamplingCounter, GlobalSamplingCounter and DeletableSamplingCounter.
    282     class AbstractSamplingCounter {
    283         friend class DeletableSamplingCounter;
    284     public:
    285         void count(uint32_t count = 1)
    286         {
    287             m_counter += count;
    288         }
    289 
    290         static void dump();
    291 
    292         int64_t* addressOfCounter() { return &m_counter; }
    293 
    294     protected:
    295         // Effectively the contructor, however called lazily in the case of GlobalSamplingCounter.
    296         void init(const char* name)
    297         {
    298             m_counter = 0;
    299             m_name = name;
    300 
    301             // Set m_next to point to the head of the chain, and inform whatever is
    302             // currently at the head that this node will now hold the pointer to it.
    303             m_next = s_abstractSamplingCounterChain;
    304             s_abstractSamplingCounterChain->m_referer = &m_next;
    305             // Add this node to the head of the list.
    306             s_abstractSamplingCounterChain = this;
    307             m_referer = &s_abstractSamplingCounterChain;
    308         }
    309 
    310         int64_t m_counter;
    311         const char* m_name;
    312         AbstractSamplingCounter* m_next;
    313         // This is a pointer to the pointer to this node in the chain; used to
    314         // allow fast linked list deletion.
    315         AbstractSamplingCounter** m_referer;
    316         // Null object used to detect end of static chain.
    317         static AbstractSamplingCounter s_abstractSamplingCounterChainEnd;
    318         static AbstractSamplingCounter* s_abstractSamplingCounterChain;
    319         static bool s_completed;
    320     };
    321 
    322 #if ENABLE(SAMPLING_COUNTERS)
    323     // SamplingCounter:
    324     //
    325     // This class is suitable and (hopefully!) convenient for cases where a counter is
    326     // required within the scope of a single function.  It can be instantiated as a
    327     // static variable since it contains a constructor but not a destructor (static
    328     // variables in WebKit cannot have destructors).
    329     //
    330     // For example:
    331     //
    332     // void someFunction()
    333     // {
    334     //     static SamplingCounter countMe("This is my counter.  There are many like it, but this one is mine.");
    335     //     countMe.count();
    336     //     // ...
    337     // }
    338     //
    339     class SamplingCounter : public AbstractSamplingCounter {
    340     public:
    341         SamplingCounter(const char* name) { init(name); }
    342     };
    343 
    344     // GlobalSamplingCounter:
    345     //
    346     // This class is suitable for use where a counter is to be declared globally,
    347     // since it contains neither a constructor nor destructor.  Instead, ensure
    348     // that 'name()' is called to provide the counter with a name (and also to
    349     // allow it to be printed out on exit).
    350     //
    351     // GlobalSamplingCounter globalCounter;
    352     //
    353     // void firstFunction()
    354     // {
    355     //     // Put this within a function that is definitely called!
    356     //     // (Or alternatively alongside all calls to 'count()').
    357     //     globalCounter.name("I Name You Destroyer.");
    358     //     globalCounter.count();
    359     //     // ...
    360     // }
    361     //
    362     // void secondFunction()
    363     // {
    364     //     globalCounter.count();
    365     //     // ...
    366     // }
    367     //
    368     class GlobalSamplingCounter : public AbstractSamplingCounter {
    369     public:
    370         void name(const char* name)
    371         {
    372             // Global objects should be mapped in zero filled memory, so this should
    373             // be a safe (albeit not necessarily threadsafe) check for 'first call'.
    374             if (!m_next)
    375                 init(name);
    376         }
    377     };
    378 
    379     // DeletableSamplingCounter:
    380     //
    381     // The above classes (SamplingCounter, GlobalSamplingCounter), are intended for
    382     // use within a global or static scope, and as such cannot have a destructor.
    383     // This means there is no convenient way for them to remove themselves from the
    384     // static list of counters, and should an instance of either class be freed
    385     // before 'dump()' has walked over the list it will potentially walk over an
    386     // invalid pointer.
    387     //
    388     // This class is intended for use where the counter may possibly be deleted before
    389     // the program exits.  Should this occur, the counter will print it's value to
    390     // stderr, and remove itself from the static list.  Example:
    391     //
    392     // DeletableSamplingCounter* counter = new DeletableSamplingCounter("The Counter With No Name");
    393     // counter->count();
    394     // delete counter;
    395     //
    396     class DeletableSamplingCounter : public AbstractSamplingCounter {
    397     public:
    398         DeletableSamplingCounter(const char* name) { init(name); }
    399 
    400         ~DeletableSamplingCounter()
    401         {
    402             if (!s_completed)
    403                 fprintf(stderr, "DeletableSamplingCounter \"%s\" deleted early (with count %lld)\n", m_name, m_counter);
    404             // Our m_referer pointer should know where the pointer to this node is,
    405             // and m_next should know that this node is the previous node in the list.
    406             ASSERT(*m_referer == this);
    407             ASSERT(m_next->m_referer == &m_next);
    408             // Remove this node from the list, and inform m_next that we have done so.
    409             m_next->m_referer = m_referer;
    410             *m_referer = m_next;
    411         }
    412     };
    413 #endif
    414 
    415 } // namespace JSC
    416 
    417 #endif // SamplingTool_h
    418