diff options
Diffstat (limited to 'Source/JavaScriptCore/bytecode/SamplingTool.h')
-rw-r--r-- | Source/JavaScriptCore/bytecode/SamplingTool.h | 414 |
1 files changed, 414 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/bytecode/SamplingTool.h b/Source/JavaScriptCore/bytecode/SamplingTool.h new file mode 100644 index 0000000..8e3ed9e --- /dev/null +++ b/Source/JavaScriptCore/bytecode/SamplingTool.h @@ -0,0 +1,414 @@ +/* + * 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 <wtf/Assertions.h> +#include <wtf/HashMap.h> +#include <wtf/Threading.h> + +#include "Nodes.h" +#include "Opcode.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(ScriptExecutable* executable) + : m_executable(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*); + + RefPtr<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 : public Noncopyable { + 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 : public Noncopyable { + 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 JIT; + friend class DeletableSamplingCounter; + public: + void count(uint32_t count = 1) + { + m_counter += count; + } + + static void dump(); + + 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 |