Ignore:
Timestamp:
Nov 18, 2010, 4:54:26 PM (15 years ago)
Author:
[email protected]
Message:

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:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/profiler/ProfileGenerator.cpp

    r72176 r72351  
    6464
    6565    exec->interpreter()->retrieveLastCaller(exec, lineNumber, sourceID, sourceURL, function);
    66     m_currentNode = ProfileNode::create(Profiler::createCallIdentifier(exec, function ? function.toThisObject(exec) : 0, sourceURL, lineNumber), m_head.get(), m_head.get());
     66    m_currentNode = ProfileNode::create(exec, Profiler::createCallIdentifier(exec, function ? function.toThisObject(exec) : 0, sourceURL, lineNumber), m_head.get(), m_head.get());
    6767    m_head->insertNode(m_currentNode.get());
    6868}
     
    7373}
    7474
    75 void ProfileGenerator::willExecute(const CallIdentifier& callIdentifier)
     75void ProfileGenerator::willExecute(ExecState* callerCallFrame, const CallIdentifier& callIdentifier)
    7676{
    7777    if (JAVASCRIPTCORE_PROFILE_WILL_EXECUTE_ENABLED()) {
     
    8484        return;
    8585
    86     ASSERT_ARG(m_currentNode, m_currentNode);
    87     m_currentNode = m_currentNode->willExecute(callIdentifier);
     86    ASSERT(m_currentNode);
     87    m_currentNode = m_currentNode->willExecute(callerCallFrame, callIdentifier);
    8888}
    8989
    90 void ProfileGenerator::didExecute(const CallIdentifier& callIdentifier)
     90void ProfileGenerator::didExecute(ExecState* callerCallFrame, const CallIdentifier& callIdentifier)
    9191{
    9292    if (JAVASCRIPTCORE_PROFILE_DID_EXECUTE_ENABLED()) {
     
    9999        return;
    100100
    101     ASSERT_ARG(m_currentNode, m_currentNode);
     101    ASSERT(m_currentNode);
    102102    if (m_currentNode->callIdentifier() != callIdentifier) {
    103         RefPtr<ProfileNode> returningNode = ProfileNode::create(callIdentifier, m_head.get(), m_currentNode.get());
     103        RefPtr<ProfileNode> returningNode = ProfileNode::create(callerCallFrame, callIdentifier, m_head.get(), m_currentNode.get());
    104104        returningNode->setStartTime(m_currentNode->startTime());
    105105        returningNode->didExecute();
     
    111111}
    112112
     113void ProfileGenerator::exceptionUnwind(ExecState* handlerCallFrame, const CallIdentifier&)
     114{
     115    // If the current node was called by the handler (==) or any
     116    // more nested function (>) the we have exited early from it.
     117    ASSERT(m_currentNode);
     118    while (m_currentNode->callerCallFrame() >= handlerCallFrame) {
     119        didExecute(m_currentNode->callerCallFrame(), m_currentNode->callIdentifier());
     120        ASSERT(m_currentNode);
     121    }
     122}
     123
    113124void ProfileGenerator::stopProfiling()
    114125{
     
    118129    removeProfileEnd();
    119130
    120     ASSERT_ARG(m_currentNode, m_currentNode);
     131    ASSERT(m_currentNode);
    121132
    122133    // Set the current node to the parent, because we are in a call that
     
    125136
    126137   if (double headSelfTime = m_head->selfTime()) {
    127         RefPtr<ProfileNode> idleNode = ProfileNode::create(CallIdentifier(NonJSExecution, UString(), 0), m_head.get(), m_head.get());
     138        RefPtr<ProfileNode> idleNode = ProfileNode::create(0, CallIdentifier(NonJSExecution, UString(), 0), m_head.get(), m_head.get());
    128139
    129140        idleNode->setTotalTime(headSelfTime);
Note: See TracChangeset for help on using the changeset viewer.