[SCM] WebKit Debian packaging branch, debian/experimental, updated. upstream/1.3.3-9427-gc2be6fc

barraclough at apple.com barraclough at apple.com
Wed Dec 22 16:09:07 UTC 2010


The following commit has been merged in the debian/experimental branch:
commit e7f6c08816b528473176390f51024196f6e9e5fc
Author: barraclough at apple.com <barraclough at apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Date:   Fri Nov 19 00:54:26 2010 +0000

    Bug 49635 - Profiler implementation is fragile
    
    Reviewed by Oliver Hunt.
    
    JavaScriptCore:
    
    The profile presently requires the exception handling mechanism to explicitly
    remove all stack frames that are exited during the exception unwind mechanism.
    This is fragile in a number of ways:
      * We have to change bytecode register allocation when compiling code to run
        when profiling, to preserve the callee function (this is also required to
        call did_call after the call has returned).
      * In the JIT we have to maintain additional data structures
        (CodeBlock::RareData::m_functionRegisterInfos) to map back to the register
        containing the callee.
      * In the interpreter we use 'magic values' to offset into the instruction
        stream to rediscover the register containing the function.
    
    Instead, move profiling into the head and tail of functions.
      * This correctly accounts the cost of the call itself to the caller.
      * This allows us to access the callee function object from the callframe.
      * This means that at the point a call is made we can track the stack depth
        on the ProfileNode.
      * When unwinding we can simply report the depth at which the exception is
        being handled - all call frames above this level are freed.
    
    * bytecode/CodeBlock.cpp:
    (JSC::CodeBlock::shrinkToFit):
    * bytecode/CodeBlock.h:
    (JSC::CodeBlock::bytecodeOffset):
    (JSC::CodeBlock::methodCallLinkInfo):
    * bytecompiler/BytecodeGenerator.cpp:
    (JSC::BytecodeGenerator::emitCall):
    (JSC::BytecodeGenerator::emitCallVarargs):
    * interpreter/Interpreter.cpp:
    (JSC::Interpreter::unwindCallFrame):
    (JSC::Interpreter::throwException):
    (JSC::Interpreter::execute):
    (JSC::Interpreter::executeCall):
    (JSC::Interpreter::executeConstruct):
    (JSC::Interpreter::privateExecute):
    * jit/JITStubs.cpp:
    (JSC::DEFINE_STUB_FUNCTION):
    * profiler/Profile.cpp:
    (JSC::Profile::Profile):
    * profiler/ProfileGenerator.cpp:
    (JSC::ProfileGenerator::addParentForConsoleStart):
    (JSC::ProfileGenerator::willExecute):
    (JSC::ProfileGenerator::didExecute):
    (JSC::ProfileGenerator::exceptionUnwind):
    (JSC::ProfileGenerator::stopProfiling):
    * profiler/ProfileGenerator.h:
    * profiler/ProfileNode.cpp:
    (JSC::ProfileNode::ProfileNode):
    (JSC::ProfileNode::willExecute):
    * profiler/ProfileNode.h:
    (JSC::ProfileNode::create):
    (JSC::ProfileNode::callerCallFrame):
    * profiler/Profiler.cpp:
    (JSC::dispatchFunctionToProfiles):
    (JSC::Profiler::_willExecute):
    (JSC::Profiler::_didExecute):
    (JSC::Profiler::exceptionUnwind):
    * profiler/Profiler.h:
    
    LayoutTests:
    
    Fixes previously failing tests - output was incorrect, showing duplicate entries
    for '(program) (no file) (line 1)'.
    
    * fast/profiler/throw-exception-from-eval-expected.txt:
    
    
    
    git-svn-id: http://svn.webkit.org/repository/webkit/trunk@72351 268f45cc-cd09-0410-ab3c-d52691b4dbfc

diff --git a/JavaScriptCore/ChangeLog b/JavaScriptCore/ChangeLog
index c6a0818..aa275a7 100644
--- a/JavaScriptCore/ChangeLog
+++ b/JavaScriptCore/ChangeLog
@@ -1,3 +1,68 @@
+2010-11-18  Gavin Barraclough  <barraclough at apple.com>
+
+        Reviewed by Oliver Hunt.
+
+        Bug 49635 - Profiler implementation is fragile
+
+        The profile presently requires the exception handling mechanism to explicitly
+        remove all stack frames that are exited during the exception unwind mechanism.
+        This is fragile in a number of ways:
+          * We have to change bytecode register allocation when compiling code to run
+            when profiling, to preserve the callee function (this is also required to
+            call did_call after the call has returned).
+          * In the JIT we have to maintain additional data structures
+            (CodeBlock::RareData::m_functionRegisterInfos) to map back to the register
+            containing the callee.
+          * In the interpreter we use 'magic values' to offset into the instruction
+            stream to rediscover the register containing the function.
+
+        Instead, move profiling into the head and tail of functions.
+          * This correctly accounts the cost of the call itself to the caller.
+          * This allows us to access the callee function object from the callframe.
+          * This means that at the point a call is made we can track the stack depth
+            on the ProfileNode.
+          * When unwinding we can simply report the depth at which the exception is
+            being handled - all call frames above this level are freed.
+
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::shrinkToFit):
+        * bytecode/CodeBlock.h:
+        (JSC::CodeBlock::bytecodeOffset):
+        (JSC::CodeBlock::methodCallLinkInfo):
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::emitCall):
+        (JSC::BytecodeGenerator::emitCallVarargs):
+        * interpreter/Interpreter.cpp:
+        (JSC::Interpreter::unwindCallFrame):
+        (JSC::Interpreter::throwException):
+        (JSC::Interpreter::execute):
+        (JSC::Interpreter::executeCall):
+        (JSC::Interpreter::executeConstruct):
+        (JSC::Interpreter::privateExecute):
+        * jit/JITStubs.cpp:
+        (JSC::DEFINE_STUB_FUNCTION):
+        * profiler/Profile.cpp:
+        (JSC::Profile::Profile):
+        * profiler/ProfileGenerator.cpp:
+        (JSC::ProfileGenerator::addParentForConsoleStart):
+        (JSC::ProfileGenerator::willExecute):
+        (JSC::ProfileGenerator::didExecute):
+        (JSC::ProfileGenerator::exceptionUnwind):
+        (JSC::ProfileGenerator::stopProfiling):
+        * profiler/ProfileGenerator.h:
+        * profiler/ProfileNode.cpp:
+        (JSC::ProfileNode::ProfileNode):
+        (JSC::ProfileNode::willExecute):
+        * profiler/ProfileNode.h:
+        (JSC::ProfileNode::create):
+        (JSC::ProfileNode::callerCallFrame):
+        * profiler/Profiler.cpp:
+        (JSC::dispatchFunctionToProfiles):
+        (JSC::Profiler::_willExecute):
+        (JSC::Profiler::_didExecute):
+        (JSC::Profiler::exceptionUnwind):
+        * profiler/Profiler.h:
+
 2010-11-18  Steve Falkenburg  <sfalken at apple.com>
 
         Reviewed by Adam Roben.
diff --git a/JavaScriptCore/bytecode/CodeBlock.cpp b/JavaScriptCore/bytecode/CodeBlock.cpp
index bcd2af3..00ebde8 100644
--- a/JavaScriptCore/bytecode/CodeBlock.cpp
+++ b/JavaScriptCore/bytecode/CodeBlock.cpp
@@ -1662,32 +1662,6 @@ void CodeBlock::expressionRangeForBytecodeOffset(CallFrame* callFrame, unsigned
     return;
 }
 
-#if ENABLE(JIT)
-bool CodeBlock::functionRegisterForBytecodeOffset(unsigned bytecodeOffset, int& functionRegisterIndex)
-{
-    ASSERT(bytecodeOffset < m_instructionCount);
-
-    if (!m_rareData || !m_rareData->m_functionRegisterInfos.size())
-        return false;
-
-    int low = 0;
-    int high = m_rareData->m_functionRegisterInfos.size();
-    while (low < high) {
-        int mid = low + (high - low) / 2;
-        if (m_rareData->m_functionRegisterInfos[mid].bytecodeOffset <= bytecodeOffset)
-            low = mid + 1;
-        else
-            high = mid;
-    }
-
-    if (!low || m_rareData->m_functionRegisterInfos[low - 1].bytecodeOffset != bytecodeOffset)
-        return false;
-
-    functionRegisterIndex = m_rareData->m_functionRegisterInfos[low - 1].functionRegisterIndex;
-    return true;
-}
-#endif
-
 #if ENABLE(INTERPRETER)
 bool CodeBlock::hasGlobalResolveInstructionAtBytecodeOffset(unsigned bytecodeOffset)
 {
@@ -1762,9 +1736,6 @@ void CodeBlock::shrinkToFit()
         m_rareData->m_immediateSwitchJumpTables.shrinkToFit();
         m_rareData->m_characterSwitchJumpTables.shrinkToFit();
         m_rareData->m_stringSwitchJumpTables.shrinkToFit();
-#if ENABLE(JIT)
-        m_rareData->m_functionRegisterInfos.shrinkToFit();
-#endif
     }
 }
 
diff --git a/JavaScriptCore/bytecode/CodeBlock.h b/JavaScriptCore/bytecode/CodeBlock.h
index 54acd50..5298327 100644
--- a/JavaScriptCore/bytecode/CodeBlock.h
+++ b/JavaScriptCore/bytecode/CodeBlock.h
@@ -351,8 +351,6 @@ namespace JSC {
                 return 1;
             return binaryChop<CallReturnOffsetToBytecodeOffset, unsigned, getCallReturnOffset>(callReturnIndexVector().begin(), callReturnIndexVector().size(), getJITCode().offsetOf(returnAddress.value()))->bytecodeOffset;
         }
-        
-        bool functionRegisterForBytecodeOffset(unsigned bytecodeOffset, int& functionRegisterIndex);
 #endif
 #if ENABLE(INTERPRETER)
         unsigned bytecodeOffset(CallFrame*, Instruction* returnAddress)
@@ -443,8 +441,6 @@ namespace JSC {
 
         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
 
         // Exception handling support
@@ -598,10 +594,6 @@ namespace JSC {
             Vector<StringJumpTable> m_stringSwitchJumpTables;
 
             EvalCodeCache m_evalCodeCache;
-
-#if ENABLE(JIT)
-            Vector<FunctionRegisterInfo> m_functionRegisterInfos;
-#endif
         };
         OwnPtr<RareData> m_rareData;
     };
diff --git a/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
index 1fa5aa4..0221cfa 100644
--- a/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
+++ b/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
@@ -1649,10 +1649,6 @@ RegisterID* BytecodeGenerator::emitCall(OpcodeID opcodeID, RegisterID* dst, Regi
     if (m_shouldEmitProfileHooks) {
         emitOpcode(op_profile_will_call);
         instructions().append(callArguments.profileHookRegister()->index());
-
-#if ENABLE(JIT)
-        m_codeBlock->addFunctionRegisterInfo(instructions().size(), callArguments.profileHookRegister()->index());
-#endif
     }
 
     emitExpressionInfo(divot, startOffset, endOffset);
@@ -1697,10 +1693,6 @@ RegisterID* BytecodeGenerator::emitCallVarargs(RegisterID* dst, RegisterID* func
     if (m_shouldEmitProfileHooks) {
         emitOpcode(op_profile_will_call);
         instructions().append(func->index());
-        
-#if ENABLE(JIT)
-        m_codeBlock->addFunctionRegisterInfo(instructions().size(), func->index());
-#endif
     }
     
     emitExpressionInfo(divot, startOffset, endOffset);
diff --git a/JavaScriptCore/interpreter/Interpreter.cpp b/JavaScriptCore/interpreter/Interpreter.cpp
index 0a1be48..d75dca2 100644
--- a/JavaScriptCore/interpreter/Interpreter.cpp
+++ b/JavaScriptCore/interpreter/Interpreter.cpp
@@ -558,13 +558,6 @@ NEVER_INLINE bool Interpreter::unwindCallFrame(CallFrame*& callFrame, JSValue ex
             debugger->didExecuteProgram(debuggerCallFrame, codeBlock->ownerExecutable()->sourceID(), codeBlock->ownerExecutable()->lastLine());
     }
 
-    if (Profiler* profiler = *Profiler::enabledProfilerReference()) {
-        if (callFrame->callee())
-            profiler->didExecute(callFrame, callFrame->callee());
-        else
-            profiler->didExecute(callFrame, codeBlock->ownerExecutable()->sourceURL(), codeBlock->ownerExecutable()->lineNo());
-    }
-
     // If this call frame created an activation or an 'arguments' object, tear it off.
     if (oldCodeBlock->codeType() == FunctionCode && oldCodeBlock->needsFullScopeChain()) {
         if (!callFrame->r(oldCodeBlock->activationRegister()).jsValue()) {
@@ -657,9 +650,10 @@ static void appendSourceToError(CallFrame* callFrame, ErrorInstance* exception,
 
 NEVER_INLINE HandlerInfo* Interpreter::throwException(CallFrame*& callFrame, JSValue& exceptionValue, unsigned bytecodeOffset, bool explicitThrow)
 {
-    // Set up the exception object
-
     CodeBlock* codeBlock = callFrame->codeBlock();
+    bool isInterrupt = false;
+
+    // Set up the exception object
     if (exceptionValue.isObject()) {
         JSObject* exception = asObject(exceptionValue);
         if (!explicitThrow && exception->isErrorInstance() && static_cast<ErrorInstance*>(exception)->appendSourceToMessage())
@@ -671,12 +665,7 @@ NEVER_INLINE HandlerInfo* Interpreter::throwException(CallFrame*& callFrame, JSV
             addErrorInfo(callFrame, exception, codeBlock->lineNumberForBytecodeOffset(callFrame, bytecodeOffset), codeBlock->ownerExecutable()->source());
 
         ComplType exceptionType = exception->exceptionType();
-        if (exceptionType == Interrupted || exceptionType == Terminated) {
-            while (unwindCallFrame(callFrame, exceptionValue, bytecodeOffset, codeBlock)) {
-                // Don't need handler checks or anything, we just want to unroll all the JS callframes possible.
-            }
-            return 0;
-        }
+        isInterrupt = exceptionType == Interrupted || exceptionType == Terminated;
     }
 
     if (Debugger* debugger = callFrame->dynamicGlobalObject()->debugger()) {
@@ -685,39 +674,19 @@ NEVER_INLINE HandlerInfo* Interpreter::throwException(CallFrame*& callFrame, JSV
         debugger->exception(debuggerCallFrame, codeBlock->ownerExecutable()->sourceID(), codeBlock->lineNumberForBytecodeOffset(callFrame, bytecodeOffset), hasHandler);
     }
 
-    // If we throw in the middle of a call instruction, we need to notify
-    // the profiler manually that the call instruction has returned, since
-    // we'll never reach the relevant op_profile_did_call.
-    if (Profiler* profiler = *Profiler::enabledProfilerReference()) {
-#if ENABLE(INTERPRETER)
-        if (!callFrame->globalData().canUseJIT()) {
-            // FIXME: Why 8? - work out what this magic value is, replace the constant with something more helpful.
-            if (isCallBytecode(codeBlock->instructions()[bytecodeOffset].u.opcode))
-                profiler->didExecute(callFrame, callFrame->r(codeBlock->instructions()[bytecodeOffset + 1].u.operand).jsValue());
-            else if (codeBlock->instructions().size() > (bytecodeOffset + 8) && codeBlock->instructions()[bytecodeOffset + 8].u.opcode == getOpcode(op_construct))
-                profiler->didExecute(callFrame, callFrame->r(codeBlock->instructions()[bytecodeOffset + 9].u.operand).jsValue());
-        }
-#if ENABLE(JIT)
-        else
-#endif
-#endif
-#if ENABLE(JIT)
-        {
-            int functionRegisterIndex;
-            if (codeBlock->functionRegisterForBytecodeOffset(bytecodeOffset, functionRegisterIndex))
-                profiler->didExecute(callFrame, callFrame->r(functionRegisterIndex).jsValue());
-        }
-#endif
-    }
-
     // Calculate an exception handler vPC, unwinding call frames as necessary.
-
     HandlerInfo* handler = 0;
-    while (!(handler = codeBlock->handlerForBytecodeOffset(bytecodeOffset))) {
-        if (!unwindCallFrame(callFrame, exceptionValue, bytecodeOffset, codeBlock))
+    while (isInterrupt || !(handler = codeBlock->handlerForBytecodeOffset(bytecodeOffset))) {
+        if (!unwindCallFrame(callFrame, exceptionValue, bytecodeOffset, codeBlock)) {
+            if (Profiler* profiler = *Profiler::enabledProfilerReference())
+                profiler->exceptionUnwind(callFrame);
             return 0;
+        }
     }
 
+    if (Profiler* profiler = *Profiler::enabledProfilerReference())
+        profiler->exceptionUnwind(callFrame);
+
     // Shrink the JS stack, in case stack overflow made it huge.
     Register* highWaterMark = 0;
     for (CallFrame* callerFrame = callFrame; callerFrame; callerFrame = callerFrame->callerFrame()->removeHostCallFrameFlag()) {
@@ -789,7 +758,7 @@ JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, S
 
     Profiler** profiler = Profiler::enabledProfilerReference();
     if (*profiler)
-        (*profiler)->willExecute(newCallFrame, program->sourceURL(), program->lineNo());
+        (*profiler)->willExecute(callFrame, program->sourceURL(), program->lineNo());
 
     JSValue result;
     {
@@ -860,7 +829,7 @@ JSValue Interpreter::executeCall(CallFrame* callFrame, JSObject* function, CallT
 
         Profiler** profiler = Profiler::enabledProfilerReference();
         if (*profiler)
-            (*profiler)->willExecute(newCallFrame, function);
+            (*profiler)->willExecute(callFrame, function);
 
         JSValue result;
         {
@@ -877,7 +846,7 @@ JSValue Interpreter::executeCall(CallFrame* callFrame, JSObject* function, CallT
         }
 
         if (*profiler)
-            (*profiler)->didExecute(newCallFrame, function);
+            (*profiler)->didExecute(callFrame, function);
 
         m_registerFile.shrink(oldEnd);
         return checkedReturn(result);
@@ -892,7 +861,7 @@ JSValue Interpreter::executeCall(CallFrame* callFrame, JSObject* function, CallT
 
     Profiler** profiler = Profiler::enabledProfilerReference();
     if (*profiler)
-        (*profiler)->willExecute(newCallFrame, function);
+        (*profiler)->willExecute(callFrame, function);
 
     JSValue result;
     {
@@ -901,7 +870,7 @@ JSValue Interpreter::executeCall(CallFrame* callFrame, JSObject* function, CallT
     }
 
     if (*profiler)
-        (*profiler)->didExecute(newCallFrame, function);
+        (*profiler)->didExecute(callFrame, function);
 
     m_registerFile.shrink(oldEnd);
     return checkedReturn(result);
@@ -949,7 +918,7 @@ JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* construc
 
         Profiler** profiler = Profiler::enabledProfilerReference();
         if (*profiler)
-            (*profiler)->willExecute(newCallFrame, constructor);
+            (*profiler)->willExecute(callFrame, constructor);
 
         JSValue result;
         {
@@ -966,7 +935,7 @@ JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* construc
         }
 
         if (*profiler)
-            (*profiler)->didExecute(newCallFrame, constructor);
+            (*profiler)->didExecute(callFrame, constructor);
 
         m_registerFile.shrink(oldEnd);
         if (callFrame->hadException())
@@ -984,7 +953,7 @@ JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* construc
 
     Profiler** profiler = Profiler::enabledProfilerReference();
     if (*profiler)
-        (*profiler)->willExecute(newCallFrame, constructor);
+        (*profiler)->willExecute(callFrame, constructor);
 
     JSValue result;
     {
@@ -993,7 +962,7 @@ JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* construc
     }
 
     if (*profiler)
-        (*profiler)->didExecute(newCallFrame, constructor);
+        (*profiler)->didExecute(callFrame, constructor);
 
     m_registerFile.shrink(oldEnd);
     if (callFrame->hadException())
@@ -1159,7 +1128,7 @@ JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSObjec
 
     Profiler** profiler = Profiler::enabledProfilerReference();
     if (*profiler)
-        (*profiler)->willExecute(newCallFrame, eval->sourceURL(), eval->lineNo());
+        (*profiler)->willExecute(callFrame, eval->sourceURL(), eval->lineNo());
 
     JSValue result;
     {
diff --git a/JavaScriptCore/profiler/Profile.cpp b/JavaScriptCore/profiler/Profile.cpp
index bc239b3..1a84518 100644
--- a/JavaScriptCore/profiler/Profile.cpp
+++ b/JavaScriptCore/profiler/Profile.cpp
@@ -42,7 +42,7 @@ Profile::Profile(const UString& title, unsigned uid)
 {
     // FIXME: When multi-threading is supported this will be a vector and calls
     // into the profiler will need to know which thread it is executing on.
-    m_head = ProfileNode::create(CallIdentifier("Thread_1", UString(), 0), 0, 0);
+    m_head = ProfileNode::create(0, CallIdentifier("Thread_1", UString(), 0), 0, 0);
 }
 
 Profile::~Profile()
diff --git a/JavaScriptCore/profiler/ProfileGenerator.cpp b/JavaScriptCore/profiler/ProfileGenerator.cpp
index 1d916ea..68d1733 100644
--- a/JavaScriptCore/profiler/ProfileGenerator.cpp
+++ b/JavaScriptCore/profiler/ProfileGenerator.cpp
@@ -63,7 +63,7 @@ void ProfileGenerator::addParentForConsoleStart(ExecState* exec)
     JSValue function;
 
     exec->interpreter()->retrieveLastCaller(exec, lineNumber, sourceID, sourceURL, function);
-    m_currentNode = ProfileNode::create(Profiler::createCallIdentifier(exec, function ? function.toThisObject(exec) : 0, sourceURL, lineNumber), m_head.get(), m_head.get());
+    m_currentNode = ProfileNode::create(exec, Profiler::createCallIdentifier(exec, function ? function.toThisObject(exec) : 0, sourceURL, lineNumber), m_head.get(), m_head.get());
     m_head->insertNode(m_currentNode.get());
 }
 
@@ -72,7 +72,7 @@ const UString& ProfileGenerator::title() const
     return m_profile->title();
 }
 
-void ProfileGenerator::willExecute(const CallIdentifier& callIdentifier)
+void ProfileGenerator::willExecute(ExecState* callerCallFrame, const CallIdentifier& callIdentifier)
 {
     if (JAVASCRIPTCORE_PROFILE_WILL_EXECUTE_ENABLED()) {
         CString name = callIdentifier.m_name.utf8();
@@ -83,11 +83,11 @@ void ProfileGenerator::willExecute(const CallIdentifier& callIdentifier)
     if (!m_originatingGlobalExec)
         return;
 
-    ASSERT_ARG(m_currentNode, m_currentNode);
-    m_currentNode = m_currentNode->willExecute(callIdentifier);
+    ASSERT(m_currentNode);
+    m_currentNode = m_currentNode->willExecute(callerCallFrame, callIdentifier);
 }
 
-void ProfileGenerator::didExecute(const CallIdentifier& callIdentifier)
+void ProfileGenerator::didExecute(ExecState* callerCallFrame, const CallIdentifier& callIdentifier)
 {
     if (JAVASCRIPTCORE_PROFILE_DID_EXECUTE_ENABLED()) {
         CString name = callIdentifier.m_name.utf8();
@@ -98,9 +98,9 @@ void ProfileGenerator::didExecute(const CallIdentifier& callIdentifier)
     if (!m_originatingGlobalExec)
         return;
 
-    ASSERT_ARG(m_currentNode, m_currentNode);
+    ASSERT(m_currentNode);
     if (m_currentNode->callIdentifier() != callIdentifier) {
-        RefPtr<ProfileNode> returningNode = ProfileNode::create(callIdentifier, m_head.get(), m_currentNode.get());
+        RefPtr<ProfileNode> returningNode = ProfileNode::create(callerCallFrame, callIdentifier, m_head.get(), m_currentNode.get());
         returningNode->setStartTime(m_currentNode->startTime());
         returningNode->didExecute();
         m_currentNode->insertNode(returningNode.release());
@@ -110,6 +110,17 @@ void ProfileGenerator::didExecute(const CallIdentifier& callIdentifier)
     m_currentNode = m_currentNode->didExecute();
 }
 
+void ProfileGenerator::exceptionUnwind(ExecState* handlerCallFrame, const CallIdentifier&)
+{
+    // If the current node was called by the handler (==) or any
+    // more nested function (>) the we have exited early from it.
+    ASSERT(m_currentNode);
+    while (m_currentNode->callerCallFrame() >= handlerCallFrame) {
+        didExecute(m_currentNode->callerCallFrame(), m_currentNode->callIdentifier());
+        ASSERT(m_currentNode);
+    }
+}
+
 void ProfileGenerator::stopProfiling()
 {
     m_profile->forEach(&ProfileNode::stopProfiling);
@@ -117,14 +128,14 @@ void ProfileGenerator::stopProfiling()
     removeProfileStart();
     removeProfileEnd();
 
-    ASSERT_ARG(m_currentNode, m_currentNode);
+    ASSERT(m_currentNode);
 
     // Set the current node to the parent, because we are in a call that
     // will not get didExecute call.
     m_currentNode = m_currentNode->parent();
 
    if (double headSelfTime = m_head->selfTime()) {
-        RefPtr<ProfileNode> idleNode = ProfileNode::create(CallIdentifier(NonJSExecution, UString(), 0), m_head.get(), m_head.get());
+        RefPtr<ProfileNode> idleNode = ProfileNode::create(0, CallIdentifier(NonJSExecution, UString(), 0), m_head.get(), m_head.get());
 
         idleNode->setTotalTime(headSelfTime);
         idleNode->setSelfTime(headSelfTime);
diff --git a/JavaScriptCore/profiler/ProfileGenerator.h b/JavaScriptCore/profiler/ProfileGenerator.h
index 82149b3..cbed73b 100644
--- a/JavaScriptCore/profiler/ProfileGenerator.h
+++ b/JavaScriptCore/profiler/ProfileGenerator.h
@@ -50,13 +50,15 @@ namespace JSC {
         unsigned profileGroup() const { return m_profileGroup; }
 
         // Collecting
-        void willExecute(const CallIdentifier&);
-        void didExecute(const CallIdentifier&);
+        void willExecute(ExecState* callerCallFrame, const CallIdentifier&);
+        void didExecute(ExecState* callerCallFrame, const CallIdentifier&);
+
+        void exceptionUnwind(ExecState* handlerCallFrame, const CallIdentifier&);
 
         // Stopping Profiling
         void stopProfiling();
 
-        typedef void (ProfileGenerator::*ProfileFunction)(const CallIdentifier& callIdentifier);
+        typedef void (ProfileGenerator::*ProfileFunction)(ExecState* callerOrHandlerCallFrame, const CallIdentifier& callIdentifier);
 
     private:
         ProfileGenerator(const UString& title, ExecState* originatingExec, unsigned uid);
diff --git a/JavaScriptCore/profiler/ProfileNode.cpp b/JavaScriptCore/profiler/ProfileNode.cpp
index bfcfcbe..8f20bbe 100644
--- a/JavaScriptCore/profiler/ProfileNode.cpp
+++ b/JavaScriptCore/profiler/ProfileNode.cpp
@@ -56,8 +56,9 @@ static double getCount()
 #endif
 }
 
-ProfileNode::ProfileNode(const CallIdentifier& callIdentifier, ProfileNode* headNode, ProfileNode* parentNode)
-    : m_callIdentifier(callIdentifier)
+ProfileNode::ProfileNode(ExecState* callerCallFrame, const CallIdentifier& callIdentifier, ProfileNode* headNode, ProfileNode* parentNode)
+    : m_callerCallFrame(callerCallFrame)
+    , m_callIdentifier(callIdentifier)
     , m_head(headNode)
     , m_parent(parentNode)
     , m_nextSibling(0)
@@ -72,8 +73,9 @@ ProfileNode::ProfileNode(const CallIdentifier& callIdentifier, ProfileNode* head
     startTimer();
 }
 
-ProfileNode::ProfileNode(ProfileNode* headNode, ProfileNode* nodeToCopy)
-    : m_callIdentifier(nodeToCopy->callIdentifier())
+ProfileNode::ProfileNode(ExecState* callerCallFrame, ProfileNode* headNode, ProfileNode* nodeToCopy)
+    : m_callerCallFrame(callerCallFrame)
+    , m_callIdentifier(nodeToCopy->callIdentifier())
     , m_head(headNode)
     , m_parent(nodeToCopy->parent())
     , m_nextSibling(0)
@@ -87,7 +89,7 @@ ProfileNode::ProfileNode(ProfileNode* headNode, ProfileNode* nodeToCopy)
 {
 }
 
-ProfileNode* ProfileNode::willExecute(const CallIdentifier& callIdentifier)
+ProfileNode* ProfileNode::willExecute(ExecState* callerCallFrame, const CallIdentifier& callIdentifier)
 {
     for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild) {
         if ((*currentChild)->callIdentifier() == callIdentifier) {
@@ -96,7 +98,7 @@ ProfileNode* ProfileNode::willExecute(const CallIdentifier& callIdentifier)
         }
     }
 
-    RefPtr<ProfileNode> newChild = ProfileNode::create(callIdentifier, m_head ? m_head : this, this); // If this ProfileNode has no head it is the head.
+    RefPtr<ProfileNode> newChild = ProfileNode::create(callerCallFrame, callIdentifier, m_head ? m_head : this, this); // If this ProfileNode has no head it is the head.
     if (m_children.size())
         m_children.last()->setNextSibling(newChild.get());
     m_children.append(newChild.release());
diff --git a/JavaScriptCore/profiler/ProfileNode.h b/JavaScriptCore/profiler/ProfileNode.h
index eafeea6..ffe7b6f 100644
--- a/JavaScriptCore/profiler/ProfileNode.h
+++ b/JavaScriptCore/profiler/ProfileNode.h
@@ -37,6 +37,7 @@
 
 namespace JSC {
 
+    class ExecState;
     class ProfileNode;
 
     typedef Vector<RefPtr<ProfileNode> >::const_iterator StackIterator;
@@ -44,23 +45,24 @@ namespace JSC {
 
     class ProfileNode : public RefCounted<ProfileNode> {
     public:
-        static PassRefPtr<ProfileNode> create(const CallIdentifier& callIdentifier, ProfileNode* headNode, ProfileNode* parentNode)
+        static PassRefPtr<ProfileNode> create(ExecState* callerCallFrame, const CallIdentifier& callIdentifier, ProfileNode* headNode, ProfileNode* parentNode)
         {
-            return adoptRef(new ProfileNode(callIdentifier, headNode, parentNode));
+            return adoptRef(new ProfileNode(callerCallFrame, callIdentifier, headNode, parentNode));
         }
-        static PassRefPtr<ProfileNode> create(ProfileNode* headNode, ProfileNode* node)
+        static PassRefPtr<ProfileNode> create(ExecState* callerCallFrame, ProfileNode* headNode, ProfileNode* node)
         {
-            return adoptRef(new ProfileNode(headNode, node));
+            return adoptRef(new ProfileNode(callerCallFrame, headNode, node));
         }
 
         bool operator==(ProfileNode* node) { return m_callIdentifier == node->callIdentifier(); }
 
-        ProfileNode* willExecute(const CallIdentifier&);
+        ProfileNode* willExecute(ExecState* callerCallFrame, const CallIdentifier&);
         ProfileNode* didExecute();
 
         void stopProfiling();
 
         // CallIdentifier members
+        ExecState* callerCallFrame() const { return m_callerCallFrame; }
         const CallIdentifier& callIdentifier() const { return m_callIdentifier; }
         const UString& functionName() const { return m_callIdentifier.m_name; }
         const UString& url() const { return m_callIdentifier.m_url; }
@@ -128,8 +130,8 @@ namespace JSC {
 #endif
 
     private:
-        ProfileNode(const CallIdentifier&, ProfileNode* headNode, ProfileNode* parentNode);
-        ProfileNode(ProfileNode* headNode, ProfileNode* nodeToCopy);
+        ProfileNode(ExecState* callerCallFrame, const CallIdentifier&, ProfileNode* headNode, ProfileNode* parentNode);
+        ProfileNode(ExecState* callerCallFrame, ProfileNode* headNode, ProfileNode* nodeToCopy);
 
         void startTimer();
         void resetChildrensSiblings();
@@ -147,6 +149,7 @@ namespace JSC {
         static inline bool functionNameDescendingComparator(const RefPtr<ProfileNode>& a, const RefPtr<ProfileNode>& b) { return a->functionName() > b->functionName(); }
         static inline bool functionNameAscendingComparator(const RefPtr<ProfileNode>& a, const RefPtr<ProfileNode>& b) { return a->functionName() < b->functionName(); }
 
+        ExecState* m_callerCallFrame;
         CallIdentifier m_callIdentifier;
         ProfileNode* m_head;
         ProfileNode* m_parent;
diff --git a/JavaScriptCore/profiler/Profiler.cpp b/JavaScriptCore/profiler/Profiler.cpp
index 78fe2f5..9ac73fd 100644
--- a/JavaScriptCore/profiler/Profiler.cpp
+++ b/JavaScriptCore/profiler/Profiler.cpp
@@ -99,42 +99,49 @@ PassRefPtr<Profile> Profiler::stopProfiling(ExecState* exec, const UString& titl
     return 0;
 }
 
-static inline void dispatchFunctionToProfiles(const Vector<RefPtr<ProfileGenerator> >& profiles, ProfileGenerator::ProfileFunction function, const CallIdentifier& callIdentifier, unsigned currentProfileTargetGroup)
+static inline void dispatchFunctionToProfiles(ExecState* callerOrHandlerCallFrame, const Vector<RefPtr<ProfileGenerator> >& profiles, ProfileGenerator::ProfileFunction function, const CallIdentifier& callIdentifier, unsigned currentProfileTargetGroup)
 {
     for (size_t i = 0; i < profiles.size(); ++i) {
         if (profiles[i]->profileGroup() == currentProfileTargetGroup || !profiles[i]->originatingGlobalExec())
-            (profiles[i].get()->*function)(callIdentifier);
+            (profiles[i].get()->*function)(callerOrHandlerCallFrame, callIdentifier);
     }
 }
 
-void Profiler::willExecute(ExecState* exec, JSValue function)
+void Profiler::willExecute(ExecState* callerCallFrame, JSValue function)
 {
     ASSERT(!m_currentProfiles.isEmpty());
 
-    dispatchFunctionToProfiles(m_currentProfiles, &ProfileGenerator::willExecute, createCallIdentifier(exec, function, "", 0), exec->lexicalGlobalObject()->profileGroup());
+    dispatchFunctionToProfiles(callerCallFrame, m_currentProfiles, &ProfileGenerator::willExecute, createCallIdentifier(callerCallFrame, function, "", 0), callerCallFrame->lexicalGlobalObject()->profileGroup());
 }
 
-void Profiler::willExecute(ExecState* exec, const UString& sourceURL, int startingLineNumber)
+void Profiler::willExecute(ExecState* callerCallFrame, const UString& sourceURL, int startingLineNumber)
 {
     ASSERT(!m_currentProfiles.isEmpty());
 
-    CallIdentifier callIdentifier = createCallIdentifier(exec, JSValue(), sourceURL, startingLineNumber);
+    CallIdentifier callIdentifier = createCallIdentifier(callerCallFrame, JSValue(), sourceURL, startingLineNumber);
 
-    dispatchFunctionToProfiles(m_currentProfiles, &ProfileGenerator::willExecute, callIdentifier, exec->lexicalGlobalObject()->profileGroup());
+    dispatchFunctionToProfiles(callerCallFrame, m_currentProfiles, &ProfileGenerator::willExecute, callIdentifier, callerCallFrame->lexicalGlobalObject()->profileGroup());
 }
 
-void Profiler::didExecute(ExecState* exec, JSValue function)
+void Profiler::didExecute(ExecState* callerCallFrame, JSValue function)
 {
     ASSERT(!m_currentProfiles.isEmpty());
 
-    dispatchFunctionToProfiles(m_currentProfiles, &ProfileGenerator::didExecute, createCallIdentifier(exec, function, "", 0), exec->lexicalGlobalObject()->profileGroup());
+    dispatchFunctionToProfiles(callerCallFrame, m_currentProfiles, &ProfileGenerator::didExecute, createCallIdentifier(callerCallFrame, function, "", 0), callerCallFrame->lexicalGlobalObject()->profileGroup());
 }
 
-void Profiler::didExecute(ExecState* exec, const UString& sourceURL, int startingLineNumber)
+void Profiler::didExecute(ExecState* callerCallFrame, const UString& sourceURL, int startingLineNumber)
 {
     ASSERT(!m_currentProfiles.isEmpty());
 
-    dispatchFunctionToProfiles(m_currentProfiles, &ProfileGenerator::didExecute, createCallIdentifier(exec, JSValue(), sourceURL, startingLineNumber), exec->lexicalGlobalObject()->profileGroup());
+    dispatchFunctionToProfiles(callerCallFrame, m_currentProfiles, &ProfileGenerator::didExecute, createCallIdentifier(callerCallFrame, JSValue(), sourceURL, startingLineNumber), callerCallFrame->lexicalGlobalObject()->profileGroup());
+}
+
+void Profiler::exceptionUnwind(ExecState* handlerCallFrame)
+{
+    ASSERT(!m_currentProfiles.isEmpty());
+
+    dispatchFunctionToProfiles(handlerCallFrame, m_currentProfiles, &ProfileGenerator::exceptionUnwind, createCallIdentifier(handlerCallFrame, JSValue(), "", 0), handlerCallFrame->lexicalGlobalObject()->profileGroup());
 }
 
 CallIdentifier Profiler::createCallIdentifier(ExecState* exec, JSValue functionValue, const UString& defaultSourceURL, int defaultLineNumber)
diff --git a/JavaScriptCore/profiler/Profiler.h b/JavaScriptCore/profiler/Profiler.h
index 4b8b4a0..f9c2ccb 100644
--- a/JavaScriptCore/profiler/Profiler.h
+++ b/JavaScriptCore/profiler/Profiler.h
@@ -57,10 +57,12 @@ namespace JSC {
         void startProfiling(ExecState*, const UString& title);
         PassRefPtr<Profile> stopProfiling(ExecState*, const UString& title);
 
-        void willExecute(ExecState*, JSValue function);
-        void willExecute(ExecState*, const UString& sourceURL, int startingLineNumber);
-        void didExecute(ExecState*, JSValue function);
-        void didExecute(ExecState*, const UString& sourceURL, int startingLineNumber);
+        void willExecute(ExecState* callerCallFrame, JSValue function);
+        void willExecute(ExecState* callerCallFrame, const UString& sourceURL, int startingLineNumber);
+        void didExecute(ExecState* callerCallFrame, JSValue function);
+        void didExecute(ExecState* callerCallFrame, const UString& sourceURL, int startingLineNumber);
+
+        void exceptionUnwind(ExecState* handlerCallFrame);
 
         const Vector<RefPtr<ProfileGenerator> >& currentProfiles() { return m_currentProfiles; };
 
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index d29ac7c..ff07299 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,14 @@
+2010-11-18  Gavin Barraclough  <barraclough at apple.com>
+
+        Reviewed by Oliver Hunt.
+
+        Bug 49635 - Profiler implementation is fragile
+
+        Fixes previously failing tests - output was incorrect, showing duplicate entries
+        for '(program) (no file) (line 1)'.
+
+        * fast/profiler/throw-exception-from-eval-expected.txt:
+
 2010-11-18  Kent Tamura  <tkent at chromium.org>
 
         Unreviewed, test expectation update.
diff --git a/LayoutTests/fast/profiler/throw-exception-from-eval-expected.txt b/LayoutTests/fast/profiler/throw-exception-from-eval-expected.txt
index 01944a8..847517a 100644
--- a/LayoutTests/fast/profiler/throw-exception-from-eval-expected.txt
+++ b/LayoutTests/fast/profiler/throw-exception-from-eval-expected.txt
@@ -5,20 +5,19 @@ To run this test manually, load it in the browser then load the WebInspector and
 
 Profile title: Throw within an eval.
 Thread_1 (no file) (line 0)
-   (program) throw-exception-from-eval.html (line 10)
-      (program) throw-exception-from-eval.html (line 19)
+   (program) throw-exception-from-eval.html (line 19)
+      (program) throw-exception-from-eval.html (line 10)
          (program) throw-exception-from-eval.html (line 4)
          (program) throw-exception-from-eval.html (line 19)
             eval (no file) (line 0)
                (program) (no file) (line 1)
-                  (program) (no file) (line 1)
-      onload throw-exception-from-eval.html (line 24)
-         startTest throw-exception-from-eval.html (line 13)
-            insertNewText profiler-test-JS-resources.js (line 17)
-               createElement (no file) (line 0)
-               createTextNode (no file) (line 0)
-               appendChild (no file) (line 0)
-               getElementById (no file) (line 0)
-            endTest profiler-test-JS-resources.js (line 1)
+   onload throw-exception-from-eval.html (line 24)
+      startTest throw-exception-from-eval.html (line 13)
+         insertNewText profiler-test-JS-resources.js (line 17)
+            createElement (no file) (line 0)
+            createTextNode (no file) (line 0)
+            appendChild (no file) (line 0)
+            getElementById (no file) (line 0)
+         endTest profiler-test-JS-resources.js (line 1)
 
 

-- 
WebKit Debian packaging



More information about the Pkg-webkit-commits mailing list