dumpCallFrame is broken in ToT
authoroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 18 Jul 2012 23:26:06 +0000 (23:26 +0000)
committeroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 18 Jul 2012 23:26:06 +0000 (23:26 +0000)
https://bugs.webkit.org/show_bug.cgi?id=91444

Reviewed by Gavin Barraclough.

Various changes have been made to the SF calling convention, but
dumpCallFrame has not been updated to reflect these changes.
That resulted in both bogus information, as well as numerous
assertions of sadness.

This patch makes dumpCallFrame actually work again and adds the
wonderful feature of telling you the name of the variable that a
register reflects, or what value it contains.

* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::nameForRegister):
    A really innefficient mechanism for finding the name of a local register.
    This should only ever be used by debug code so this should be okay.
* bytecode/CodeBlock.h:
(CodeBlock):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::generate):
    Debug builds no longer throw away a functions symbol table, this allows
    us to actually perform a register# to name mapping
* dfg/DFGJITCompiler.cpp:
(JSC::DFG::JITCompiler::link):
    We weren't propogating the bytecode offset here leading to assertions
    in debug builds when dumping bytecode of DFG compiled code.
* interpreter/Interpreter.cpp:
(JSC):
(JSC::Interpreter::dumpRegisters):
     Rework to actually be correct.
(JSC::getCallerInfo):
     Return the byteocde offset as well now, given we have to determine it
     anyway.
(JSC::Interpreter::getStackTrace):
(JSC::Interpreter::retrieveCallerFromVMCode):
* interpreter/Interpreter.h:
(Interpreter):
* jsc.cpp:
(GlobalObject::finishCreation):
(functionDumpCallFrame):
     Give debug builds of JSC a method for calling dumpCallFrame so we can
     inspect a callframe without requiring us to break in a debugger.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@123042 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecode/CodeBlock.h
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/dfg/DFGJITCompiler.cpp
Source/JavaScriptCore/interpreter/Interpreter.cpp
Source/JavaScriptCore/interpreter/Interpreter.h
Source/JavaScriptCore/jsc.cpp

index 9da96af..e1dc680 100644 (file)
@@ -1,3 +1,50 @@
+2012-07-16  Oliver Hunt  <oliver@apple.com>
+
+        dumpCallFrame is broken in ToT
+        https://bugs.webkit.org/show_bug.cgi?id=91444
+
+        Reviewed by Gavin Barraclough.
+
+        Various changes have been made to the SF calling convention, but
+        dumpCallFrame has not been updated to reflect these changes.
+        That resulted in both bogus information, as well as numerous
+        assertions of sadness.
+
+        This patch makes dumpCallFrame actually work again and adds the
+        wonderful feature of telling you the name of the variable that a
+        register reflects, or what value it contains.
+
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::nameForRegister):
+            A really innefficient mechanism for finding the name of a local register.
+            This should only ever be used by debug code so this should be okay.
+        * bytecode/CodeBlock.h:
+        (CodeBlock):
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::generate):
+            Debug builds no longer throw away a functions symbol table, this allows
+            us to actually perform a register# to name mapping
+        * dfg/DFGJITCompiler.cpp:
+        (JSC::DFG::JITCompiler::link):
+            We weren't propogating the bytecode offset here leading to assertions
+            in debug builds when dumping bytecode of DFG compiled code.
+        * interpreter/Interpreter.cpp:
+        (JSC):
+        (JSC::Interpreter::dumpRegisters):
+             Rework to actually be correct.
+        (JSC::getCallerInfo):
+             Return the byteocde offset as well now, given we have to determine it
+             anyway.
+        (JSC::Interpreter::getStackTrace):
+        (JSC::Interpreter::retrieveCallerFromVMCode):
+        * interpreter/Interpreter.h:
+        (Interpreter):
+        * jsc.cpp:
+        (GlobalObject::finishCreation):
+        (functionDumpCallFrame):
+             Give debug builds of JSC a method for calling dumpCallFrame so we can
+             inspect a callframe without requiring us to break in a debugger.
+
 2012-07-18  Filip Pizlo  <fpizlo@apple.com>
 
         DFG 32-bit PutById transition stub storage reallocation case copies the first pointer of each JSValue instead of the whole JSValue
index dfa38aa..d8f838e 100755 (executable)
@@ -163,6 +163,7 @@ EXPORTS
     ?displayName@JSFunction@JSC@@QAE?BVUString@2@PAVExecState@2@@Z
     ?dtoa@WTF@@YAXQADNAA_NAAHAAI@Z
     ?dumpAllOptions@Options@JSC@@SAXPAU_iobuf@@@Z
+    ?dumpCallFrame@Interpreter@JSC@@QAEXPAVExecState@2@@Z
     ?dumpSampleData@JSGlobalData@JSC@@QAEXPAVExecState@2@@Z
     ?empty@StringImpl@WTF@@SAPAV12@XZ
     ?enumerable@PropertyDescriptor@JSC@@QBE_NXZ
index d417a5f..363efa2 100644 (file)
@@ -2942,4 +2942,29 @@ bool CodeBlock::usesOpcode(OpcodeID opcodeID)
     return false;
 }
 
+UString CodeBlock::nameForRegister(int registerNumber)
+{
+    SymbolTable::iterator end = m_symbolTable->end();
+    for (SymbolTable::iterator ptr = m_symbolTable->begin(); ptr != end; ++ptr) {
+        if (ptr->second.getIndex() == registerNumber)
+            return UString(ptr->first);
+    }
+    if (needsActivation() && registerNumber == activationRegister())
+        return "activation";
+    if (registerNumber == thisRegister())
+        return "this";
+    if (usesArguments()) {
+        if (registerNumber == argumentsRegister())
+            return "arguments";
+        if (unmodifiedArgumentsRegister(argumentsRegister()) == registerNumber)
+            return "real arguments";
+    }
+    if (registerNumber < 0) {
+        int argumentPosition = -registerNumber;
+        argumentPosition -= RegisterFile::CallFrameHeaderSize + 1;
+        return String::format("arguments[%3d]", argumentPosition - 1).impl();
+    }
+    return "";
+}
+
 } // namespace JSC
index 56ede59..2a7d212 100644 (file)
@@ -578,7 +578,9 @@ namespace JSC {
         void createActivation(CallFrame*);
 
         void clearEvalCache();
-
+        
+        UString nameForRegister(int registerNumber);
+        
         void addPropertyAccessInstruction(unsigned propertyAccessInstruction)
         {
             m_propertyAccessInstructions.append(propertyAccessInstruction);
index b104788..33f282c 100644 (file)
@@ -193,8 +193,10 @@ JSObject* BytecodeGenerator::generate()
     if (s_dumpsGeneratedCode)
         m_codeBlock->dump(m_scopeChain->globalObject->globalExec());
 
+#ifdef NDEBUG
     if ((m_codeType == FunctionCode && !m_codeBlock->needsFullScopeChain() && !m_codeBlock->usesArguments()) || m_codeType == EvalCode)
         symbolTable().clear();
+#endif
 
     m_codeBlock->shrinkToFit(CodeBlock::EarlyShrink);
 
index 5a9f972..ae28fad 100644 (file)
@@ -188,6 +188,7 @@ void JITCompiler::link(LinkBuffer& linkBuffer)
         CallLinkInfo& info = m_codeBlock->callLinkInfo(i);
         info.callType = m_jsCalls[i].m_callType;
         info.isDFG = true;
+        info.bytecodeIndex = m_jsCalls[i].m_codeOrigin.bytecodeIndex;
         linkBuffer.link(m_jsCalls[i].m_slowCall, FunctionPtr((m_globalData->getCTIStub(info.callType == CallLinkInfo::Construct ? linkConstructThunkGenerator : linkCallThunkGenerator)).code().executableAddress()));
         info.callReturnLocation = linkBuffer.locationOfNearCall(m_jsCalls[i].m_slowCall);
         info.hotPathBegin = linkBuffer.locationOf(m_jsCalls[i].m_targetToCheck);
index b6072a5..df22c32 100644 (file)
@@ -77,6 +77,8 @@ using namespace std;
 
 namespace JSC {
 
+static CallFrame* getCallerInfo(JSGlobalData*, CallFrame*, int& lineNumber, unsigned& bytecodeOffset);
+
 // Returns the depth of the scope chain within a given call frame.
 static int depth(CodeBlock* codeBlock, ScopeChainNode* sc)
 {
@@ -606,7 +608,13 @@ void Interpreter::initialize(LLInt::Data* llintData, bool canUseJIT)
 #endif
 }
 
-#ifndef NDEBUG
+#ifdef NDEBUG
+
+void Interpreter::dumpCallFrame(CallFrame*)
+{
+}
+
+#else
 
 void Interpreter::dumpCallFrame(CallFrame* callFrame)
 {
@@ -624,34 +632,42 @@ void Interpreter::dumpRegisters(CallFrame* callFrame)
     CodeBlock* codeBlock = callFrame->codeBlock();
     const Register* it;
     const Register* end;
-    JSValue v;
 
-    it = callFrame->registers() - RegisterFile::CallFrameHeaderSize - codeBlock->numParameters();
-    v = (*it).jsValue();
-#if USE(JSVALUE32_64)
-    dataLog("[this]                     | %10p | %-16s 0x%llx \n", it, v.description(), JSValue::encode(v)); ++it;
-#else
-    dataLog("[this]                     | %10p | %-16s %p \n", it, v.description(), JSValue::encode(v)); ++it;
-#endif
-    end = it + max(codeBlock->numParameters() - 1, 0); // - 1 to skip "this"
-    if (it != end) {
-        do {
-            v = (*it).jsValue();
+    it = callFrame->registers() - RegisterFile::CallFrameHeaderSize - callFrame->argumentCountIncludingThis();
+    end = callFrame->registers() - RegisterFile::CallFrameHeaderSize;
+    while (it < end) {
+        JSValue v = it->jsValue();
+        int registerNumber = it - callFrame->registers();
+        UString name = codeBlock->nameForRegister(registerNumber);
 #if USE(JSVALUE32_64)
-            dataLog("[param]                    | %10p | %-16s 0x%llx \n", it, v.description(), JSValue::encode(v));
+        dataLog("[r% 3d %14s]      | %10p | %-16s 0x%llx \n", registerNumber, name.ascii().data(), it, v.description(), JSValue::encode(v));
 #else
-            dataLog("[param]                    | %10p | %-16s %p \n", it, v.description(), JSValue::encode(v));
+        dataLog("[r% 3d %14s]      | %10p | %-16s %p \n", registerNumber, name.ascii().data(), it, v.description(), JSValue::encode(v));
 #endif
-            ++it;
-        } while (it != end);
+        it++;
     }
+    
     dataLog("-----------------------------------------------------------------------------\n");
-    dataLog("[CodeBlock]                | %10p | %p \n", it, (*it).codeBlock()); ++it;
-    dataLog("[ScopeChain]               | %10p | %p \n", it, (*it).scopeChain()); ++it;
-    dataLog("[CallerRegisters]          | %10p | %d \n", it, (*it).i()); ++it;
-    dataLog("[ReturnPC]                 | %10p | %p \n", it, (*it).vPC()); ++it;
-    dataLog("[ArgumentCount]            | %10p | %d \n", it, (*it).i()); ++it;
-    dataLog("[Callee]                   | %10p | %p \n", it, (*it).function()); ++it;
+    dataLog("[ArgumentCount]            | %10p | %ld \n", it, callFrame->argumentCount());
+    ++it;
+    dataLog("[CallerFrame]              | %10p | %p \n", it, callFrame->callerFrame());
+    ++it;
+    dataLog("[Callee]                   | %10p | %p \n", it, callFrame->callee());
+    ++it;
+    dataLog("[ScopeChain]               | %10p | %p \n", it, callFrame->scopeChain());
+    ++it;
+#if ENABLE(JIT)
+    AbstractPC pc = callFrame->abstractReturnPC(callFrame->globalData());
+    if (pc.hasJITReturnAddress())
+        dataLog("[ReturnJITPC]              | %10p | %p \n", it, pc.jitReturnAddress().value());
+#endif
+    unsigned bytecodeOffset = 0;
+    int line = 0;
+    getCallerInfo(&callFrame->globalData(), callFrame, line, bytecodeOffset);
+    dataLog("[ReturnVPC]                | %10p | %d (line %d)\n", it, bytecodeOffset, line);
+    ++it;
+    dataLog("[CodeBlock]                | %10p | %p \n", it, callFrame->codeBlock());
+    ++it;
     dataLog("-----------------------------------------------------------------------------\n");
 
     int registerCount = 0;
@@ -659,11 +675,13 @@ void Interpreter::dumpRegisters(CallFrame* callFrame)
     end = it + codeBlock->m_numVars;
     if (it != end) {
         do {
-            v = (*it).jsValue();
+            JSValue v = it->jsValue();
+            int registerNumber = it - callFrame->registers();
+            UString name = codeBlock->nameForRegister(registerNumber);
 #if USE(JSVALUE32_64)
-            dataLog("[r%2d]                      | %10p | %-16s 0x%llx \n", registerCount, it, v.description(), JSValue::encode(v));
+            dataLog("[r% 3d %14s]      | %10p | %-16s 0x%llx \n", registerNumber, name.ascii().data(), it, v.description(), JSValue::encode(v));
 #else
-            dataLog("[r%2d]                      | %10p | %-16s %p \n", registerCount, it, v.description(), JSValue::encode(v));
+            dataLog("[r% 3d %14s]      | %10p | %-16s %p \n", registerNumber, name.ascii().data(), it, v.description(), JSValue::encode(v));
 #endif
             ++it;
             ++registerCount;
@@ -674,11 +692,11 @@ void Interpreter::dumpRegisters(CallFrame* callFrame)
     end = it + codeBlock->m_numCalleeRegisters - codeBlock->m_numVars;
     if (it != end) {
         do {
-            v = (*it).jsValue();
+            JSValue v = (*it).jsValue();
 #if USE(JSVALUE32_64)
-            dataLog("[r%2d]                      | %10p | %-16s 0x%llx \n", registerCount, it, v.description(), JSValue::encode(v));
+            dataLog("[r% 3d]                     | %10p | %-16s 0x%llx \n", registerCount, it, v.description(), JSValue::encode(v));
 #else
-            dataLog("[r%2d]                      | %10p | %-16s %p \n", registerCount, it, v.description(), JSValue::encode(v));
+            dataLog("[r% 3d]                     | %10p | %-16s %p \n", registerCount, it, v.description(), JSValue::encode(v));
 #endif
             ++it;
             ++registerCount;
@@ -838,10 +856,10 @@ static int getLineNumberForCallFrame(JSGlobalData* globalData, CallFrame* callFr
 #endif
 }
 
-static CallFrame* getCallerInfo(JSGlobalData* globalData, CallFrame* callFrame, int& lineNumber)
+static CallFrame* getCallerInfo(JSGlobalData* globalData, CallFrame* callFrame, int& lineNumber, unsigned& bytecodeOffset)
 {
     UNUSED_PARAM(globalData);
-    unsigned bytecodeOffset = 0;
+    bytecodeOffset = 0;
     lineNumber = -1;
     ASSERT(!callFrame->hasHostCallFrameFlag());
     CallFrame* callerFrame = callFrame->codeBlock() ? callFrame->trueCallerFrame() : callFrame->callerFrame()->removeHostCallFrameFlag();
@@ -973,7 +991,8 @@ void Interpreter::getStackTrace(JSGlobalData* globalData, Vector<StackFrame>& re
             StackFrame s = { Strong<JSObject>(*globalData, callFrame->callee()), StackFrameNativeCode, Strong<ExecutableBase>(), -1, UString()};
             results.append(s);
         }
-        callFrame = getCallerInfo(globalData, callFrame, line);
+        unsigned unusedBytecodeOffset = 0;
+        callFrame = getCallerInfo(globalData, callFrame, line, unusedBytecodeOffset);
     }
 }
 
@@ -5330,7 +5349,8 @@ JSValue Interpreter::retrieveCallerFromVMCode(CallFrame* callFrame, JSFunction*
         return jsNull();
     
     int lineNumber;
-    CallFrame* callerFrame = getCallerInfo(&callFrame->globalData(), functionCallFrame, lineNumber);
+    unsigned bytecodeOffset;
+    CallFrame* callerFrame = getCallerInfo(&callFrame->globalData(), functionCallFrame, lineNumber, bytecodeOffset);
     if (!callerFrame)
         return jsNull();
     JSValue caller = callerFrame->callee();
@@ -5340,7 +5360,7 @@ JSValue Interpreter::retrieveCallerFromVMCode(CallFrame* callFrame, JSFunction*
     // Skip over function bindings.
     ASSERT(caller.isObject());
     while (asObject(caller)->inherits(&JSBoundFunction::s_info)) {
-        callerFrame = getCallerInfo(&callFrame->globalData(), callerFrame, lineNumber);
+        callerFrame = getCallerInfo(&callFrame->globalData(), callerFrame, lineNumber, bytecodeOffset);
         if (!callerFrame)
             return jsNull();
         caller = callerFrame->callee();
index ba2f4fa..8663bf8 100644 (file)
@@ -248,6 +248,9 @@ namespace JSC {
         void dumpSampleData(ExecState* exec);
         void startSampling();
         void stopSampling();
+
+        JS_EXPORT_PRIVATE void dumpCallFrame(CallFrame*);
+
     private:
         enum ExecutionFlag { Normal, InitializeAndReturn };
 
@@ -279,7 +282,6 @@ namespace JSC {
 
         JSValue privateExecute(ExecutionFlag, RegisterFile*, CallFrame*);
 
-        void dumpCallFrame(CallFrame*);
         void dumpRegisters(CallFrame*);
         
         bool isCallBytecode(Opcode opcode) { return opcode == getOpcode(op_call) || opcode == getOpcode(op_construct) || opcode == getOpcode(op_call_eval); }
index 5f3dbdb..00ad5d3 100644 (file)
@@ -88,6 +88,7 @@ static EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionGC(ExecState*);
 #ifndef NDEBUG
 static EncodedJSValue JSC_HOST_CALL functionReleaseExecutableMemory(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionDumpCallFrame(ExecState*);
 #endif
 static EncodedJSValue JSC_HOST_CALL functionVersion(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionRun(ExecState*);
@@ -194,6 +195,7 @@ protected:
         addFunction(globalData, "quit", functionQuit, 0);
         addFunction(globalData, "gc", functionGC, 0);
 #ifndef NDEBUG
+        addFunction(globalData, "dumpCallFrame", functionDumpCallFrame, 0);
         addFunction(globalData, "releaseExecutableMemory", functionReleaseExecutableMemory, 0);
 #endif
         addFunction(globalData, "version", functionVersion, 1);
@@ -281,6 +283,15 @@ EncodedJSValue JSC_HOST_CALL functionPrint(ExecState* exec)
     return JSValue::encode(jsUndefined());
 }
 
+#ifndef NDEBUG
+EncodedJSValue JSC_HOST_CALL functionDumpCallFrame(ExecState* exec)
+{
+    if (!exec->callerFrame()->hasHostCallFrameFlag())
+        exec->globalData().interpreter->dumpCallFrame(exec->callerFrame());
+    return JSValue::encode(jsUndefined());
+}
+#endif
+
 EncodedJSValue JSC_HOST_CALL functionDebug(ExecState* exec)
 {
     fprintf(stderr, "--> %s\n", exec->argument(0).toString(exec)->value(exec).utf8().data());