Changeset 162598 in webkit


Ignore:
Timestamp:
Jan 22, 2014 11:39:58 PM (10 years ago)
Author:
mark.lam@apple.com
Message:

Poor man's fast breakpoints for a 2.3x debugger speedup.
<https://webkit.org/b/122836>

Reviewed by Geoffrey Garen.

Previously we gained back some performance (run at baseline JIT speeds)
when the WebInspector is opened provided no breakpoints are set. This
was achieved by simply skipping all op_debug callbacks to the debugger
if no breakpoints are set. If any breakpoints are set, the debugger will
set a m_needsOpDebugCallbacks flag which causes the callbacks to be
called, and we don't get the baseline JIT speeds anymore.

With this patch, we will now track the number of breakpoints set in the
CodeBlock that they are set in. The LLINT and baseline JIT code will
check CodeBlock::m_numBreakpoints to determine if the op_debug callbacks
need to be called. With this, we will only enable op_debug callbacks for
CodeBlocks that need it i.e. those with breakpoints set in them.

Debugger::m_needsOpDebugCallbacks is now obsoleted. The LLINT and baseline
JIT code still needs to check Debugger::m_shouldPause to determine if the
debugger is in stepping mode and hence, needs op_debug callbacks enabled
for everything until the debugger "continues" the run and exit stepping
mode.

Also in this patch, I fixed a regression in DOM breakpoints which relies
Debugger::breakProgram() to pause the debugger.

  • bytecode/CodeBlock.cpp:

(JSC::CodeBlock::dumpBytecode):

  • Missed accounting for op_debug's new hasBreakpointFlag operand here when it was added.

(JSC::CodeBlock::CodeBlock):
(JSC::CodeBlock::hasOpDebugForLineAndColumn):

  • This is needed in Debugger::toggleBreakpoint() to determine if a breakpoint falls within a CodeBlock or not. Simply checking the bounds of the CodeBlock is insufficient. For example, let's say we have the following JS code:

begin global scope
function f1() {

function f2() {

... set breakpoint here.

}

}
end global scope

Using the CodeBlock bounds alone, the breakpoint above will to appear
to be in the global program CodeBlock, and the CodeBlocks for function
f1() and f2(). With CodeBlock::hasOpDebugForLineAndColumn() we can
rule out the global program CodeBlock and f1(), and only apply the
breakpoint to f2(0 where it belongs.

CodeBlock::hasOpDebugForLineAndColumn() works by iterating over all
the opcodes in the CodeBlock to look for op_debug's. For each op_debug,
it calls CodeBlock::expressionRangeForBytecodeOffset() to do a binary
seach to get the line and column info for that op_debug. This is a
N * log(N) algorithm. However, a quick hands on test using the
WebInspector (with this patch applied) to exercise setting, breaking
on, and clearing breakpoints, as well as stepping through some code
shows no noticeable degradation of the user experience compared to the
baseline without this patch.

  • bytecode/CodeBlock.h:

(JSC::CodeBlock::numBreakpoints):
(JSC::CodeBlock::numBreakpointsOffset):
(JSC::CodeBlock::addBreakpoint):
(JSC::CodeBlock::removeBreakpoint):
(JSC::CodeBlock::clearAllBreakpoints):

  • debugger/Breakpoint.h:
  • defined Breakpoint::unspecifiedColumn so that we can explicitly indicate when the WebInspector was setting a line breakpoint and did not provide a column value. CodeBlock::hasOpDebugForLineAndColumn() needs this information in order to loosen its matching criteria for op_debug bytecodes for the specified breakpoint line and column values provided by the debugger.

Previously, we just hijack a 0 value column as an unspecified column.
However, the WebInspector operates on 0-based ints for column values.
Hence, 0 should be a valid column value and should not be hijacked to
mean an unspecified column.

  • debugger/Debugger.cpp:

(JSC::Debugger::Debugger):

  • added tracking of the VM that the debugger is used with. This is needed by Debugger::breakProgram().

The VM pointer is attained from the first JSGlobalObject that the debugger
attaches to. When the debugger detaches from the last JSGlobalObject, it
will nullify its VM pointer to allow a new one to be set on the next
attach.

We were always only using each debugger instance with one VM. This change
makes it explicit with an assert to ensure that all globalObjects that
the debugger attaches to beongs to the same VM.

(JSC::Debugger::attach):
(JSC::Debugger::detach):
(JSC::Debugger::setShouldPause):

(JSC::Debugger::registerCodeBlock):
(JSC::Debugger::unregisterCodeBlock):

  • registerCodeBlock() is responsible for applying pre-existing breakpoints to new CodeBlocks being installed. Similarly, unregisterCodeBlock() clears the breakpoints.

(JSC::Debugger::toggleBreakpoint):

  • This is the workhorse function that checks if a breakpoint falls within a CodeBlock or not. If it does, then it can either enable or disable said breakpoint in the CodeBlock. In the current implementation, enabling/disabling the breakpoint simply means incrementing/decrementing the CodeBlock's m_numBreakpoints.

(JSC::Debugger::applyBreakpoints):

(JSC::Debugger::ToggleBreakpointFunctor::ToggleBreakpointFunctor):
(JSC::Debugger::ToggleBreakpointFunctor::operator()):
(JSC::Debugger::toggleBreakpoint):

  • Iterates all relevant CodeBlocks and apply the specified breakpoint if appropriate. This is called when a new breakpoint is being defined by the WebInspector and needs to be applied to an already installed CodeBlock.

(JSC::Debugger::setBreakpoint):
(JSC::Debugger::removeBreakpoint):
(JSC::Debugger::hasBreakpoint):
(JSC::Debugger::ClearBreakpointsFunctor::ClearBreakpointsFunctor):
(JSC::Debugger::ClearBreakpointsFunctor::operator()):
(JSC::Debugger::clearBreakpoints):

(JSC::Debugger::breakProgram):

  • Fixed a regression that broke DOM breakpoints. The issue is that with the skipping of op_debug callbacks, we don't always have an updated m_currentCallFrame. Normally, m_currentCallFrame is provided as arg in the op_debug callback. In this case, we can get the CallFrame* from m_vm->topCallFrame.

(JSC::Debugger::updateCallFrameAndPauseIfNeeded):
(JSC::Debugger::pauseIfNeeded):
(JSC::Debugger::willExecuteProgram):

  • debugger/Debugger.h:

(JSC::Debugger::Debugger):
(JSC::Debugger::shouldPause):

  • heap/CodeBlockSet.h:

(JSC::CodeBlockSet::iterate):

  • heap/Heap.h:

(JSC::Heap::forEachCodeBlock):

  • Added utility to iterate all CodeBlocks in the heap / VM.
  • interpreter/Interpreter.cpp:

(JSC::Interpreter::debug):

  • jit/JITOpcodes.cpp:

(JSC::JIT::emit_op_debug):

  • jit/JITOpcodes32_64.cpp:

(JSC::JIT::emit_op_debug):

  • llint/LowLevelInterpreter.asm:
  • These now checks CodeBlock::m_numBreakpoints and Debugger::m_shouldPause instead of Debugger::m_needsOpDebugCallbacks.
  • runtime/Executable.cpp:

(JSC::ScriptExecutable::installCode):

Location:
trunk/Source/JavaScriptCore
Files:
13 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r162579 r162598  
     12014-01-22  Mark Lam  <mark.lam@apple.com>
     2
     3        Poor man's fast breakpoints for a 2.3x debugger speedup.
     4        <https://webkit.org/b/122836>
     5
     6        Reviewed by Geoffrey Garen.
     7
     8        Previously we gained back some performance (run at baseline JIT speeds)
     9        when the WebInspector is opened provided no breakpoints are set. This
     10        was achieved by simply skipping all op_debug callbacks to the debugger
     11        if no breakpoints are set. If any breakpoints are set, the debugger will
     12        set a m_needsOpDebugCallbacks flag which causes the callbacks to be
     13        called, and we don't get the baseline JIT speeds anymore.
     14
     15        With this patch, we will now track the number of breakpoints set in the
     16        CodeBlock that they are set in. The LLINT and baseline JIT code will
     17        check CodeBlock::m_numBreakpoints to determine if the op_debug callbacks
     18        need to be called. With this, we will only enable op_debug callbacks for
     19        CodeBlocks that need it i.e. those with breakpoints set in them.
     20
     21        Debugger::m_needsOpDebugCallbacks is now obsoleted. The LLINT and baseline
     22        JIT code still needs to check Debugger::m_shouldPause to determine if the
     23        debugger is in stepping mode and hence, needs op_debug callbacks enabled
     24        for everything until the debugger "continues" the run and exit stepping
     25        mode.
     26
     27        Also in this patch, I fixed a regression in DOM breakpoints which relies
     28        Debugger::breakProgram() to pause the debugger.
     29
     30        * bytecode/CodeBlock.cpp:
     31        (JSC::CodeBlock::dumpBytecode):
     32        - Missed accounting for op_debug's new hasBreakpointFlag operand here when
     33          it was added.
     34        (JSC::CodeBlock::CodeBlock):
     35        (JSC::CodeBlock::hasOpDebugForLineAndColumn):
     36        - This is needed in Debugger::toggleBreakpoint() to determine if a
     37          breakpoint falls within a CodeBlock or not. Simply checking the bounds
     38          of the CodeBlock is insufficient. For example, let's say we have the
     39          following JS code:
     40
     41              // begin global scope
     42              function f1() {
     43                  function f2() {
     44                     ... // set breakpoint here.
     45                  }
     46              }
     47              // end global scope
     48
     49          Using the CodeBlock bounds alone, the breakpoint above will to appear
     50          to be in the global program CodeBlock, and the CodeBlocks for function
     51          f1() and f2(). With CodeBlock::hasOpDebugForLineAndColumn() we can
     52          rule out the global program CodeBlock and f1(), and only apply the
     53          breakpoint to f2(0 where it belongs.
     54
     55          CodeBlock::hasOpDebugForLineAndColumn() works by iterating over all
     56          the opcodes in the CodeBlock to look for op_debug's. For each op_debug,
     57          it calls CodeBlock::expressionRangeForBytecodeOffset() to do a binary
     58          seach to get the line and column info for that op_debug. This is a
     59          N * log(N) algorithm. However, a quick hands on test using the
     60          WebInspector (with this patch applied) to exercise setting, breaking
     61          on, and clearing breakpoints, as well as stepping through some code
     62          shows no noticeable degradation of the user experience compared to the
     63          baseline without this patch.
     64
     65        * bytecode/CodeBlock.h:
     66        (JSC::CodeBlock::numBreakpoints):
     67        (JSC::CodeBlock::numBreakpointsOffset):
     68        (JSC::CodeBlock::addBreakpoint):
     69        (JSC::CodeBlock::removeBreakpoint):
     70        (JSC::CodeBlock::clearAllBreakpoints):
     71        * debugger/Breakpoint.h:
     72        - defined Breakpoint::unspecifiedColumn so that we can explicitly indicate
     73          when the WebInspector was setting a line breakpoint and did not provide
     74          a column value. CodeBlock::hasOpDebugForLineAndColumn() needs this
     75          information in order to loosen its matching criteria for op_debug
     76          bytecodes for the specified breakpoint line and column values provided
     77          by the debugger.
     78
     79          Previously, we just hijack a 0 value column as an unspecified column.
     80          However, the WebInspector operates on 0-based ints for column values.
     81          Hence, 0 should be a valid column value and should not be hijacked to
     82          mean an unspecified column.
     83
     84        * debugger/Debugger.cpp:
     85        (JSC::Debugger::Debugger):
     86        - added tracking of the VM that the debugger is used with. This is
     87          needed by Debugger::breakProgram().
     88
     89          The VM pointer is attained from the first JSGlobalObject that the debugger
     90          attaches to. When the debugger detaches from the last JSGlobalObject, it
     91          will nullify its VM pointer to allow a new one to be set on the next
     92          attach.
     93
     94          We were always only using each debugger instance with one VM. This change
     95          makes it explicit with an assert to ensure that all globalObjects that
     96          the debugger attaches to beongs to the same VM.
     97
     98        (JSC::Debugger::attach):
     99        (JSC::Debugger::detach):
     100        (JSC::Debugger::setShouldPause):
     101
     102        (JSC::Debugger::registerCodeBlock):
     103        (JSC::Debugger::unregisterCodeBlock):
     104        - registerCodeBlock() is responsible for applying pre-existing breakpoints
     105          to new CodeBlocks being installed. Similarly, unregisterCodeBlock()
     106          clears the breakpoints.
     107
     108        (JSC::Debugger::toggleBreakpoint):
     109        - This is the workhorse function that checks if a breakpoint falls within
     110          a CodeBlock or not. If it does, then it can either enable or disable
     111          said breakpoint in the CodeBlock. In the current implementation,
     112          enabling/disabling the breakpoint simply means incrementing/decrementing
     113          the CodeBlock's m_numBreakpoints.
     114
     115        (JSC::Debugger::applyBreakpoints):
     116
     117        (JSC::Debugger::ToggleBreakpointFunctor::ToggleBreakpointFunctor):
     118        (JSC::Debugger::ToggleBreakpointFunctor::operator()):
     119        (JSC::Debugger::toggleBreakpoint):
     120        - Iterates all relevant CodeBlocks and apply the specified breakpoint
     121          if appropriate. This is called when a new breakpoint is being defined
     122          by the WebInspector and needs to be applied to an already installed
     123          CodeBlock.
     124
     125        (JSC::Debugger::setBreakpoint):
     126        (JSC::Debugger::removeBreakpoint):
     127        (JSC::Debugger::hasBreakpoint):
     128        (JSC::Debugger::ClearBreakpointsFunctor::ClearBreakpointsFunctor):
     129        (JSC::Debugger::ClearBreakpointsFunctor::operator()):
     130        (JSC::Debugger::clearBreakpoints):
     131
     132        (JSC::Debugger::breakProgram):
     133        - Fixed a regression that broke DOM breakpoints. The issue is that with
     134          the skipping of op_debug callbacks, we don't always have an updated
     135          m_currentCallFrame. Normally, m_currentCallFrame is provided as arg
     136          in the op_debug callback. In this case, we can get the CallFrame* from
     137          m_vm->topCallFrame.
     138
     139        (JSC::Debugger::updateCallFrameAndPauseIfNeeded):
     140        (JSC::Debugger::pauseIfNeeded):
     141        (JSC::Debugger::willExecuteProgram):
     142        * debugger/Debugger.h:
     143        (JSC::Debugger::Debugger):
     144        (JSC::Debugger::shouldPause):
     145
     146        * heap/CodeBlockSet.h:
     147        (JSC::CodeBlockSet::iterate):
     148        * heap/Heap.h:
     149        (JSC::Heap::forEachCodeBlock):
     150        - Added utility to iterate all CodeBlocks in the heap / VM.
     151
     152        * interpreter/Interpreter.cpp:
     153        (JSC::Interpreter::debug):
     154
     155        * jit/JITOpcodes.cpp:
     156        (JSC::JIT::emit_op_debug):
     157        * jit/JITOpcodes32_64.cpp:
     158        (JSC::JIT::emit_op_debug):
     159        * llint/LowLevelInterpreter.asm:
     160        - These now checks CodeBlock::m_numBreakpoints and Debugger::m_shouldPause
     161          instead of Debugger::m_needsOpDebugCallbacks.
     162
     163        * runtime/Executable.cpp:
     164        (JSC::ScriptExecutable::installCode):
     165
    11662014-01-22  Myles C. Maxfield  <mmaxfield@apple.com>
    2167
  • trunk/Source/JavaScriptCore/bytecode/CodeBlock.cpp

    r162390 r162598  
    13531353        case op_debug: {
    13541354            int debugHookID = (++it)->u.operand;
     1355            int hasBreakpointFlag = (++it)->u.operand;
    13551356            printLocationAndOp(out, exec, location, it, "debug");
    1356             out.printf("%s", debugHookName(debugHookID));
     1357            out.printf("%s %d", debugHookName(debugHookID), hasBreakpointFlag);
    13571358            break;
    13581359        }
     
    14691470    , m_didFailFTLCompilation(false)
    14701471    , m_unlinkedCode(*other.m_vm, other.m_ownerExecutable.get(), other.m_unlinkedCode.get())
     1472    , m_numBreakpoints(0)
    14711473    , m_ownerExecutable(*other.m_vm, other.m_ownerExecutable.get(), other.m_ownerExecutable.get())
    14721474    , m_vm(other.m_vm)
     
    15231525    , m_didFailFTLCompilation(false)
    15241526    , m_unlinkedCode(m_globalObject->vm(), ownerExecutable, unlinkedCodeBlock)
     1527    , m_numBreakpoints(0)
    15251528    , m_ownerExecutable(m_globalObject->vm(), ownerExecutable, ownerExecutable)
    15261529    , m_vm(unlinkedCodeBlock->vm())
     
    25672570}
    25682571
     2572bool CodeBlock::hasOpDebugForLineAndColumn(unsigned line, unsigned column)
     2573{
     2574    Interpreter* interpreter = vm()->interpreter;
     2575    const Instruction* begin = instructions().begin();
     2576    const Instruction* end = instructions().end();
     2577    for (const Instruction* it = begin; it != end;) {
     2578        OpcodeID opcodeID = interpreter->getOpcodeID(it->u.opcode);
     2579        if (opcodeID == op_debug) {
     2580            unsigned bytecodeOffset = it - begin;
     2581            int unused;
     2582            unsigned opDebugLine;
     2583            unsigned opDebugColumn;
     2584            expressionRangeForBytecodeOffset(bytecodeOffset, unused, unused, unused, opDebugLine, opDebugColumn);
     2585            if (line == opDebugLine && (column == Breakpoint::unspecifiedColumn || column == opDebugColumn))
     2586                return true;
     2587        }
     2588        it += opcodeLengths[opcodeID];
     2589    }
     2590    return false;
     2591}
     2592
    25692593void CodeBlock::shrinkToFit(ShrinkMode shrinkMode)
    25702594{
  • trunk/Source/JavaScriptCore/bytecode/CodeBlock.h

    r162389 r162598  
    870870    unsigned frameRegisterCount();
    871871
     872    bool hasOpDebugForLineAndColumn(unsigned line, unsigned column);
     873
     874    int numBreakpoints() const { return m_numBreakpoints; }
     875    static ptrdiff_t numBreakpointsOffset() { return OBJECT_OFFSETOF(CodeBlock, m_numBreakpoints); }
     876
     877    void addBreakpoint(int numBreakpoints) { m_numBreakpoints += numBreakpoints; }
     878    void removeBreakpoint(int numBreakpoints)
     879    {
     880        m_numBreakpoints -= numBreakpoints;
     881        ASSERT(m_numBreakpoints >= 0);
     882    }
     883    void clearAllBreakpoints() { m_numBreakpoints = 0; }
     884
    872885    // FIXME: Make these remaining members private.
    873886
     
    10061019    WriteBarrier<UnlinkedCodeBlock> m_unlinkedCode;
    10071020    int m_numParameters;
     1021    int m_numBreakpoints;
    10081022    WriteBarrier<ScriptExecutable> m_ownerExecutable;
    10091023    VM* m_vm;
  • trunk/Source/JavaScriptCore/debugger/Breakpoint.h

    r158937 r162598  
    6060    String condition;
    6161    bool autoContinue;
     62
     63    static const unsigned unspecifiedColumn = UINT_MAX;
    6264};
    6365
  • trunk/Source/JavaScriptCore/debugger/Debugger.cpp

    r160082 r162598  
    11/*
    2  *  Copyright (C) 2008, 2013 Apple Inc. All rights reserved.
     2 *  Copyright (C) 2008, 2013, 2014 Apple Inc. All rights reserved.
    33 *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
    44 *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
     
    2525#include "Debugger.h"
    2626
     27#include "CodeBlock.h"
    2728#include "DebuggerCallFrame.h"
    2829#include "Error.h"
     30
    2931#include "HeapIterationScope.h"
    3032#include "Interpreter.h"
     
    140142
    141143Debugger::Debugger(bool isInWorkerThread)
    142     : m_pauseOnExceptionsState(DontPauseOnExceptions)
     144    : m_vm(nullptr)
     145    , m_pauseOnExceptionsState(DontPauseOnExceptions)
    143146    , m_pauseOnNextStatement(false)
    144147    , m_isPaused(false)
     
    152155    , m_lastExecutedSourceID(noSourceID)
    153156    , m_topBreakpointID(noBreakpointID)
    154     , m_needsOpDebugCallbacks(false)
    155157    , m_shouldPause(false)
    156158{
     
    167169{
    168170    ASSERT(!globalObject->debugger());
     171    if (!m_vm)
     172        m_vm = &globalObject->vm();
     173    else
     174        ASSERT(m_vm == &globalObject->vm());
    169175    globalObject->setDebugger(this);
    170176    m_globalObjects.add(globalObject);
     
    185191    m_globalObjects.remove(globalObject);
    186192    globalObject->setDebugger(0);
     193    if (!m_globalObjects.size())
     194        m_vm = nullptr;
    187195}
    188196
     
    190198{
    191199    m_shouldPause = value;
    192     updateNeedForOpDebugCallbacks();
     200}
     201
     202void Debugger::registerCodeBlock(CodeBlock* codeBlock)
     203{
     204    applyBreakpoints(codeBlock);
     205}
     206
     207void Debugger::unregisterCodeBlock(CodeBlock* codeBlock)
     208{
     209    codeBlock->clearAllBreakpoints();
     210}
     211
     212void Debugger::toggleBreakpoint(CodeBlock* codeBlock, Breakpoint& breakpoint, BreakpointState enabledOrNot)
     213{
     214    ASSERT(codeBlock->jitCode()->jitType() == JITCode::InterpreterThunk
     215        || codeBlock->jitCode()->jitType() == JITCode::BaselineJIT);
     216
     217    ScriptExecutable* executable = codeBlock->ownerExecutable();
     218
     219    SourceID sourceID = static_cast<SourceID>(executable->sourceID());
     220    if (breakpoint.sourceID != sourceID)
     221        return;
     222
     223    unsigned line = breakpoint.line;
     224    unsigned column = breakpoint.column;
     225
     226    unsigned startLine = executable->lineNo();
     227    unsigned startColumn = executable->startColumn();
     228    unsigned endLine = executable->lastLine();
     229    unsigned endColumn = executable->endColumn();
     230
     231    // Inspector breakpoint line and column values are zero-based but the executable
     232    // and CodeBlock line and column values are one-based.
     233    line += 1;
     234    column = column ? column + 1 : Breakpoint::unspecifiedColumn;
     235
     236    if (line < startLine || line > endLine)
     237        return;
     238    if (column != Breakpoint::unspecifiedColumn) {
     239        if (line == startLine && column < startColumn)
     240            return;
     241        if (line == endLine && column > endColumn)
     242            return;
     243    }
     244    if (!codeBlock->hasOpDebugForLineAndColumn(line, column))
     245        return;
     246
     247    if (enabledOrNot == BreakpointEnabled)
     248        codeBlock->addBreakpoint(1);
     249    else
     250        codeBlock->removeBreakpoint(1);
     251}
     252
     253void Debugger::applyBreakpoints(CodeBlock* codeBlock)
     254{
     255    BreakpointIDToBreakpointMap& breakpoints = m_breakpointIDToBreakpoint;
     256    for (auto it = breakpoints.begin(); it != breakpoints.end(); ++it) {
     257        Breakpoint& breakpoint = *it->value;
     258        toggleBreakpoint(codeBlock, breakpoint, BreakpointEnabled);
     259    }
     260}
     261
     262class Debugger::ToggleBreakpointFunctor {
     263public:
     264    ToggleBreakpointFunctor(Debugger* debugger, Breakpoint& breakpoint, BreakpointState enabledOrNot)
     265        : m_debugger(debugger)
     266        , m_breakpoint(breakpoint)
     267        , m_enabledOrNot(enabledOrNot)
     268    {
     269    }
     270
     271    bool operator()(CodeBlock* codeBlock)
     272    {
     273        if (m_debugger == codeBlock->globalObject()->debugger())
     274            m_debugger->toggleBreakpoint(codeBlock, m_breakpoint, m_enabledOrNot);
     275        return false;
     276    }
     277
     278private:
     279    Debugger* m_debugger;
     280    Breakpoint& m_breakpoint;
     281    BreakpointState m_enabledOrNot;
     282};
     283
     284void Debugger::toggleBreakpoint(Breakpoint& breakpoint, Debugger::BreakpointState enabledOrNot)
     285{
     286    if (!m_vm)
     287        return;
     288    HeapIterationScope iterationScope(m_vm->heap);
     289    ToggleBreakpointFunctor functor(this, breakpoint, enabledOrNot);
     290    m_vm->heap.forEachCodeBlock(functor);
    193291}
    194292
     
    206304    HeapIterationScope iterationScope(vm->heap);
    207305    vm->heap.objectSpace().forEachLiveCell(iterationScope, recompiler);
    208 }
    209 
    210 void Debugger::updateNeedForOpDebugCallbacks()
    211 {
    212     size_t numberOfBreakpoints = m_breakpointIDToBreakpoint.size();
    213     m_needsOpDebugCallbacks = m_shouldPause || numberOfBreakpoints;
    214306}
    215307
     
    248340    m_breakpointIDToBreakpoint.set(id, &breakpoints.last());
    249341
    250     updateNeedForOpDebugCallbacks();
     342    toggleBreakpoint(breakpoint, BreakpointEnabled);
    251343
    252344    return id;
     
    267359    LineToBreakpointsMap::iterator breaksIt = it->value.find(breakpoint.line);
    268360    ASSERT(breaksIt != it->value.end());
     361
     362    toggleBreakpoint(breakpoint, BreakpointDisabled);
    269363
    270364    BreakpointsInLine& breakpoints = breaksIt->value;
     
    283377        }
    284378    }
    285 
    286     updateNeedForOpDebugCallbacks();
    287379}
    288380
     
    311403        unsigned breakColumn = breakpoints[i].column;
    312404        // Since frontend truncates the indent, the first statement in a line must match the breakpoint (line,0).
     405        ASSERT(this == m_currentCallFrame->codeBlock()->globalObject()->debugger());
    313406        if ((line != m_lastExecutedLine && line == breakLine && !breakColumn)
    314407            || (line == breakLine && column == breakColumn)) {
     
    346439}
    347440
     441class Debugger::ClearBreakpointsFunctor {
     442public:
     443    ClearBreakpointsFunctor(Debugger* debugger)
     444        : m_debugger(debugger)
     445    {
     446    }
     447
     448    bool operator()(CodeBlock* codeBlock)
     449    {
     450        if (codeBlock->numBreakpoints() && m_debugger == codeBlock->globalObject()->debugger())
     451            codeBlock->clearAllBreakpoints();
     452        return false;
     453    }
     454
     455private:
     456    Debugger* m_debugger;
     457};
     458
    348459void Debugger::clearBreakpoints()
    349460{
     
    352463    m_sourceIDToBreakpoints.clear();
    353464
    354     updateNeedForOpDebugCallbacks();
     465    if (!m_vm)
     466        return;
     467    HeapIterationScope iterationScope(m_vm->heap);
     468    ClearBreakpointsFunctor functor(this);
     469    m_vm->heap.forEachCodeBlock(functor);
    355470}
    356471
     
    374489void Debugger::breakProgram()
    375490{
    376     if (m_isPaused || !m_currentCallFrame)
     491    if (m_isPaused)
    377492        return;
    378493
    379494    m_pauseOnNextStatement = true;
    380495    setShouldPause(true);
     496    m_currentCallFrame = m_vm->topCallFrame;
     497    ASSERT(m_currentCallFrame);
    381498    pauseIfNeeded(m_currentCallFrame);
    382499}
     
    433550    updateCallFrame(callFrame);
    434551    pauseIfNeeded(callFrame);
    435     if (!needsOpDebugCallbacks())
     552    if (!shouldPause())
    436553        m_currentCallFrame = 0;
    437554}
     
    478595    if (!m_pauseOnNextStatement && !m_pauseOnCallFrame) {
    479596        setShouldPause(false);
    480         if (!needsOpDebugCallbacks())
    481             m_currentCallFrame = 0;
     597        m_currentCallFrame = nullptr;
    482598    }
    483599}
     
    549665    if (!m_isInWorkerThread)
    550666        updateCallFrameAndPauseIfNeeded(callFrame);
    551     else if (needsOpDebugCallbacks())
     667    else if (shouldPause())
    552668        updateCallFrame(callFrame);
    553669}
  • trunk/Source/JavaScriptCore/debugger/Debugger.h

    r161353 r162598  
    22 *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
    33 *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
    4  *  Copyright (C) 2008, 2009, 2013 Apple Inc. All rights reserved.
     4 *  Copyright (C) 2008, 2009, 2013, 2014 Apple Inc. All rights reserved.
    55 *
    66 *  This library is free software; you can redistribute it and/or
     
    4949    virtual ~Debugger();
    5050
    51     bool needsOpDebugCallbacks() const { return m_needsOpDebugCallbacks; }
    52     static ptrdiff_t needsOpDebugCallbacksOffset() { return OBJECT_OFFSETOF(Debugger, m_needsOpDebugCallbacks); }
     51    bool shouldPause() const { return m_shouldPause; }
     52    static ptrdiff_t shouldPauseOffset() { return OBJECT_OFFSETOF(Debugger, m_shouldPause); }
    5353
    5454    JSC::DebuggerCallFrame* currentDebuggerCallFrame() const;
     
    105105    void recompileAllJSFunctions(VM*);
    106106
     107    void registerCodeBlock(CodeBlock*);
     108    void unregisterCodeBlock(CodeBlock*);
     109
    107110protected:
    108111    virtual bool needPauseHandling(JSGlobalObject*) { return false; }
     
    129132    typedef Vector<Breakpoint> BreakpointsInLine;
    130133    typedef HashMap<unsigned, BreakpointsInLine, WTF::IntHash<int>, WTF::UnsignedWithZeroKeyHashTraits<int>> LineToBreakpointsMap;
    131     typedef HashMap<SourceID, LineToBreakpointsMap> SourceIDToBreakpointsMap;
     134    typedef HashMap<SourceID, LineToBreakpointsMap, WTF::IntHash<SourceID>, WTF::UnsignedWithZeroKeyHashTraits<SourceID>> SourceIDToBreakpointsMap;
     135
     136    class ToggleBreakpointFunctor;
     137    class ClearBreakpointsFunctor;
    132138
    133139    class PauseReasonDeclaration {
     
    149155    bool hasBreakpoint(SourceID, const TextPosition&, Breakpoint* hitBreakpoint);
    150156
    151     bool shouldPause() const { return m_shouldPause; }
    152157    void setShouldPause(bool);
    153158    void updateNeedForOpDebugCallbacks();
     
    162167    void pauseIfNeeded(JSC::CallFrame*);
    163168
     169    enum BreakpointState {
     170        BreakpointDisabled,
     171        BreakpointEnabled
     172    };
     173    void toggleBreakpoint(CodeBlock*, Breakpoint&, BreakpointState);
     174    void applyBreakpoints(CodeBlock*);
     175    void toggleBreakpoint(Breakpoint&, BreakpointState);
     176
     177    VM* m_vm;
    164178    HashSet<JSGlobalObject*> m_globalObjects;
    165179
     
    182196    SourceIDToBreakpointsMap m_sourceIDToBreakpoints;
    183197
    184     bool m_needsOpDebugCallbacks;
    185198    bool m_shouldPause;
    186199
     
    196209class Debugger {
    197210public:
    198     Debugger(bool = false) : m_needsOpDebugCallbacks(false) { }
    199     bool needsOpDebugCallbacks() const { return false; }
     211    Debugger(bool = false)
     212        : m_shouldPause(false)
     213    {
     214    }
     215    bool shouldPause() const { return false; }
    200216    bool needsExceptionCallbacks() const { return false; }
    201217    void detach(JSGlobalObject*) { }
     
    209225    void didReachBreakpoint(CallFrame*) { }
    210226
    211     bool m_needsOpDebugCallbacks;
     227private:
     228    bool m_shouldPause;
    212229};
    213230
  • trunk/Source/JavaScriptCore/heap/CodeBlockSet.h

    r161615 r162598  
    7272    void rememberCurrentlyExecutingCodeBlocks(Heap*);
    7373
     74    // Visits each CodeBlock in the heap until the visitor function returns true
     75    // to indicate that it is done iterating, or until every CodeBlock has been
     76    // visited.
     77    template<typename Functor> void iterate(Functor& functor)
     78    {
     79        for (auto &codeBlock : m_set) {
     80            bool done = functor(codeBlock);
     81            if (done)
     82                break;
     83        }
     84    }
     85
    7486private:
    7587    // This is not a set of RefPtr<CodeBlock> because we need to be able to find
  • trunk/Source/JavaScriptCore/heap/Heap.h

    r162139 r162598  
    175175        template<typename Functor> typename Functor::ReturnType forEachProtectedCell(Functor&);
    176176        template<typename Functor> typename Functor::ReturnType forEachProtectedCell();
     177        template<typename Functor> inline void forEachCodeBlock(Functor&);
    177178
    178179        HandleSet* handleSet() { return &m_handleSet; }
     
    433434    }
    434435
     436    template<typename Functor> inline void Heap::forEachCodeBlock(Functor& functor)
     437    {
     438        return m_codeBlocks.iterate<Functor>(functor);
     439    }
     440
    435441    inline void* Heap::allocateWithNormalDestructor(size_t bytes)
    436442    {
  • trunk/Source/JavaScriptCore/interpreter/Interpreter.cpp

    r162156 r162598  
    12061206{
    12071207    Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger();
    1208     if (!debugger || !debugger->needsOpDebugCallbacks())
     1208    if (!debugger)
    12091209        return;
     1210    ASSERT(debugger->shouldPause() || callFrame->codeBlock()->numBreakpoints() || callFrame->hadException());
    12101211
    12111212    switch (debugHookID) {
  • trunk/Source/JavaScriptCore/jit/JITOpcodes.cpp

    r161364 r162598  
    716716#elif ENABLE(JAVASCRIPT_DEBUGGER)
    717717    JSGlobalObject* globalObject = codeBlock()->globalObject();
    718     Debugger* debugger = globalObject->debugger();
    719718    char* debuggerAddress = reinterpret_cast<char*>(globalObject) + JSGlobalObject::debuggerOffset();
    720719    Jump noDebugger = branchTestPtr(Zero, AbsoluteAddress(debuggerAddress));
    721     char* flagAddress = reinterpret_cast<char*>(debugger) + Debugger::needsOpDebugCallbacksOffset();
    722     Jump skipDebugHook = branchTest8(Zero, AbsoluteAddress(flagAddress));
     720
     721    Debugger* debugger = globalObject->debugger();
     722    char* shouldPauseAddress = reinterpret_cast<char*>(debugger) + Debugger::shouldPauseOffset();
     723    Jump callbackNeeded = branchTest8(NonZero, AbsoluteAddress(shouldPauseAddress));
     724
     725    char* numBreakpointsAddress = reinterpret_cast<char*>(codeBlock()) + CodeBlock::numBreakpointsOffset();
     726    load32(numBreakpointsAddress, regT0);
     727    Jump noBreakpointSet = branchTest32(Zero, regT0);
     728
     729    callbackNeeded.link(this);
    723730    callOperation(operationDebug, currentInstruction[1].u.operand);
    724     skipDebugHook.link(this);
     731
     732    noBreakpointSet.link(this);
    725733    noDebugger.link(this);
    726734#else
  • trunk/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp

    r161364 r162598  
    998998#elif ENABLE(JAVASCRIPT_DEBUGGER)
    999999    JSGlobalObject* globalObject = codeBlock()->globalObject();
    1000     Debugger* debugger = globalObject->debugger();
    10011000    char* debuggerAddress = reinterpret_cast<char*>(globalObject) + JSGlobalObject::debuggerOffset();
    10021001    loadPtr(debuggerAddress, regT0);
    10031002    Jump noDebugger = branchTestPtr(Zero, regT0);
    1004     char* flagAddress = reinterpret_cast<char*>(debugger) + Debugger::needsOpDebugCallbacksOffset();
    1005     Jump skipDebugHook = branchTest8(Zero, AbsoluteAddress(flagAddress));
     1003
     1004    Debugger* debugger = globalObject->debugger();
     1005    char* shouldPauseAddress = reinterpret_cast<char*>(debugger) + Debugger::shouldPauseOffset();
     1006    Jump callbackNeeded = branchTest8(NonZero, AbsoluteAddress(shouldPauseAddress));
     1007
     1008    char* numBreakpointsAddress = reinterpret_cast<char*>(codeBlock()) + CodeBlock::numBreakpointsOffset();
     1009    load32(numBreakpointsAddress, regT0);
     1010    Jump noBreakpointSet = branchTest32(Zero, regT0);
     1011
     1012    callbackNeeded.link(this);
    10061013    callOperation(operationDebug, currentInstruction[1].u.operand);
    1007     skipDebugHook.link(this);
     1014
     1015    noBreakpointSet.link(this);
    10081016    noDebugger.link(this);
    10091017#else
  • trunk/Source/JavaScriptCore/llint/LowLevelInterpreter.asm

    r162270 r162598  
    836836_llint_op_debug:
    837837    traceExecution()
    838     loadp CodeBlock[cfr], t0
    839     loadp CodeBlock::m_globalObject[t0], t0
     838    loadp CodeBlock[cfr], t1
     839    loadp CodeBlock::m_globalObject[t1], t0
    840840    loadp JSGlobalObject::m_debugger[t0], t0
    841841    btiz t0, .opDebugDone
    842     loadb Debugger::m_needsOpDebugCallbacks[t0], t0
    843     btbz t0, .opDebugDone
    844 
     842
     843    loadb Debugger::m_shouldPause[t0], t0
     844    btbnz t0, .opDebugDoCallback
     845
     846    loadi CodeBlock::m_numBreakpoints[t1], t0
     847    btiz t0, .opDebugDone
     848
     849.opDebugDoCallback:
    845850    callSlowPath(_llint_slow_path_debug)
    846851.opDebugDone:                   
  • trunk/Source/JavaScriptCore/runtime/Executable.cpp

    r159825 r162598  
    173173    if (oldCodeBlock)
    174174        oldCodeBlock->unlinkIncomingCalls();
     175
     176    Debugger* debugger = genericCodeBlock->globalObject()->debugger();
     177    if (debugger)
     178        debugger->registerCodeBlock(genericCodeBlock);
    175179}
    176180
Note: See TracChangeset for help on using the changeset viewer.