diff options
| author | Feng Qian <fqian@google.com> | 2009-06-17 12:12:20 -0700 |
|---|---|---|
| committer | Feng Qian <fqian@google.com> | 2009-06-17 12:12:20 -0700 |
| commit | 5f1ab04193ad0130ca8204aadaceae083aca9881 (patch) | |
| tree | 5a92cd389e2cfe7fb67197ce14b38469462379f8 /JavaScriptCore/bytecode | |
| parent | 194315e5a908cc8ed67d597010544803eef1ac59 (diff) | |
| download | external_webkit-5f1ab04193ad0130ca8204aadaceae083aca9881.zip external_webkit-5f1ab04193ad0130ca8204aadaceae083aca9881.tar.gz external_webkit-5f1ab04193ad0130ca8204aadaceae083aca9881.tar.bz2 | |
Get WebKit r44544.
Diffstat (limited to 'JavaScriptCore/bytecode')
| -rw-r--r-- | JavaScriptCore/bytecode/CodeBlock.cpp | 72 | ||||
| -rw-r--r-- | JavaScriptCore/bytecode/CodeBlock.h | 73 | ||||
| -rw-r--r-- | JavaScriptCore/bytecode/EvalCodeCache.h | 2 | ||||
| -rw-r--r-- | JavaScriptCore/bytecode/Instruction.h | 2 | ||||
| -rw-r--r-- | JavaScriptCore/bytecode/Opcode.h | 8 | ||||
| -rw-r--r-- | JavaScriptCore/bytecode/SamplingTool.cpp | 216 | ||||
| -rw-r--r-- | JavaScriptCore/bytecode/SamplingTool.h | 222 |
7 files changed, 477 insertions, 118 deletions
diff --git a/JavaScriptCore/bytecode/CodeBlock.cpp b/JavaScriptCore/bytecode/CodeBlock.cpp index be060d0..d2b122a 100644 --- a/JavaScriptCore/bytecode/CodeBlock.cpp +++ b/JavaScriptCore/bytecode/CodeBlock.cpp @@ -55,7 +55,7 @@ static UString escapeQuotes(const UString& str) return result; } -static UString valueToSourceString(ExecState* exec, JSValuePtr val) +static UString valueToSourceString(ExecState* exec, JSValue val) { if (val.isString()) { UString result("\""); @@ -74,7 +74,7 @@ static CString registerName(int r) return (UString("r") + UString::from(r)).UTF8String(); } -static CString constantName(ExecState* exec, int k, JSValuePtr value) +static CString constantName(ExecState* exec, int k, JSValue value) { return (valueToSourceString(exec, value) + "(@k" + UString::from(k) + ")").UTF8String(); } @@ -357,7 +357,7 @@ void CodeBlock::dump(ExecState* exec) const unsigned registerIndex = m_numVars; size_t i = 0; do { - printf(" r%u = %s\n", registerIndex, valueToSourceString(exec, m_constantRegisters[i].jsValue(exec)).ascii()); + printf(" r%u = %s\n", registerIndex, valueToSourceString(exec, m_constantRegisters[i].jsValue()).ascii()); ++i; ++registerIndex; } while (i < m_constantRegisters.size()); @@ -497,6 +497,10 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& printf("[%4d] create_arguments\n", location); break; } + case op_init_arguments: { + printf("[%4d] init_arguments\n", location); + break; + } case op_convert_this: { int r0 = (++it)->u.operand; printf("[%4d] convert_this %s\n", location, registerName(r0).c_str()); @@ -703,7 +707,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& } case op_resolve_global: { int r0 = (++it)->u.operand; - JSValuePtr scope = JSValuePtr((++it)->u.jsCell); + JSValue scope = JSValue((++it)->u.jsCell); int id0 = (++it)->u.operand; printf("[%4d] resolve_global\t %s, %s, %s\n", location, registerName(r0).c_str(), valueToSourceString(exec, scope).ascii(), idName(id0, m_identifiers[id0]).c_str()); it += 2; @@ -725,13 +729,13 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& } case op_get_global_var: { int r0 = (++it)->u.operand; - JSValuePtr scope = JSValuePtr((++it)->u.jsCell); + JSValue scope = JSValue((++it)->u.jsCell); int index = (++it)->u.operand; printf("[%4d] get_global_var\t %s, %s, %d\n", location, registerName(r0).c_str(), valueToSourceString(exec, scope).ascii(), index); break; } case op_put_global_var: { - JSValuePtr scope = JSValuePtr((++it)->u.jsCell); + JSValue scope = JSValue((++it)->u.jsCell); int index = (++it)->u.operand; int r0 = (++it)->u.operand; printf("[%4d] put_global_var\t %s, %d, %s\n", location, valueToSourceString(exec, scope).ascii(), index, registerName(r0).c_str()); @@ -823,6 +827,10 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& printf("[%4d] put_setter\t %s, %s, %s\n", location, registerName(r0).c_str(), idName(id0, m_identifiers[id0]).c_str(), registerName(r1).c_str()); break; } + case op_method_check: { + printf("[%4d] op_method_check\n", location); + break; + } case op_del_by_id: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; @@ -888,6 +896,13 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& printConditionalJump(begin, it, location, "jneq_null"); break; } + case op_jneq_ptr: { + int r0 = (++it)->u.operand; + int r1 = (++it)->u.operand; + int offset = (++it)->u.operand; + printf("[%4d] jneq_ptr\t\t %s, %s, %d(->%d)\n", location, registerName(r0).c_str(), registerName(r1).c_str(), offset, locationForOffset(begin, it, offset)); + break; + } case op_jnless: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; @@ -895,6 +910,13 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& printf("[%4d] jnless\t\t %s, %s, %d(->%d)\n", location, registerName(r0).c_str(), registerName(r1).c_str(), offset, locationForOffset(begin, it, offset)); break; } + case op_jnlesseq: { + int r0 = (++it)->u.operand; + int r1 = (++it)->u.operand; + int offset = (++it)->u.operand; + printf("[%4d] jnlesseq\t\t %s, %s, %d(->%d)\n", location, registerName(r0).c_str(), registerName(r1).c_str(), offset, locationForOffset(begin, it, offset)); + break; + } case op_loop_if_less: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; @@ -958,6 +980,18 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& printf("[%4d] call_eval\t %s, %s, %d, %d\n", location, registerName(dst).c_str(), registerName(func).c_str(), argCount, registerOffset); break; } + case op_call_varargs: { + int dst = (++it)->u.operand; + int func = (++it)->u.operand; + int argCount = (++it)->u.operand; + int registerOffset = (++it)->u.operand; + printf("[%4d] call_varargs\t %s, %s, %s, %d\n", location, registerName(dst).c_str(), registerName(func).c_str(), registerName(argCount).c_str(), registerOffset); + break; + } + case op_load_varargs: { + printUnaryOp(location, it, "load_varargs"); + break; + } case op_tear_off_activation: { int r0 = (++it)->u.operand; printf("[%4d] tear_off_activation\t %s\n", location, registerName(r0).c_str()); @@ -988,6 +1022,19 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& printf("[%4d] construct_verify\t %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str()); break; } + case op_strcat: { + int r0 = (++it)->u.operand; + int r1 = (++it)->u.operand; + int count = (++it)->u.operand; + printf("[%4d] op_strcat\t %s, %s, %d\n", location, registerName(r0).c_str(), registerName(r1).c_str(), count); + break; + } + case op_to_primitive: { + int r0 = (++it)->u.operand; + int r1 = (++it)->u.operand; + printf("[%4d] op_to_primitive\t %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str()); + break; + } case op_get_pnames: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; @@ -1267,6 +1314,11 @@ CodeBlock::~CodeBlock() callLinkInfo->callee->removeCaller(callLinkInfo); } + for (size_t size = m_methodCallLinkInfos.size(), i = 0; i < size; ++i) { + if (Structure* structure = m_methodCallLinkInfos[i].cachedStructure) + structure->deref(); + } + unlinkCallers(); #endif @@ -1420,7 +1472,7 @@ void CodeBlock::reparseForExceptionInfoIfNecessary(CallFrame* callFrame) #if ENABLE(JIT) JIT::compile(m_globalData, &newCodeBlock); - ASSERT(newCodeBlock.m_jitCode.codeSize == m_jitCode.codeSize); + ASSERT(newFunctionBody->generatedJITCode().size() == ownerNode()->generatedJITCode().size()); #endif m_exceptionInfo.set(newCodeBlock.m_exceptionInfo.release()); @@ -1441,7 +1493,7 @@ void CodeBlock::reparseForExceptionInfoIfNecessary(CallFrame* callFrame) #if ENABLE(JIT) JIT::compile(m_globalData, &newCodeBlock); - ASSERT(newCodeBlock.m_jitCode.codeSize == m_jitCode.codeSize); + ASSERT(newEvalBody->generatedJITCode().size() == ownerNode()->generatedJITCode().size()); #endif m_exceptionInfo.set(newCodeBlock.m_exceptionInfo.release()); @@ -1635,9 +1687,9 @@ bool CodeBlock::hasGlobalResolveInfoAtBytecodeOffset(unsigned bytecodeOffset) #endif #if ENABLE(JIT) -void CodeBlock::setJITCode(JITCodeRef& jitCode) +void CodeBlock::setJITCode(JITCode jitCode) { - m_jitCode = jitCode; + ownerNode()->setJITCode(jitCode); #if !ENABLE(OPCODE_SAMPLING) if (!BytecodeGenerator::dumpsGeneratedCode()) m_instructions.clear(); diff --git a/JavaScriptCore/bytecode/CodeBlock.h b/JavaScriptCore/bytecode/CodeBlock.h index 9bd4090..ac29c6c 100644 --- a/JavaScriptCore/bytecode/CodeBlock.h +++ b/JavaScriptCore/bytecode/CodeBlock.h @@ -63,34 +63,6 @@ namespace JSC { #endif }; -#if ENABLE(JIT) - // The code, and the associated pool from which it was allocated. - struct JITCodeRef { - JITCode code; -#ifndef NDEBUG - unsigned codeSize; -#endif - RefPtr<ExecutablePool> executablePool; - - JITCodeRef() - : code(0) -#ifndef NDEBUG - , codeSize(0) -#endif - { - } - - JITCodeRef(void* code, PassRefPtr<ExecutablePool> executablePool) - : code(code) -#ifndef NDEBUG - , codeSize(0) -#endif - , executablePool(executablePool) - { - } - }; -#endif - struct ExpressionRangeInfo { enum { MaxOffset = (1 << 7) - 1, @@ -134,6 +106,17 @@ namespace JSC { bool isLinked() { return callee; } }; + struct MethodCallLinkInfo { + MethodCallLinkInfo() + : cachedStructure(0) + { + } + + MacroAssembler::CodeLocationCall callReturnLocation; + MacroAssembler::CodeLocationDataLabelPtr structureLabel; + Structure* cachedStructure; + }; + struct FunctionRegisterInfo { FunctionRegisterInfo(unsigned bytecodeOffset, int functionRegisterIndex) : bytecodeOffset(bytecodeOffset) @@ -185,6 +168,11 @@ namespace JSC { return callLinkInfo->callReturnLocation.calleeReturnAddressValue(); } + inline void* getMethodCallLinkInfoReturnLocation(MethodCallLinkInfo* methodCallLinkInfo) + { + return methodCallLinkInfo->callReturnLocation.calleeReturnAddressValue(); + } + inline unsigned getCallReturnOffset(CallReturnOffsetToBytecodeIndex* pc) { return pc->callReturnOffset; @@ -264,9 +252,9 @@ namespace JSC { return index >= m_numVars && index < m_numVars + m_numConstants; } - ALWAYS_INLINE JSValuePtr getConstant(int index) + ALWAYS_INLINE JSValue getConstant(int index) { - return m_constantRegisters[index - m_numVars].getJSValue(); + return m_constantRegisters[index - m_numVars].jsValue(); } ALWAYS_INLINE bool isTemporaryRegisterIndex(int index) @@ -309,10 +297,15 @@ namespace JSC { return *(binaryChop<CallLinkInfo, void*, getCallLinkInfoReturnLocation>(m_callLinkInfos.begin(), m_callLinkInfos.size(), returnAddress)); } + MethodCallLinkInfo& getMethodCallLinkInfo(void* returnAddress) + { + return *(binaryChop<MethodCallLinkInfo, void*, getMethodCallLinkInfoReturnLocation>(m_methodCallLinkInfos.begin(), m_methodCallLinkInfos.size(), returnAddress)); + } + unsigned getBytecodeIndex(CallFrame* callFrame, void* nativePC) { reparseForExceptionInfoIfNecessary(callFrame); - return binaryChop<CallReturnOffsetToBytecodeIndex, unsigned, getCallReturnOffset>(m_exceptionInfo->m_callReturnIndexVector.begin(), m_exceptionInfo->m_callReturnIndexVector.size(), m_jitCode.code.offsetOf(nativePC))->bytecodeIndex; + return binaryChop<CallReturnOffsetToBytecodeIndex, unsigned, getCallReturnOffset>(m_exceptionInfo->m_callReturnIndexVector.begin(), m_exceptionInfo->m_callReturnIndexVector.size(), ownerNode()->generatedJITCode().offsetOf(nativePC))->bytecodeIndex; } bool functionRegisterForBytecodeOffset(unsigned bytecodeOffset, int& functionRegisterIndex); @@ -327,9 +320,8 @@ namespace JSC { #endif #if ENABLE(JIT) - void setJITCode(JITCodeRef& jitCode); - JITCode jitCode() { return m_jitCode.code; } - ExecutablePool* executablePool() { return m_jitCode.executablePool.get(); } + void setJITCode(JITCode); + ExecutablePool* executablePool() { return ownerNode()->getExecutablePool(); } #endif ScopeNode* ownerNode() const { return m_ownerNode; } @@ -373,6 +365,9 @@ namespace JSC { void addCallLinkInfo() { m_callLinkInfos.append(CallLinkInfo()); } CallLinkInfo& callLinkInfo(int index) { return m_callLinkInfos[index]; } + void addMethodCallLinkInfos(unsigned n) { m_methodCallLinkInfos.grow(n); } + MethodCallLinkInfo& methodCallLinkInfo(int index) { return m_methodCallLinkInfos[index]; } + void addFunctionRegisterInfo(unsigned bytecodeOffset, int functionIndex) { createRareDataIfNecessary(); m_rareData->m_functionRegisterInfos.append(FunctionRegisterInfo(bytecodeOffset, functionIndex)); } #endif @@ -414,8 +409,8 @@ namespace JSC { bool hasFunctions() const { return m_functionExpressions.size() || (m_rareData && m_rareData->m_functions.size()); } - unsigned addUnexpectedConstant(JSValuePtr v) { createRareDataIfNecessary(); unsigned size = m_rareData->m_unexpectedConstants.size(); m_rareData->m_unexpectedConstants.append(v); return size; } - JSValuePtr unexpectedConstant(int index) const { ASSERT(m_rareData); return m_rareData->m_unexpectedConstants[index]; } + unsigned addUnexpectedConstant(JSValue v) { createRareDataIfNecessary(); unsigned size = m_rareData->m_unexpectedConstants.size(); m_rareData->m_unexpectedConstants.append(v); return size; } + JSValue unexpectedConstant(int index) const { ASSERT(m_rareData); return m_rareData->m_unexpectedConstants[index]; } unsigned addRegExp(RegExp* r) { createRareDataIfNecessary(); unsigned size = m_rareData->m_regexps.size(); m_rareData->m_regexps.append(r); return size; } RegExp* regexp(int index) const { ASSERT(m_rareData); return m_rareData->m_regexps[index].get(); } @@ -473,9 +468,6 @@ namespace JSC { #ifndef NDEBUG unsigned m_instructionCount; #endif -#if ENABLE(JIT) - JITCodeRef m_jitCode; -#endif int m_thisRegister; @@ -496,6 +488,7 @@ namespace JSC { Vector<StructureStubInfo> m_structureStubInfos; Vector<GlobalResolveInfo> m_globalResolveInfos; Vector<CallLinkInfo> m_callLinkInfos; + Vector<MethodCallLinkInfo> m_methodCallLinkInfos; Vector<CallLinkInfo*> m_linkedCallerList; #endif @@ -524,7 +517,7 @@ namespace JSC { // Rare Constants Vector<RefPtr<FuncDeclNode> > m_functions; - Vector<JSValuePtr> m_unexpectedConstants; + Vector<JSValue> m_unexpectedConstants; Vector<RefPtr<RegExp> > m_regexps; // Jump Tables diff --git a/JavaScriptCore/bytecode/EvalCodeCache.h b/JavaScriptCore/bytecode/EvalCodeCache.h index 2d6f7dc..f0ce73e 100644 --- a/JavaScriptCore/bytecode/EvalCodeCache.h +++ b/JavaScriptCore/bytecode/EvalCodeCache.h @@ -41,7 +41,7 @@ namespace JSC { class EvalCodeCache { public: - PassRefPtr<EvalNode> get(ExecState* exec, const UString& evalSource, ScopeChainNode* scopeChain, JSValuePtr& exceptionValue) + PassRefPtr<EvalNode> get(ExecState* exec, const UString& evalSource, ScopeChainNode* scopeChain, JSValue& exceptionValue) { RefPtr<EvalNode> evalNode; diff --git a/JavaScriptCore/bytecode/Instruction.h b/JavaScriptCore/bytecode/Instruction.h index 314fda4..24ba490 100644 --- a/JavaScriptCore/bytecode/Instruction.h +++ b/JavaScriptCore/bytecode/Instruction.h @@ -34,7 +34,7 @@ #include "Structure.h" #include <wtf/VectorTraits.h> -#define POLYMORPHIC_LIST_CACHE_SIZE 4 +#define POLYMORPHIC_LIST_CACHE_SIZE 8 namespace JSC { diff --git a/JavaScriptCore/bytecode/Opcode.h b/JavaScriptCore/bytecode/Opcode.h index d00178b..f4421df 100644 --- a/JavaScriptCore/bytecode/Opcode.h +++ b/JavaScriptCore/bytecode/Opcode.h @@ -40,6 +40,7 @@ namespace JSC { #define FOR_EACH_OPCODE_ID(macro) \ macro(op_enter, 1) \ macro(op_enter_with_activation, 2) \ + macro(op_init_arguments, 1) \ macro(op_create_arguments, 1) \ macro(op_convert_this, 2) \ \ @@ -125,7 +126,9 @@ namespace JSC { macro(op_jfalse, 3) \ macro(op_jeq_null, 3) \ macro(op_jneq_null, 3) \ + macro(op_jneq_ptr, 4) \ macro(op_jnless, 4) \ + macro(op_jnlesseq, 4) \ macro(op_jmp_scopes, 3) \ macro(op_loop, 2) \ macro(op_loop_if_true, 3) \ @@ -139,12 +142,17 @@ namespace JSC { macro(op_new_func_exp, 3) \ macro(op_call, 5) \ macro(op_call_eval, 5) \ + macro(op_call_varargs, 5) \ + macro(op_load_varargs, 3) \ macro(op_tear_off_activation, 2) \ macro(op_tear_off_arguments, 1) \ macro(op_ret, 2) \ + macro(op_method_check, 1) \ \ macro(op_construct, 7) \ macro(op_construct_verify, 3) \ + macro(op_strcat, 4) \ + macro(op_to_primitive, 3) \ \ macro(op_get_pnames, 3) \ macro(op_next_pname, 4) \ diff --git a/JavaScriptCore/bytecode/SamplingTool.cpp b/JavaScriptCore/bytecode/SamplingTool.cpp index 215ebe5..8651723 100644 --- a/JavaScriptCore/bytecode/SamplingTool.cpp +++ b/JavaScriptCore/bytecode/SamplingTool.cpp @@ -39,24 +39,57 @@ namespace JSC { -void ScopeSampleRecord::sample(CodeBlock* codeBlock, Instruction* vPC) +#if ENABLE(SAMPLING_FLAGS) + +void SamplingFlags::sample() { - if (!m_samples) { - m_size = codeBlock->instructions().size(); - m_samples = static_cast<int*>(calloc(m_size, sizeof(int))); - m_codeBlock = codeBlock; + uint32_t mask = 1 << 31; + unsigned index; + + for (index = 0; index < 32; ++index) { + if (mask & s_flags) + break; + mask >>= 1; } - ++m_sampleCount; + s_flagCounts[32 - index]++; +} - unsigned offest = vPC - codeBlock->instructions().begin(); - // Since we don't read and write codeBlock and vPC atomically, this check - // can fail if we sample mid op_call / op_ret. - if (offest < m_size) { - m_samples[offest]++; - m_opcodeSampleCount++; - } +void SamplingFlags::start() +{ + for (unsigned i = 0; i <= 32; ++i) + s_flagCounts[i] = 0; } +void SamplingFlags::stop() +{ + uint64_t total = 0; + for (unsigned i = 0; i <= 32; ++i) + total += s_flagCounts[i]; + + if (total) { + printf("\nSamplingFlags: sample counts with flags set: (%lld total)\n", total); + for (unsigned i = 0; i <= 32; ++i) { + if (s_flagCounts[i]) + printf(" [ %02d ] : %lld\t\t(%03.2f%%)\n", i, s_flagCounts[i], (100.0 * s_flagCounts[i]) / total); + } + printf("\n"); + } else + printf("\nSamplingFlags: no samples.\n\n"); +} +uint64_t SamplingFlags::s_flagCounts[33]; + +#else +void SamplingFlags::start() {} +void SamplingFlags::stop() {} +#endif + +/* + Start with flag 16 set. + By doing this the monitoring of lower valued flags will be masked out + until flag 16 is explictly cleared. +*/ +uint32_t SamplingFlags::s_flags = 1 << 15; + #if PLATFORM(WIN_OS) @@ -82,62 +115,113 @@ static inline unsigned hertz2us(unsigned hertz) return 1000000 / hertz; } -void SamplingTool::run() + +SamplingTool* SamplingTool::s_samplingTool = 0; + + +bool SamplingThread::s_running = false; +unsigned SamplingThread::s_hertz = 10000; +ThreadIdentifier SamplingThread::s_samplingThread; + +void* SamplingThread::threadStartFunc(void*) { - while (m_running) { - sleepForMicroseconds(hertz2us(m_hertz)); + while (s_running) { + sleepForMicroseconds(hertz2us(s_hertz)); - Sample sample(m_sample, m_codeBlock); - ++m_sampleCount; +#if ENABLE(SAMPLING_FLAGS) + SamplingFlags::sample(); +#endif +#if ENABLE(OPCODE_SAMPLING) + SamplingTool::sample(); +#endif + } - if (sample.isNull()) - continue; + return 0; +} - if (!sample.inHostFunction()) { - unsigned opcodeID = m_interpreter->getOpcodeID(sample.vPC()[0].u.opcode); - ++m_opcodeSampleCount; - ++m_opcodeSamples[opcodeID]; +void SamplingThread::start(unsigned hertz) +{ + ASSERT(!s_running); + s_running = true; + s_hertz = hertz; - if (sample.inCTIFunction()) - m_opcodeSamplesInCTIFunctions[opcodeID]++; - } + s_samplingThread = createThread(threadStartFunc, 0, "JavaScriptCore::Sampler"); +} + +void SamplingThread::stop() +{ + ASSERT(s_running); + s_running = false; + waitForThreadCompletion(s_samplingThread, 0); +} + + +void ScopeSampleRecord::sample(CodeBlock* codeBlock, Instruction* vPC) +{ + if (!m_samples) { + m_size = codeBlock->instructions().size(); + m_samples = static_cast<int*>(calloc(m_size, sizeof(int))); + m_codeBlock = codeBlock; + } + + ++m_sampleCount; + + unsigned offest = vPC - codeBlock->instructions().begin(); + // Since we don't read and write codeBlock and vPC atomically, this check + // can fail if we sample mid op_call / op_ret. + if (offest < m_size) { + m_samples[offest]++; + m_opcodeSampleCount++; + } +} + +void SamplingTool::doRun() +{ + Sample sample(m_sample, m_codeBlock); + ++m_sampleCount; + + if (sample.isNull()) + return; + + if (!sample.inHostFunction()) { + unsigned opcodeID = m_interpreter->getOpcodeID(sample.vPC()[0].u.opcode); + + ++m_opcodeSampleCount; + ++m_opcodeSamples[opcodeID]; + + if (sample.inCTIFunction()) + m_opcodeSamplesInCTIFunctions[opcodeID]++; + } #if ENABLE(CODEBLOCK_SAMPLING) + if (CodeBlock* codeBlock = sample.codeBlock()) { MutexLocker locker(m_scopeSampleMapMutex); - ScopeSampleRecord* record = m_scopeSampleMap->get(sample.codeBlock()->ownerNode()); + ScopeSampleRecord* record = m_scopeSampleMap->get(codeBlock->ownerNode()); ASSERT(record); - record->sample(sample.codeBlock(), sample.vPC()); -#endif + record->sample(codeBlock, sample.vPC()); } +#endif } -void* SamplingTool::threadStartFunc(void* samplingTool) +void SamplingTool::sample() { - reinterpret_cast<SamplingTool*>(samplingTool)->run(); - return 0; + s_samplingTool->doRun(); } void SamplingTool::notifyOfScope(ScopeNode* scope) { +#if ENABLE(CODEBLOCK_SAMPLING) MutexLocker locker(m_scopeSampleMapMutex); m_scopeSampleMap->set(scope, new ScopeSampleRecord(scope)); +#else + UNUSED_PARAM(scope); +#endif } -void SamplingTool::start(unsigned hertz) -{ - ASSERT(!m_running); - m_running = true; - m_hertz = hertz; - - m_samplingThread = createThread(threadStartFunc, this, "JavaScriptCore::Sampler"); -} - -void SamplingTool::stop() +void SamplingTool::setup() { - ASSERT(m_running); - m_running = false; - waitForThreadCompletion(m_samplingThread, 0); + s_samplingTool = this; } #if ENABLE(OPCODE_SAMPLING) @@ -153,14 +237,6 @@ struct LineCountInfo { unsigned count; }; -static int compareLineCountInfoSampling(const void* left, const void* right) -{ - const LineCountInfo* leftLineCount = reinterpret_cast<const LineCountInfo*>(left); - const LineCountInfo* rightLineCount = reinterpret_cast<const LineCountInfo*>(right); - - return (leftLineCount->line > rightLineCount->line) ? 1 : (leftLineCount->line < rightLineCount->line) ? -1 : 0; -} - static int compareOpcodeIndicesSampling(const void* left, const void* right) { const OpcodeSampleInfo* leftSampleInfo = reinterpret_cast<const OpcodeSampleInfo*>(left); @@ -169,6 +245,15 @@ static int compareOpcodeIndicesSampling(const void* left, const void* right) return (leftSampleInfo->count < rightSampleInfo->count) ? 1 : (leftSampleInfo->count > rightSampleInfo->count) ? -1 : 0; } +#if ENABLE(CODEBLOCK_SAMPLING) +static int compareLineCountInfoSampling(const void* left, const void* right) +{ + const LineCountInfo* leftLineCount = reinterpret_cast<const LineCountInfo*>(left); + const LineCountInfo* rightLineCount = reinterpret_cast<const LineCountInfo*>(right); + + return (leftLineCount->line > rightLineCount->line) ? 1 : (leftLineCount->line < rightLineCount->line) ? -1 : 0; +} + static int compareScopeSampleRecords(const void* left, const void* right) { const ScopeSampleRecord* const leftValue = *static_cast<const ScopeSampleRecord* const *>(left); @@ -176,6 +261,7 @@ static int compareScopeSampleRecords(const void* left, const void* right) return (leftValue->m_sampleCount < rightValue->m_sampleCount) ? 1 : (leftValue->m_sampleCount > rightValue->m_sampleCount) ? -1 : 0; } +#endif void SamplingTool::dump(ExecState* exec) { @@ -227,6 +313,8 @@ void SamplingTool::dump(ExecState* exec) printf("\tcti count:\tsamples inside a CTI function called by this opcode\n"); printf("\tcti %% of self:\tcti count / sample count\n"); +#if ENABLE(CODEBLOCK_SAMPLING) + // (3) Build and sort 'codeBlockSamples' array. int scopeCount = m_scopeSampleMap->size(); @@ -285,6 +373,9 @@ void SamplingTool::dump(ExecState* exec) } } } +#else + UNUSED_PARAM(exec); +#endif } #else @@ -295,4 +386,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 diff --git a/JavaScriptCore/bytecode/SamplingTool.h b/JavaScriptCore/bytecode/SamplingTool.h index d1cf2e88..7d7dc9c 100644 --- a/JavaScriptCore/bytecode/SamplingTool.h +++ b/JavaScriptCore/bytecode/SamplingTool.h @@ -38,6 +38,54 @@ namespace JSC { + 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; @@ -73,6 +121,19 @@ namespace JSC { typedef WTF::HashMap<ScopeNode*, ScopeSampleRecord*> ScopeSampleRecordMap; + 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 class CallRecord; @@ -127,12 +188,13 @@ namespace JSC { SamplingTool(Interpreter* interpreter) : m_interpreter(interpreter) - , m_running(false) , m_codeBlock(0) , m_sample(0) , m_sampleCount(0) , m_opcodeSampleCount(0) +#if ENABLE(CODEBLOCK_SAMPLING) , m_scopeSampleMap(new ScopeSampleRecordMap()) +#endif { memset(m_opcodeSamples, 0, sizeof(m_opcodeSamples)); memset(m_opcodeSamplesInCTIFunctions, 0, sizeof(m_opcodeSamplesInCTIFunctions)); @@ -140,11 +202,12 @@ namespace JSC { ~SamplingTool() { +#if ENABLE(CODEBLOCK_SAMPLING) deleteAllValues(*m_scopeSampleMap); +#endif } - void start(unsigned hertz=10000); - void stop(); + void setup(); void dump(ExecState*); void notifyOfScope(ScopeNode* scope); @@ -165,6 +228,8 @@ namespace JSC { 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: @@ -174,7 +239,7 @@ namespace JSC { { } - bool isNull() { return !m_sample || !m_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; } @@ -184,17 +249,12 @@ namespace JSC { intptr_t m_sample; CodeBlock* m_codeBlock; }; - - static void* threadStartFunc(void*); - void run(); + + void doRun(); + static SamplingTool* s_samplingTool; Interpreter* m_interpreter; - // Sampling thread state. - bool m_running; - unsigned m_hertz; - ThreadIdentifier m_samplingThread; - // State tracked by the main thread, used by the sampling thread. CodeBlock* m_codeBlock; intptr_t m_sample; @@ -205,9 +265,147 @@ namespace JSC { unsigned m_opcodeSamples[numOpcodeIDs]; unsigned m_opcodeSamplesInCTIFunctions[numOpcodeIDs]; +#if ENABLE(CODEBLOCK_SAMPLING) Mutex m_scopeSampleMapMutex; OwnPtr<ScopeSampleRecordMap> 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 |
