/* | |
* 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*); | |
#if COMPILER(WINSCW) || COMPILER(ACC) | |
ScriptExecutable* m_executable; | |
#else | |
RefPtr<ScriptExecutable> m_executable; | |
#endif | |
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 |