/*
- * Copyright (C) 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2013 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 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
+ * 3. Neither the name of Apple 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.
*
#ifndef SamplingTool_h
#define SamplingTool_h
+#include "Strong.h"
+#include "Opcode.h"
+#include "SamplingCounter.h"
#include <wtf/Assertions.h>
+#include <wtf/Atomics.h>
#include <wtf/HashMap.h>
+#include <wtf/MainThread.h>
+#include <wtf/Spectrum.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();
+ JS_EXPORT_PRIVATE static void start();
+ JS_EXPORT_PRIVATE static void stop();
#if ENABLE(SAMPLING_FLAGS)
static void setFlag(unsigned flag)
int m_flag;
};
+ static const void* addressOfFlags()
+ {
+ return &s_flags;
+ }
+
#endif
private:
- static uint32_t s_flags;
+ JS_EXPORTDATA static uint32_t s_flags;
#if ENABLE(SAMPLING_FLAGS)
static uint64_t s_flagCounts[33];
#endif
};
+#if ENABLE(SAMPLING_REGIONS)
+ class SamplingRegion {
+ public:
+ // Create a scoped sampling region using a C string constant name that describes
+ // what you are doing. This must be a string constant that persists for the
+ // lifetime of the process and is immutable.
+ SamplingRegion(const char* name)
+ {
+ if (!isMainThread()) {
+ m_name = 0;
+ return;
+ }
+
+ m_name = name;
+ exchangeCurrent(this, &m_previous);
+ ASSERT(!m_previous || m_previous > this);
+ }
+
+ ~SamplingRegion()
+ {
+ if (!m_name)
+ return;
+
+ ASSERT(bitwise_cast<SamplingRegion*>(s_currentOrReserved & ~1) == this);
+ exchangeCurrent(m_previous);
+ }
+
+ static void sample();
+
+ JS_EXPORT_PRIVATE static void dump();
+
+ private:
+ const char* m_name;
+ SamplingRegion* m_previous;
+
+ static void exchangeCurrent(SamplingRegion* current, SamplingRegion** previousPtr = 0)
+ {
+ uintptr_t previous;
+ while (true) {
+ previous = s_currentOrReserved;
+
+ // If it's reserved (i.e. sampling thread is reading it), loop around.
+ if (previous & 1) {
+#if OS(UNIX)
+ sched_yield();
+#endif
+ continue;
+ }
+
+ // If we're going to CAS, then make sure previous is set.
+ if (previousPtr)
+ *previousPtr = bitwise_cast<SamplingRegion*>(previous);
+
+ if (WTF::weakCompareAndSwapUIntPtr(&s_currentOrReserved, previous, bitwise_cast<uintptr_t>(current)))
+ break;
+ }
+ }
+
+ static void dumpInternal();
+
+ class Locker {
+ public:
+ Locker();
+ ~Locker();
+ };
+
+ static volatile uintptr_t s_currentOrReserved;
+
+ // rely on identity hashing of string constants
+ static Spectrum<const char*>* s_spectrum;
+
+ static unsigned long s_noneOfTheAbove;
+
+ static unsigned s_numberOfSamplesSinceDump;
+ };
+#else // ENABLE(SAMPLING_REGIONS)
+ class SamplingRegion {
+ public:
+ SamplingRegion(const char*) { }
+ JS_EXPORT_PRIVATE void dump();
+ };
+#endif // ENABLE(SAMPLING_REGIONS)
+
class CodeBlock;
class ExecState;
class Interpreter;
class ScopeNode;
struct Instruction;
- struct ScopeSampleRecord {
- ScopeSampleRecord(ScopeNode* scope)
- : m_scope(scope)
+ struct ScriptSampleRecord {
+ ScriptSampleRecord(VM& vm, ScriptExecutable* executable)
+ : m_executable(vm, executable)
, m_codeBlock(0)
, m_sampleCount(0)
, m_opcodeSampleCount(0)
{
}
- ~ScopeSampleRecord()
+ ~ScriptSampleRecord()
{
if (m_samples)
free(m_samples);
void sample(CodeBlock*, Instruction*);
- RefPtr<ScopeNode> m_scope;
+ Strong<ScriptExecutable> m_executable;
CodeBlock* m_codeBlock;
int m_sampleCount;
int m_opcodeSampleCount;
unsigned m_size;
};
- typedef WTF::HashMap<ScopeNode*, ScopeSampleRecord*> ScopeSampleRecordMap;
+ typedef HashMap<ScriptExecutable*, std::unique_ptr<ScriptSampleRecord>> ScriptSampleRecordMap;
class SamplingThread {
public:
static unsigned s_hertz;
static ThreadIdentifier s_samplingThread;
- static void start(unsigned hertz=10000);
- static void stop();
+ JS_EXPORT_PRIVATE static void start(unsigned hertz=10000);
+ JS_EXPORT_PRIVATE static void stop();
- static void* threadStartFunc(void*);
+ static void threadStartFunc(void*);
};
class SamplingTool {
public:
- friend class CallRecord;
- friend class HostCallRecord;
+ friend struct CallRecord;
#if ENABLE(OPCODE_SAMPLING)
- class CallRecord : Noncopyable {
+ class CallRecord {
+ WTF_MAKE_NONCOPYABLE(CallRecord);
public:
- CallRecord(SamplingTool* samplingTool)
+ CallRecord(SamplingTool* samplingTool, bool isHostCall = false)
: m_samplingTool(samplingTool)
, m_savedSample(samplingTool->m_sample)
, m_savedCodeBlock(samplingTool->m_codeBlock)
{
+ if (isHostcall)
+ samplingTool->m_sample |= 0x1;
}
~CallRecord()
intptr_t m_savedSample;
CodeBlock* m_savedCodeBlock;
};
-
- class HostCallRecord : public CallRecord {
- public:
- HostCallRecord(SamplingTool* samplingTool)
- : CallRecord(samplingTool)
- {
- samplingTool->m_sample |= 0x1;
- }
- };
#else
- class CallRecord : Noncopyable {
+ class CallRecord {
+ WTF_MAKE_NONCOPYABLE(CallRecord);
public:
- CallRecord(SamplingTool*)
+ CallRecord(SamplingTool*, bool = false)
{
}
};
-
- class HostCallRecord : public CallRecord {
- public:
- HostCallRecord(SamplingTool* samplingTool)
- : CallRecord(samplingTool)
- {
- }
- };
-#endif
+#endif
SamplingTool(Interpreter* interpreter)
: m_interpreter(interpreter)
, m_sampleCount(0)
, m_opcodeSampleCount(0)
#if ENABLE(CODEBLOCK_SAMPLING)
- , m_scopeSampleMap(new ScopeSampleRecordMap())
+ , m_scopeSampleMap(adoptPtr(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();
+ JS_EXPORT_PRIVATE void setup();
void dump(ExecState*);
- void notifyOfScope(ScopeNode* scope);
+ void notifyOfScope(VM&, ScriptExecutable* scope);
void sample(CodeBlock* codeBlock, Instruction* vPC)
{
unsigned m_opcodeSamplesInCTIFunctions[numOpcodeIDs];
#if ENABLE(CODEBLOCK_SAMPLING)
- Mutex m_scopeSampleMapMutex;
- OwnPtr<ScopeSampleRecordMap> m_scopeSampleMap;
+ 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