| /* |
| * Copyright (C) 2008 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of |
| * its contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #ifndef SamplingTool_h |
| #define SamplingTool_h |
| |
| #include "Strong.h" |
| #include "Nodes.h" |
| #include "Opcode.h" |
| #include <wtf/Assertions.h> |
| #include <wtf/HashMap.h> |
| #include <wtf/Threading.h> |
| |
| namespace JSC { |
| |
| class ScriptExecutable; |
| |
| class SamplingFlags { |
| friend class JIT; |
| public: |
| static void start(); |
| static void stop(); |
| |
| #if ENABLE(SAMPLING_FLAGS) |
| static void setFlag(unsigned flag) |
| { |
| ASSERT(flag >= 1); |
| ASSERT(flag <= 32); |
| s_flags |= 1u << (flag - 1); |
| } |
| |
| static void clearFlag(unsigned flag) |
| { |
| ASSERT(flag >= 1); |
| ASSERT(flag <= 32); |
| s_flags &= ~(1u << (flag - 1)); |
| } |
| |
| static void sample(); |
| |
| class ScopedFlag { |
| public: |
| ScopedFlag(int flag) |
| : m_flag(flag) |
| { |
| setFlag(flag); |
| } |
| |
| ~ScopedFlag() |
| { |
| clearFlag(m_flag); |
| } |
| |
| private: |
| int m_flag; |
| }; |
| |
| #endif |
| private: |
| static uint32_t s_flags; |
| #if ENABLE(SAMPLING_FLAGS) |
| static uint64_t s_flagCounts[33]; |
| #endif |
| }; |
| |
| class CodeBlock; |
| class ExecState; |
| class Interpreter; |
| class ScopeNode; |
| struct Instruction; |
| |
| struct ScriptSampleRecord { |
| ScriptSampleRecord(JSGlobalData& globalData, ScriptExecutable* executable) |
| : m_executable(globalData, executable) |
| , m_codeBlock(0) |
| , m_sampleCount(0) |
| , m_opcodeSampleCount(0) |
| , m_samples(0) |
| , m_size(0) |
| { |
| } |
| |
| ~ScriptSampleRecord() |
| { |
| if (m_samples) |
| free(m_samples); |
| } |
| |
| void sample(CodeBlock*, Instruction*); |
| |
| Strong<ScriptExecutable> m_executable; |
| CodeBlock* m_codeBlock; |
| int m_sampleCount; |
| int m_opcodeSampleCount; |
| int* m_samples; |
| unsigned m_size; |
| }; |
| |
| typedef WTF::HashMap<ScriptExecutable*, ScriptSampleRecord*> ScriptSampleRecordMap; |
| |
| class SamplingThread { |
| public: |
| // Sampling thread state. |
| static bool s_running; |
| static unsigned s_hertz; |
| static ThreadIdentifier s_samplingThread; |
| |
| static void start(unsigned hertz=10000); |
| static void stop(); |
| |
| static void* threadStartFunc(void*); |
| }; |
| |
| class SamplingTool { |
| public: |
| friend struct CallRecord; |
| friend class HostCallRecord; |
| |
| #if ENABLE(OPCODE_SAMPLING) |
| class CallRecord { |
| WTF_MAKE_NONCOPYABLE(CallRecord); |
| public: |
| CallRecord(SamplingTool* samplingTool) |
| : m_samplingTool(samplingTool) |
| , m_savedSample(samplingTool->m_sample) |
| , m_savedCodeBlock(samplingTool->m_codeBlock) |
| { |
| } |
| |
| ~CallRecord() |
| { |
| m_samplingTool->m_sample = m_savedSample; |
| m_samplingTool->m_codeBlock = m_savedCodeBlock; |
| } |
| |
| private: |
| SamplingTool* m_samplingTool; |
| intptr_t m_savedSample; |
| CodeBlock* m_savedCodeBlock; |
| }; |
| |
| class HostCallRecord : public CallRecord { |
| public: |
| HostCallRecord(SamplingTool* samplingTool) |
| : CallRecord(samplingTool) |
| { |
| samplingTool->m_sample |= 0x1; |
| } |
| }; |
| #else |
| class CallRecord { |
| WTF_MAKE_NONCOPYABLE(CallRecord); |
| public: |
| CallRecord(SamplingTool*) |
| { |
| } |
| }; |
| |
| class HostCallRecord : public CallRecord { |
| public: |
| HostCallRecord(SamplingTool* samplingTool) |
| : CallRecord(samplingTool) |
| { |
| } |
| }; |
| #endif |
| |
| SamplingTool(Interpreter* interpreter) |
| : m_interpreter(interpreter) |
| , m_codeBlock(0) |
| , m_sample(0) |
| , m_sampleCount(0) |
| , m_opcodeSampleCount(0) |
| #if ENABLE(CODEBLOCK_SAMPLING) |
| , m_scopeSampleMap(new ScriptSampleRecordMap()) |
| #endif |
| { |
| memset(m_opcodeSamples, 0, sizeof(m_opcodeSamples)); |
| memset(m_opcodeSamplesInCTIFunctions, 0, sizeof(m_opcodeSamplesInCTIFunctions)); |
| } |
| |
| ~SamplingTool() |
| { |
| #if ENABLE(CODEBLOCK_SAMPLING) |
| deleteAllValues(*m_scopeSampleMap); |
| #endif |
| } |
| |
| void setup(); |
| void dump(ExecState*); |
| |
| void notifyOfScope(ScriptExecutable* scope); |
| |
| void sample(CodeBlock* codeBlock, Instruction* vPC) |
| { |
| ASSERT(!(reinterpret_cast<intptr_t>(vPC) & 0x3)); |
| m_codeBlock = codeBlock; |
| m_sample = reinterpret_cast<intptr_t>(vPC); |
| } |
| |
| CodeBlock** codeBlockSlot() { return &m_codeBlock; } |
| intptr_t* sampleSlot() { return &m_sample; } |
| |
| void* encodeSample(Instruction* vPC, bool inCTIFunction = false, bool inHostFunction = false) |
| { |
| ASSERT(!(reinterpret_cast<intptr_t>(vPC) & 0x3)); |
| return reinterpret_cast<void*>(reinterpret_cast<intptr_t>(vPC) | (static_cast<intptr_t>(inCTIFunction) << 1) | static_cast<intptr_t>(inHostFunction)); |
| } |
| |
| static void sample(); |
| |
| private: |
| class Sample { |
| public: |
| Sample(volatile intptr_t sample, CodeBlock* volatile codeBlock) |
| : m_sample(sample) |
| , m_codeBlock(codeBlock) |
| { |
| } |
| |
| bool isNull() { return !m_sample; } |
| CodeBlock* codeBlock() { return m_codeBlock; } |
| Instruction* vPC() { return reinterpret_cast<Instruction*>(m_sample & ~0x3); } |
| bool inHostFunction() { return m_sample & 0x1; } |
| bool inCTIFunction() { return m_sample & 0x2; } |
| |
| private: |
| intptr_t m_sample; |
| CodeBlock* m_codeBlock; |
| }; |
| |
| void doRun(); |
| static SamplingTool* s_samplingTool; |
| |
| Interpreter* m_interpreter; |
| |
| // State tracked by the main thread, used by the sampling thread. |
| CodeBlock* m_codeBlock; |
| intptr_t m_sample; |
| |
| // Gathered sample data. |
| long long m_sampleCount; |
| long long m_opcodeSampleCount; |
| unsigned m_opcodeSamples[numOpcodeIDs]; |
| unsigned m_opcodeSamplesInCTIFunctions[numOpcodeIDs]; |
| |
| #if ENABLE(CODEBLOCK_SAMPLING) |
| Mutex m_scriptSampleMapMutex; |
| OwnPtr<ScriptSampleRecordMap> m_scopeSampleMap; |
| #endif |
| }; |
| |
| // AbstractSamplingCounter: |
| // |
| // Implements a named set of counters, printed on exit if ENABLE(SAMPLING_COUNTERS). |
| // See subclasses below, SamplingCounter, GlobalSamplingCounter and DeletableSamplingCounter. |
| class AbstractSamplingCounter { |
| friend class DeletableSamplingCounter; |
| public: |
| void count(uint32_t count = 1) |
| { |
| m_counter += count; |
| } |
| |
| static void dump(); |
| |
| int64_t* addressOfCounter() { return &m_counter; } |
| |
| protected: |
| // Effectively the contructor, however called lazily in the case of GlobalSamplingCounter. |
| void init(const char* name) |
| { |
| m_counter = 0; |
| m_name = name; |
| |
| // Set m_next to point to the head of the chain, and inform whatever is |
| // currently at the head that this node will now hold the pointer to it. |
| m_next = s_abstractSamplingCounterChain; |
| s_abstractSamplingCounterChain->m_referer = &m_next; |
| // Add this node to the head of the list. |
| s_abstractSamplingCounterChain = this; |
| m_referer = &s_abstractSamplingCounterChain; |
| } |
| |
| int64_t m_counter; |
| const char* m_name; |
| AbstractSamplingCounter* m_next; |
| // This is a pointer to the pointer to this node in the chain; used to |
| // allow fast linked list deletion. |
| AbstractSamplingCounter** m_referer; |
| // Null object used to detect end of static chain. |
| static AbstractSamplingCounter s_abstractSamplingCounterChainEnd; |
| static AbstractSamplingCounter* s_abstractSamplingCounterChain; |
| static bool s_completed; |
| }; |
| |
| #if ENABLE(SAMPLING_COUNTERS) |
| // SamplingCounter: |
| // |
| // This class is suitable and (hopefully!) convenient for cases where a counter is |
| // required within the scope of a single function. It can be instantiated as a |
| // static variable since it contains a constructor but not a destructor (static |
| // variables in WebKit cannot have destructors). |
| // |
| // For example: |
| // |
| // void someFunction() |
| // { |
| // static SamplingCounter countMe("This is my counter. There are many like it, but this one is mine."); |
| // countMe.count(); |
| // // ... |
| // } |
| // |
| class SamplingCounter : public AbstractSamplingCounter { |
| public: |
| SamplingCounter(const char* name) { init(name); } |
| }; |
| |
| // GlobalSamplingCounter: |
| // |
| // This class is suitable for use where a counter is to be declared globally, |
| // since it contains neither a constructor nor destructor. Instead, ensure |
| // that 'name()' is called to provide the counter with a name (and also to |
| // allow it to be printed out on exit). |
| // |
| // GlobalSamplingCounter globalCounter; |
| // |
| // void firstFunction() |
| // { |
| // // Put this within a function that is definitely called! |
| // // (Or alternatively alongside all calls to 'count()'). |
| // globalCounter.name("I Name You Destroyer."); |
| // globalCounter.count(); |
| // // ... |
| // } |
| // |
| // void secondFunction() |
| // { |
| // globalCounter.count(); |
| // // ... |
| // } |
| // |
| class GlobalSamplingCounter : public AbstractSamplingCounter { |
| public: |
| void name(const char* name) |
| { |
| // Global objects should be mapped in zero filled memory, so this should |
| // be a safe (albeit not necessarily threadsafe) check for 'first call'. |
| if (!m_next) |
| init(name); |
| } |
| }; |
| |
| // DeletableSamplingCounter: |
| // |
| // The above classes (SamplingCounter, GlobalSamplingCounter), are intended for |
| // use within a global or static scope, and as such cannot have a destructor. |
| // This means there is no convenient way for them to remove themselves from the |
| // static list of counters, and should an instance of either class be freed |
| // before 'dump()' has walked over the list it will potentially walk over an |
| // invalid pointer. |
| // |
| // This class is intended for use where the counter may possibly be deleted before |
| // the program exits. Should this occur, the counter will print it's value to |
| // stderr, and remove itself from the static list. Example: |
| // |
| // DeletableSamplingCounter* counter = new DeletableSamplingCounter("The Counter With No Name"); |
| // counter->count(); |
| // delete counter; |
| // |
| class DeletableSamplingCounter : public AbstractSamplingCounter { |
| public: |
| DeletableSamplingCounter(const char* name) { init(name); } |
| |
| ~DeletableSamplingCounter() |
| { |
| if (!s_completed) |
| fprintf(stderr, "DeletableSamplingCounter \"%s\" deleted early (with count %lld)\n", m_name, m_counter); |
| // Our m_referer pointer should know where the pointer to this node is, |
| // and m_next should know that this node is the previous node in the list. |
| ASSERT(*m_referer == this); |
| ASSERT(m_next->m_referer == &m_next); |
| // Remove this node from the list, and inform m_next that we have done so. |
| m_next->m_referer = m_referer; |
| *m_referer = m_next; |
| } |
| }; |
| #endif |
| |
| } // namespace JSC |
| |
| #endif // SamplingTool_h |