Commit c32f32e2 authored by barraclough@apple.com's avatar barraclough@apple.com

2009-05-12 Gavin Barraclough <barraclough@apple.com>

        Reviewed by Oliver Hunt.

        Add SamplingCounter tool to provide a simple mechanism for counting events in JSC
        (enabled using ENABLE(SAMPLING_COUNTERS)).  To count events within a single function
        use the class 'SamplingCounter', where the counter may be incremented from multiple
        functions 'GlobalSamplingCounter' may be convenient; all other counters (stack or
        heap allocated, rather than statically declared) should use the DeletableSamplingCounter.
        Further description of these classes is provided alongside their definition in 
        SamplingTool.h.

        Counters may be incremented from c++ by calling the 'count()' method on the counter,
        or may be incremented by JIT code by using the 'emitCount()' method within the JIT.

        This patch also fixes CODEBLOCK_SAMPLING, which was missing a null pointer check.

        * JavaScriptCore.exp:
        * assembler/MacroAssemblerX86.h:
        (JSC::MacroAssemblerX86::addWithCarry32):
        (JSC::MacroAssemblerX86::and32):
        (JSC::MacroAssemblerX86::or32):
        * assembler/MacroAssemblerX86Common.h:
        (JSC::MacroAssemblerX86Common::and32):
        (JSC::MacroAssemblerX86Common::or32):
        * assembler/MacroAssemblerX86_64.h:
        (JSC::MacroAssemblerX86_64::and32):
        (JSC::MacroAssemblerX86_64::or32):
        (JSC::MacroAssemblerX86_64::addPtr):
        * assembler/X86Assembler.h:
        (JSC::X86Assembler::):
        (JSC::X86Assembler::adcl_im):
        (JSC::X86Assembler::addq_im):
        (JSC::X86Assembler::andl_im):
        (JSC::X86Assembler::orl_im):
        * bytecode/SamplingTool.cpp:
        (JSC::AbstractSamplingCounter::dump):
        * bytecode/SamplingTool.h:
        (JSC::AbstractSamplingCounter::count):
        (JSC::GlobalSamplingCounter::name):
        (JSC::SamplingCounter::SamplingCounter):
        * jit/JIT.h:
        * jit/JITCall.cpp:
        (JSC::):
        * jit/JITInlineMethods.h:
        (JSC::JIT::setSamplingFlag):
        (JSC::JIT::clearSamplingFlag):
        (JSC::JIT::emitCount):
        * jsc.cpp:
        (runWithScripts):
        * parser/Nodes.cpp:
        (JSC::ScopeNode::ScopeNode):
        * wtf/Platform.h:



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@43619 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 326c5524
2009-05-12 Gavin Barraclough <barraclough@apple.com>
Reviewed by Oliver Hunt.
Add SamplingCounter tool to provide a simple mechanism for counting events in JSC
(enabled using ENABLE(SAMPLING_COUNTERS)). To count events within a single function
use the class 'SamplingCounter', where the counter may be incremented from multiple
functions 'GlobalSamplingCounter' may be convenient; all other counters (stack or
heap allocated, rather than statically declared) should use the DeletableSamplingCounter.
Further description of these classes is provided alongside their definition in
SamplingTool.h.
Counters may be incremented from c++ by calling the 'count()' method on the counter,
or may be incremented by JIT code by using the 'emitCount()' method within the JIT.
This patch also fixes CODEBLOCK_SAMPLING, which was missing a null pointer check.
* JavaScriptCore.exp:
* assembler/MacroAssemblerX86.h:
(JSC::MacroAssemblerX86::addWithCarry32):
(JSC::MacroAssemblerX86::and32):
(JSC::MacroAssemblerX86::or32):
* assembler/MacroAssemblerX86Common.h:
(JSC::MacroAssemblerX86Common::and32):
(JSC::MacroAssemblerX86Common::or32):
* assembler/MacroAssemblerX86_64.h:
(JSC::MacroAssemblerX86_64::and32):
(JSC::MacroAssemblerX86_64::or32):
(JSC::MacroAssemblerX86_64::addPtr):
* assembler/X86Assembler.h:
(JSC::X86Assembler::):
(JSC::X86Assembler::adcl_im):
(JSC::X86Assembler::addq_im):
(JSC::X86Assembler::andl_im):
(JSC::X86Assembler::orl_im):
* bytecode/SamplingTool.cpp:
(JSC::AbstractSamplingCounter::dump):
* bytecode/SamplingTool.h:
(JSC::AbstractSamplingCounter::count):
(JSC::GlobalSamplingCounter::name):
(JSC::SamplingCounter::SamplingCounter):
* jit/JIT.h:
* jit/JITCall.cpp:
(JSC::):
* jit/JITInlineMethods.h:
(JSC::JIT::setSamplingFlag):
(JSC::JIT::clearSamplingFlag):
(JSC::JIT::emitCount):
* jsc.cpp:
(runWithScripts):
* parser/Nodes.cpp:
(JSC::ScopeNode::ScopeNode):
* wtf/Platform.h:
2009-05-13 Steve Falkenburg <sfalken@apple.com>
Windows build fix.
......@@ -167,6 +167,7 @@ __ZN3JSC19constructEmptyArrayEPNS_9ExecStateE
__ZN3JSC19initializeThreadingEv
__ZN3JSC20MarkedArgumentBuffer10slowAppendENS_7JSValueE
__ZN3JSC20constructEmptyObjectEPNS_9ExecStateE
__ZN3JSC23AbstractSamplingCounter4dumpEv
__ZN3JSC23objectProtoFuncToStringEPNS_9ExecStateEPNS_8JSObjectENS_7JSValueERKNS_7ArgListE
__ZN3JSC23setUpStaticFunctionSlotEPNS_9ExecStateEPKNS_9HashEntryEPNS_8JSObjectERKNS_10IdentifierERNS_12PropertySlotE
__ZN3JSC25evaluateInGlobalCallFrameERKNS_7UStringERNS_7JSValueEPNS_14JSGlobalObjectE
......
......@@ -39,7 +39,9 @@ public:
static const Scale ScalePtr = TimesFour;
using MacroAssemblerX86Common::add32;
using MacroAssemblerX86Common::and32;
using MacroAssemblerX86Common::sub32;
using MacroAssemblerX86Common::or32;
using MacroAssemblerX86Common::load32;
using MacroAssemblerX86Common::store32;
using MacroAssemblerX86Common::branch32;
......@@ -55,6 +57,21 @@ public:
m_assembler.addl_im(imm.m_value, address.m_ptr);
}
void addWithCarry32(Imm32 imm, AbsoluteAddress address)
{
m_assembler.adcl_im(imm.m_value, address.m_ptr);
}
void and32(Imm32 imm, AbsoluteAddress address)
{
m_assembler.andl_im(imm.m_value, address.m_ptr);
}
void or32(Imm32 imm, AbsoluteAddress address)
{
m_assembler.orl_im(imm.m_value, address.m_ptr);
}
void sub32(Imm32 imm, AbsoluteAddress address)
{
m_assembler.subl_im(imm.m_value, address.m_ptr);
......
......@@ -92,6 +92,11 @@ public:
m_assembler.andl_ir(imm.m_value, dest);
}
void and32(Imm32 imm, Address address)
{
m_assembler.andl_im(imm.m_value, address.offset, address.base);
}
void lshift32(Imm32 imm, RegisterID dest)
{
m_assembler.shll_i8r(imm.m_value, dest);
......@@ -144,6 +149,11 @@ public:
m_assembler.orl_ir(imm.m_value, dest);
}
void or32(Imm32 imm, Address address)
{
m_assembler.orl_im(imm.m_value, address.offset, address.base);
}
void rshift32(RegisterID shift_amount, RegisterID dest)
{
// On x86 we can only shift by ecx; if asked to shift by another register we'll
......
......@@ -42,6 +42,8 @@ public:
static const Scale ScalePtr = TimesEight;
using MacroAssemblerX86Common::add32;
using MacroAssemblerX86Common::and32;
using MacroAssemblerX86Common::or32;
using MacroAssemblerX86Common::sub32;
using MacroAssemblerX86Common::load32;
using MacroAssemblerX86Common::store32;
......@@ -53,6 +55,18 @@ public:
add32(imm, Address(scratchRegister));
}
void and32(Imm32 imm, AbsoluteAddress address)
{
move(ImmPtr(address.m_ptr), scratchRegister);
and32(imm, Address(scratchRegister));
}
void or32(Imm32 imm, AbsoluteAddress address)
{
move(ImmPtr(address.m_ptr), scratchRegister);
or32(imm, Address(scratchRegister));
}
void sub32(Imm32 imm, AbsoluteAddress address)
{
move(ImmPtr(address.m_ptr), scratchRegister);
......@@ -125,6 +139,17 @@ public:
m_assembler.leaq_mr(imm.m_value, src, dest);
}
void addPtr(Imm32 imm, Address address)
{
m_assembler.addq_im(imm.m_value, address.offset, address.base);
}
void addPtr(Imm32 imm, AbsoluteAddress address)
{
move(ImmPtr(address.m_ptr), scratchRegister);
addPtr(imm, Address(scratchRegister));
}
void andPtr(RegisterID src, RegisterID dest)
{
m_assembler.andq_rr(src, dest);
......
......@@ -193,6 +193,7 @@ private:
typedef enum {
GROUP1_OP_ADD = 0,
GROUP1_OP_OR = 1,
GROUP1_OP_ADC = 2,
GROUP1_OP_AND = 4,
GROUP1_OP_SUB = 5,
GROUP1_OP_XOR = 6,
......@@ -295,6 +296,19 @@ public:
// Arithmetic operations:
#if !PLATFORM(X86_64)
void adcl_im(int imm, void* addr)
{
if (CAN_SIGN_EXTEND_8_32(imm)) {
m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_ADC, addr);
m_formatter.immediate8(imm);
} else {
m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_ADC, addr);
m_formatter.immediate32(imm);
}
}
#endif
void addl_rr(RegisterID src, RegisterID dst)
{
m_formatter.oneByteOp(OP_ADD_EvGv, src, dst);
......@@ -343,6 +357,17 @@ public:
m_formatter.immediate32(imm);
}
}
void addq_im(int imm, int offset, RegisterID base)
{
if (CAN_SIGN_EXTEND_8_32(imm)) {
m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_ADD, base, offset);
m_formatter.immediate8(imm);
} else {
m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_ADD, base, offset);
m_formatter.immediate32(imm);
}
}
#else
void addl_im(int imm, void* addr)
{
......@@ -372,6 +397,17 @@ public:
}
}
void andl_im(int imm, int offset, RegisterID base)
{
if (CAN_SIGN_EXTEND_8_32(imm)) {
m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_AND, base, offset);
m_formatter.immediate8(imm);
} else {
m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_AND, base, offset);
m_formatter.immediate32(imm);
}
}
#if PLATFORM(X86_64)
void andq_rr(RegisterID src, RegisterID dst)
{
......@@ -388,6 +424,17 @@ public:
m_formatter.immediate32(imm);
}
}
#else
void andl_im(int imm, void* addr)
{
if (CAN_SIGN_EXTEND_8_32(imm)) {
m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_AND, addr);
m_formatter.immediate8(imm);
} else {
m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_AND, addr);
m_formatter.immediate32(imm);
}
}
#endif
void notl_r(RegisterID dst)
......@@ -416,6 +463,17 @@ public:
}
}
void orl_im(int imm, int offset, RegisterID base)
{
if (CAN_SIGN_EXTEND_8_32(imm)) {
m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_OR, base, offset);
m_formatter.immediate8(imm);
} else {
m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_OR, base, offset);
m_formatter.immediate32(imm);
}
}
#if PLATFORM(X86_64)
void orq_rr(RegisterID src, RegisterID dst)
{
......@@ -432,6 +490,17 @@ public:
m_formatter.immediate32(imm);
}
}
#else
void orl_im(int imm, void* addr)
{
if (CAN_SIGN_EXTEND_8_32(imm)) {
m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_OR, addr);
m_formatter.immediate8(imm);
} else {
m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_OR, addr);
m_formatter.immediate32(imm);
}
}
#endif
void subl_rr(RegisterID src, RegisterID dst)
......
......@@ -390,4 +390,21 @@ void SamplingTool::dump(ExecState*)
#endif
void AbstractSamplingCounter::dump()
{
#if ENABLE(SAMPLING_COUNTERS)
if (s_abstractSamplingCounterChain != &s_abstractSamplingCounterChainEnd) {
printf("\nSampling Counter Values:\n");
for (AbstractSamplingCounter* currCounter = s_abstractSamplingCounterChain; (currCounter != &s_abstractSamplingCounterChainEnd); currCounter = currCounter->m_next)
printf("\t%s\t: %lld\n", currCounter->m_name, currCounter->m_counter);
printf("\n\n");
}
s_completed = true;
#endif
}
AbstractSamplingCounter AbstractSamplingCounter::s_abstractSamplingCounterChainEnd;
AbstractSamplingCounter* AbstractSamplingCounter::s_abstractSamplingCounterChain = &s_abstractSamplingCounterChainEnd;
bool AbstractSamplingCounter::s_completed = false;
} // namespace JSC
......@@ -272,6 +272,142 @@ namespace JSC {
#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
......@@ -614,8 +614,12 @@ namespace JSC {
#if ENABLE(SAMPLING_FLAGS)
void setSamplingFlag(int flag, RegisterID scratch);
void clearSamplingFlag(int flag, RegisterID scratch);
void setSamplingFlag(int32_t);
void clearSamplingFlag(int32_t);
#endif
#if ENABLE(SAMPLING_COUNTERS)
void emitCount(AbstractSamplingCounter&, uint32_t = 1);
#endif
#if ENABLE(OPCODE_SAMPLING)
......
......@@ -296,7 +296,6 @@ void JIT::compileOpCallSlowCase(Instruction* instruction, Vector<SlowCaseEntry>:
Jump storeResultForFirstRun = jump();
// FIXME: this label can be removed, since it is a fixed offset from 'callReturnLocation'.
// This is the address for the cold path *after* the first run (which tries to link the call).
m_callStructureStubCompilationInfo[callLinkInfoIndex].coldPathOther = MacroAssembler::Label(this);
......
......@@ -387,22 +387,33 @@ ALWAYS_INLINE void JIT::emitJumpSlowToHot(Jump jump, int relativeOffset)
}
#if ENABLE(SAMPLING_FLAGS)
ALWAYS_INLINE void JIT::setSamplingFlag(int flag, RegisterID scratch)
ALWAYS_INLINE void JIT::setSamplingFlag(int32_t flag)
{
ASSERT(flag >= 1);
ASSERT(flag <= 32);
load32(&SamplingFlags::s_flags, scratch);
or32(Imm32(1u << (flag - 1)), scratch);
store32(scratch, &SamplingFlags::s_flags);
or32(Imm32(1u << (flag - 1)), AbsoluteAddress(&SamplingFlags::s_flags));
}
ALWAYS_INLINE void JIT::clearSamplingFlag(int flag, RegisterID scratch)
ALWAYS_INLINE void JIT::clearSamplingFlag(int32_t flag)
{
ASSERT(flag >= 1);
ASSERT(flag <= 32);
load32(&SamplingFlags::s_flags, scratch);
and32(Imm32(~(1u << (flag - 1))), scratch);
store32(scratch, &SamplingFlags::s_flags);
and32(Imm32(~(1u << (flag - 1))), AbsoluteAddress(&SamplingFlags::s_flags));
}
#endif
#if ENABLE(SAMPLING_COUNTERS)
ALWAYS_INLINE void JIT::emitCount(AbstractSamplingCounter& counter, uint32_t count)
{
#if PLATFORM(X86_64) // Or any other 64-bit plattform.
addPtr(Imm32(count), AbsoluteAddress(&counter.m_counter));
#elif PLATFORM(X86) // Or any other little-endian 32-bit plattform.
intptr_t hiWord = reinterpret_cast<intptr_t>(&counter.m_counter) + sizeof(int32_t);
add32(Imm32(count), AbsoluteAddress(&counter.m_counter));
addWithCarry32(Imm32(0), AbsoluteAddress(reinterpret_cast<void*>(hiWord)));
#else
#error "SAMPLING_FLAGS not implemented on this platform."
#endif
}
#endif
......
......@@ -418,6 +418,9 @@ static bool runWithScripts(GlobalObject* globalObject, const Vector<Script>& scr
#if ENABLE(OPCODE_SAMPLING)
interpreter->sampler()->dump(globalObject->globalExec());
delete interpreter->sampler();
#endif
#if ENABLE(SAMPLING_COUNTERS)
AbstractSamplingCounter::dump();
#endif
return success;
}
......
......@@ -1832,7 +1832,8 @@ ScopeNode::ScopeNode(JSGlobalData* globalData)
, m_features(NoFeatures)
{
#if ENABLE(CODEBLOCK_SAMPLING)
globalData->interpreter->sampler()->notifyOfScope(this);
if (SamplingTool* sampler = globalData->interpreter->sampler())
sampler->notifyOfScope(this);
#endif
}
......@@ -1844,7 +1845,8 @@ ScopeNode::ScopeNode(JSGlobalData* globalData, const SourceCode& source, SourceE
, m_source(source)
{
#if ENABLE(CODEBLOCK_SAMPLING)
globalData->interpreter->sampler()->notifyOfScope(this);
if (SamplingTool* sampler = globalData->interpreter->sampler())
sampler->notifyOfScope(this);
#endif
}
......
......@@ -447,6 +447,7 @@
#define ENABLE_OPCODE_STATS 0
#endif
#define ENABLE_SAMPLING_COUNTERS 0
#define ENABLE_SAMPLING_FLAGS 0
#define ENABLE_OPCODE_SAMPLING 0
#define ENABLE_CODEBLOCK_SAMPLING 0
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment