Ignore:
Timestamp:
Nov 8, 2013, 12:03:50 PM (12 years ago)
Author:
[email protected]
Message:

Move breakpoint (and exception break) functionality into JSC::Debugger.
https://bugs.webkit.org/show_bug.cgi?id=121796.

Reviewed by Geoffrey Garen.

Source/JavaScriptCore:

  • In ScriptDebugServer and JSC::Debugger, SourceID and BreakpointID are now numeric tokens.
  • JSC::Debugger now tracks user defined breakpoints in a JSC::Breakpoint record. Previously, this info is tracked in the ScriptBreakpoint record in ScriptDebugServer. The only element of ScriptBreakpoint that is not being tracked by JSC::Breakpoint is the ScriptBreakpointAction.

The ScriptBreakpointAction is still tracked by the ScriptDebugServer

in a list keyed on the corresponding BreakpointID.

The ScriptBreakpoint record is now only used as a means of passing

breakpoint paramaters to the ScriptDebugServer.

  • ScriptDebugServer now no longer accesses the JSC::CallFrame* directly. It always goes through the DebuggerCallFrame.

(JSC::Breakpoint::Breakpoint):

  • Breakpoint class to track info for each breakpoint in JSC::Debugger.
  • debugger/Debugger.cpp:

(JSC::DebuggerCallFrameScope::DebuggerCallFrameScope):
(JSC::DebuggerCallFrameScope::~DebuggerCallFrameScope):
(JSC::Debugger::Debugger):
(JSC::Debugger::detach):
(JSC::Debugger::updateNeedForOpDebugCallbacks):
(JSC::Debugger::setBreakpoint):
(JSC::Debugger::removeBreakpoint):
(JSC::Debugger::hasBreakpoint):
(JSC::Debugger::clearBreakpoints):
(JSC::Debugger::setBreakpointsActivated):
(JSC::Debugger::setPauseOnExceptionsState):
(JSC::Debugger::setPauseOnNextStatement):
(JSC::Debugger::breakProgram):
(JSC::Debugger::continueProgram):
(JSC::Debugger::stepIntoStatement):
(JSC::Debugger::stepOverStatement):
(JSC::Debugger::stepOutOfFunction):
(JSC::Debugger::updateCallFrame):
(JSC::Debugger::updateCallFrameAndPauseIfNeeded):
(JSC::Debugger::pauseIfNeeded):
(JSC::Debugger::exception):
(JSC::Debugger::atStatement):
(JSC::Debugger::callEvent):
(JSC::Debugger::returnEvent):
(JSC::Debugger::willExecuteProgram):
(JSC::Debugger::didExecuteProgram):
(JSC::Debugger::didReachBreakpoint):
(JSC::Debugger::currentDebuggerCallFrame):

  • debugger/Debugger.h:
  • debugger/DebuggerCallFrame.cpp:

(JSC::DebuggerCallFrame::sourceID):
(JSC::DebuggerCallFrame::sourceIDForCallFrame):

  • debugger/DebuggerCallFrame.h:
  • debugger/DebuggerPrimitives.h: Added.
  • define SourceID, noSourceID, BreakpointID, and noBreakpointID.

Source/WebCore:

No new tests.

  • In ScriptDebugServer and JSC::Debugger, SourceID and BreakpointID are now numeric tokens.
  • JSC::Debugger now tracks user defined breakpoints in a JSC::Breakpoint record. Previously, this info is tracked in the ScriptBreakpoint record in ScriptDebugServer. The only element of ScriptBreakpoint that is not being tracked by JSC::Breakpoint is the ScriptBreakpointAction.

The ScriptBreakpointAction is still tracked by the ScriptDebugServer

in a list keyed on the corresponding BreakpointID.

The ScriptBreakpoint record is now only used as a means of passing

breakpoint paramaters to the ScriptDebugServer.

  • ScriptDebugServer now no longer accesses the JSC::CallFrame* directly. It always goes through the DebuggerCallFrame.
  • GNUmakefile.list.am:
  • WebCore.vcxproj/WebCore.vcxproj:
  • WebCore.vcxproj/WebCore.vcxproj.filters:
  • WebCore.xcodeproj/project.pbxproj:
  • bindings/js/BreakpointID.h: Added.
  • bindings/js/ScriptDebugServer.cpp:

(WebCore::ScriptDebugServer::ScriptDebugServer):
(WebCore::ScriptDebugServer::setBreakpoint):
(WebCore::ScriptDebugServer::removeBreakpoint):
(WebCore::ScriptDebugServer::clearBreakpoints):
(WebCore::ScriptDebugServer::dispatchDidPause):
(WebCore::ScriptDebugServer::dispatchDidContinue):
(WebCore::ScriptDebugServer::dispatchDidParseSource):
(WebCore::ScriptDebugServer::notifyDoneProcessingDebuggerEvents):
(WebCore::ScriptDebugServer::needPauseHandling):
(WebCore::ScriptDebugServer::handleBreakpointHit):
(WebCore::ScriptDebugServer::handleExceptionInBreakpointCondition):
(WebCore::ScriptDebugServer::handlePause):

  • bindings/js/ScriptDebugServer.h:
  • bindings/js/SourceID.h: Added.
  • bindings/js/WorkerScriptDebugServer.cpp:

(WebCore::WorkerScriptDebugServer::WorkerScriptDebugServer):

  • bindings/js/WorkerScriptDebugServer.h:
  • inspector/InspectorDebuggerAgent.cpp:

(WebCore::InspectorDebuggerAgent::InspectorDebuggerAgent):
(WebCore::parseLocation):
(WebCore::InspectorDebuggerAgent::setBreakpoint):
(WebCore::InspectorDebuggerAgent::continueToLocation):
(WebCore::InspectorDebuggerAgent::resolveBreakpoint):
(WebCore::InspectorDebuggerAgent::searchInContent):
(WebCore::InspectorDebuggerAgent::getScriptSource):
(WebCore::InspectorDebuggerAgent::didParseSource):
(WebCore::InspectorDebuggerAgent::didPause):
(WebCore::InspectorDebuggerAgent::clear):

  • inspector/InspectorDebuggerAgent.h:
  • inspector/ScriptDebugListener.h:

Source/WebKit/mac:

  • WebView/WebScriptDebugger.h:
  • WebView/WebScriptDebugger.mm:

(WebScriptDebugger::WebScriptDebugger):
(WebScriptDebugger::handlePause):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/debugger/Debugger.cpp

    r157746 r158937  
    11/*
    2  *  Copyright (C) 2008 Apple Inc. All rights reserved.
     2 *  Copyright (C) 2008, 2013 Apple Inc. All rights reserved.
    33 *  Copyright (C) 1999-2001 Harri Porten ([email protected])
    44 *  Copyright (C) 2001 Peter Kelly ([email protected])
     
    2323#include "Debugger.h"
    2424
     25#include "DebuggerCallFrame.h"
    2526#include "Error.h"
    2627#include "HeapIterationScope.h"
    2728#include "Interpreter.h"
     29#include "JSCJSValueInlines.h"
    2830#include "JSFunction.h"
    2931#include "JSGlobalObject.h"
     
    9294namespace JSC {
    9395
    94 Debugger::Debugger()
    95     : m_needsExceptionCallbacks(false)
     96class DebuggerCallFrameScope {
     97public:
     98    DebuggerCallFrameScope(Debugger& debugger)
     99        : m_debugger(debugger)
     100    {
     101        ASSERT(!m_debugger.m_currentDebuggerCallFrame);
     102        if (m_debugger.m_currentCallFrame)
     103            m_debugger.m_currentDebuggerCallFrame = DebuggerCallFrame::create(debugger.m_currentCallFrame);
     104    }
     105
     106    ~DebuggerCallFrameScope()
     107    {
     108        if (m_debugger.m_currentDebuggerCallFrame) {
     109            m_debugger.m_currentDebuggerCallFrame->invalidate();
     110            m_debugger.m_currentDebuggerCallFrame = 0;
     111        }
     112    }
     113
     114private:
     115    Debugger& m_debugger;
     116};
     117
     118
     119Debugger::Debugger(bool isInWorkerThread)
     120    : m_pauseOnExceptionsState(DontPauseOnExceptions)
     121    , m_pauseOnNextStatement(false)
     122    , m_isPaused(false)
     123    , m_breakpointsActivated(true)
     124    , m_hasHandlerForExceptionCallback(false)
     125    , m_isInWorkerThread(isInWorkerThread)
     126    , m_reasonForPause(NotPaused)
     127    , m_pauseOnCallFrame(0)
     128    , m_currentCallFrame(0)
     129    , m_lastExecutedLine(UINT_MAX)
     130    , m_lastExecutedSourceID(noSourceID)
     131    , m_topBreakpointID(noBreakpointID)
    96132    , m_needsOpDebugCallbacks(false)
    97133    , m_shouldPause(false)
    98     , m_numberOfBreakpoints(0)
    99134{
    100135}
     
    116151void Debugger::detach(JSGlobalObject* globalObject)
    117152{
     153    // If we're detaching from the currently executing global object, manually tear down our
     154    // stack, since we won't get further debugger callbacks to do so. Also, resume execution,
     155    // since there's no point in staying paused once a window closes.
     156    if (m_currentCallFrame && m_currentCallFrame->dynamicGlobalObject() == globalObject) {
     157        m_currentCallFrame = 0;
     158        m_pauseOnCallFrame = 0;
     159        continueProgram();
     160    }
     161
    118162    ASSERT(m_globalObjects.contains(globalObject));
    119163    m_globalObjects.remove(globalObject);
    120164    globalObject->setDebugger(0);
    121 }
    122 
    123 void Debugger::setNeedsExceptionCallbacks(bool value)
    124 {
    125     m_needsExceptionCallbacks = value;
    126165}
    127166
     
    147186}
    148187
    149 void Debugger::updateNumberOfBreakpoints(int numberOfBreakpoints)
    150 {
    151     ASSERT(numberOfBreakpoints >= 0);
    152     m_numberOfBreakpoints = numberOfBreakpoints;
     188void Debugger::updateNeedForOpDebugCallbacks()
     189{
     190    size_t numberOfBreakpoints = m_breakpointIDToBreakpoint.size();
     191    m_needsOpDebugCallbacks = m_shouldPause || numberOfBreakpoints;
     192}
     193
     194BreakpointID Debugger::setBreakpoint(Breakpoint breakpoint, unsigned& actualLine, unsigned& actualColumn)
     195{
     196    SourceID sourceID = breakpoint.sourceID;
     197    unsigned line = breakpoint.line;
     198    unsigned column = breakpoint.column;
     199
     200    SourceIDToBreakpointsMap::iterator it = m_sourceIDToBreakpoints.find(sourceID);
     201    if (it == m_sourceIDToBreakpoints.end())
     202        it = m_sourceIDToBreakpoints.set(sourceID, LineToBreakpointsMap()).iterator;
     203    LineToBreakpointsMap::iterator breaksIt = it->value.find(line);
     204    if (breaksIt == it->value.end())
     205        breaksIt = it->value.set(line, BreakpointsInLine()).iterator;
     206
     207    BreakpointsInLine& breakpoints = breaksIt->value;
     208    unsigned breakpointsCount = breakpoints.size();
     209    for (unsigned i = 0; i < breakpointsCount; i++)
     210        if (breakpoints[i].column == column) {
     211            // The breakpoint already exists. We're not allowed to create a new
     212            // breakpoint at this location. Rather than returning the breakpointID
     213            // of the pre-existing breakpoint, we need to return noBreakpointID
     214            // to indicate that we're not creating a new one.
     215            return noBreakpointID;
     216        }
     217
     218    BreakpointID id = ++m_topBreakpointID;
     219    RELEASE_ASSERT(id != noBreakpointID);
     220
     221    breakpoint.id = id;
     222    actualLine = line;
     223    actualColumn = column;
     224
     225    breakpoints.append(breakpoint);
     226    m_breakpointIDToBreakpoint.set(id, &breakpoints.last());
     227
    153228    updateNeedForOpDebugCallbacks();
    154 }
    155 
    156 void Debugger::updateNeedForOpDebugCallbacks()
    157 {
    158     m_needsOpDebugCallbacks = m_shouldPause || m_numberOfBreakpoints;
     229
     230    return id;
     231}
     232
     233void Debugger::removeBreakpoint(BreakpointID id)
     234{
     235    ASSERT(id != noBreakpointID);
     236
     237    BreakpointIDToBreakpointMap::iterator idIt = m_breakpointIDToBreakpoint.find(id);
     238    ASSERT(idIt != m_breakpointIDToBreakpoint.end());
     239    Breakpoint& breakpoint = *idIt->value;
     240
     241    SourceID sourceID = breakpoint.sourceID;
     242    ASSERT(sourceID);
     243    SourceIDToBreakpointsMap::iterator it = m_sourceIDToBreakpoints.find(sourceID);
     244    ASSERT(it != m_sourceIDToBreakpoints.end());
     245    LineToBreakpointsMap::iterator breaksIt = it->value.find(breakpoint.line);
     246    ASSERT(breaksIt != it->value.end());
     247
     248    BreakpointsInLine& breakpoints = breaksIt->value;
     249    unsigned breakpointsCount = breakpoints.size();
     250    for (unsigned i = 0; i < breakpointsCount; i++) {
     251        if (breakpoints[i].id == breakpoint.id) {
     252            breakpoints.remove(i);
     253            m_breakpointIDToBreakpoint.remove(idIt);
     254
     255            if (breakpoints.isEmpty()) {
     256                it->value.remove(breaksIt);
     257                if (it->value.isEmpty())
     258                    m_sourceIDToBreakpoints.remove(it);
     259            }
     260            break;
     261        }
     262    }
     263
     264    updateNeedForOpDebugCallbacks();
     265}
     266
     267bool Debugger::hasBreakpoint(SourceID sourceID, const TextPosition& position, Breakpoint *hitBreakpoint) const
     268{
     269    if (!m_breakpointsActivated)
     270        return false;
     271
     272    SourceIDToBreakpointsMap::const_iterator it = m_sourceIDToBreakpoints.find(sourceID);
     273    if (it == m_sourceIDToBreakpoints.end())
     274        return false;
     275
     276    unsigned line = position.m_line.zeroBasedInt();
     277    unsigned column = position.m_column.zeroBasedInt();
     278
     279    LineToBreakpointsMap::const_iterator breaksIt = it->value.find(line);
     280    if (breaksIt == it->value.end())
     281        return false;
     282
     283    bool hit = false;
     284    const BreakpointsInLine& breakpoints = breaksIt->value;
     285    unsigned breakpointsCount = breakpoints.size();
     286    unsigned i;
     287    for (i = 0; i < breakpointsCount; i++) {
     288        unsigned breakLine = breakpoints[i].line;
     289        unsigned breakColumn = breakpoints[i].column;
     290        // Since frontend truncates the indent, the first statement in a line must match the breakpoint (line,0).
     291        if ((line != m_lastExecutedLine && line == breakLine && !breakColumn)
     292            || (line == breakLine && column == breakColumn)) {
     293            hit = true;
     294            break;
     295        }
     296    }
     297    if (!hit)
     298        return false;
     299
     300    if (hitBreakpoint)
     301        *hitBreakpoint = breakpoints[i];
     302
     303    if (breakpoints[i].condition.isEmpty())
     304        return true;
     305
     306    JSValue exception;
     307    JSValue result = DebuggerCallFrame::evaluateWithCallFrame(m_currentCallFrame, breakpoints[i].condition, exception);
     308    if (exception) {
     309        // An erroneous condition counts as "false".
     310        handleExceptionInBreakpointCondition(m_currentCallFrame, exception);
     311        return false;
     312    }
     313    return result.toBoolean(m_currentCallFrame);
     314}
     315
     316void Debugger::clearBreakpoints()
     317{
     318    m_topBreakpointID = noBreakpointID;
     319    m_breakpointIDToBreakpoint.clear();
     320    m_sourceIDToBreakpoints.clear();
     321
     322    updateNeedForOpDebugCallbacks();
     323}
     324
     325void Debugger::setBreakpointsActivated(bool activated)
     326{
     327    m_breakpointsActivated = activated;
     328}
     329
     330void Debugger::setPauseOnExceptionsState(PauseOnExceptionsState pause)
     331{
     332    m_pauseOnExceptionsState = pause;
     333}
     334
     335void Debugger::setPauseOnNextStatement(bool pause)
     336{
     337    m_pauseOnNextStatement = pause;
     338    if (pause)
     339        setShouldPause(true);
     340}
     341
     342void Debugger::breakProgram()
     343{
     344    if (m_isPaused || !m_currentCallFrame)
     345        return;
     346
     347    m_pauseOnNextStatement = true;
     348    setShouldPause(true);
     349    pauseIfNeeded(m_currentCallFrame);
     350}
     351
     352void Debugger::continueProgram()
     353{
     354    if (!m_isPaused)
     355        return;
     356
     357    m_pauseOnNextStatement = false;
     358    notifyDoneProcessingDebuggerEvents();
     359}
     360
     361void Debugger::stepIntoStatement()
     362{
     363    if (!m_isPaused)
     364        return;
     365
     366    m_pauseOnNextStatement = true;
     367    setShouldPause(true);
     368    notifyDoneProcessingDebuggerEvents();
     369}
     370
     371void Debugger::stepOverStatement()
     372{
     373    if (!m_isPaused)
     374        return;
     375
     376    m_pauseOnCallFrame = m_currentCallFrame;
     377    notifyDoneProcessingDebuggerEvents();
     378}
     379
     380void Debugger::stepOutOfFunction()
     381{
     382    if (!m_isPaused)
     383        return;
     384
     385    m_pauseOnCallFrame = m_currentCallFrame ? m_currentCallFrame->callerFrameSkippingVMEntrySentinel() : 0;
     386    notifyDoneProcessingDebuggerEvents();
     387}
     388
     389void Debugger::updateCallFrame(CallFrame* callFrame)
     390{
     391    m_currentCallFrame = callFrame;
     392    SourceID sourceID = DebuggerCallFrame::sourceIDForCallFrame(callFrame);
     393    if (m_lastExecutedSourceID != sourceID) {
     394        m_lastExecutedLine = UINT_MAX;
     395        m_lastExecutedSourceID = sourceID;
     396    }
     397}
     398
     399void Debugger::updateCallFrameAndPauseIfNeeded(CallFrame* callFrame)
     400{
     401    updateCallFrame(callFrame);
     402    pauseIfNeeded(callFrame);
     403    if (!needsOpDebugCallbacks())
     404        m_currentCallFrame = 0;
     405}
     406
     407void Debugger::pauseIfNeeded(CallFrame* callFrame)
     408{
     409    if (m_isPaused)
     410        return;
     411
     412    JSGlobalObject* dynamicGlobalObject = callFrame->dynamicGlobalObject();
     413    if (!needPauseHandling(dynamicGlobalObject))
     414        return;
     415
     416    Breakpoint breakpoint;
     417    bool didHitBreakpoint = false;
     418    bool pauseNow = m_pauseOnNextStatement;
     419    pauseNow |= (m_pauseOnCallFrame == m_currentCallFrame);
     420
     421    intptr_t sourceID = DebuggerCallFrame::sourceIDForCallFrame(m_currentCallFrame);
     422    TextPosition position = DebuggerCallFrame::positionForCallFrame(m_currentCallFrame);
     423    pauseNow |= didHitBreakpoint = hasBreakpoint(sourceID, position, &breakpoint);
     424    m_lastExecutedLine = position.m_line.zeroBasedInt();
     425    if (!pauseNow)
     426        return;
     427
     428    DebuggerCallFrameScope debuggerCallFrameScope(*this);
     429
     430    if (didHitBreakpoint) {
     431        handleBreakpointHit(breakpoint);
     432        if (breakpoint.autoContinue)
     433            return;
     434    }
     435
     436    m_pauseOnCallFrame = 0;
     437    m_pauseOnNextStatement = false;
     438    m_isPaused = true;
     439
     440    handlePause(m_reasonForPause, dynamicGlobalObject);
     441
     442    if (!m_pauseOnNextStatement && !m_pauseOnCallFrame) {
     443        setShouldPause(false);
     444        if (!needsOpDebugCallbacks())
     445            m_currentCallFrame = 0;
     446    }
     447
     448    m_isPaused = false;
     449}
     450
     451void Debugger::exception(CallFrame* callFrame, JSValue exception, bool hasHandler)
     452{
     453    if (m_isPaused)
     454        return;
     455
     456    PauseReasonDeclaration reason(*this, PausedForException);
     457    if (m_pauseOnExceptionsState == PauseOnAllExceptions || (m_pauseOnExceptionsState == PauseOnUncaughtExceptions && !hasHandler)) {
     458        m_pauseOnNextStatement = true;
     459        setShouldPause(true);
     460    }
     461
     462    m_hasHandlerForExceptionCallback = true;
     463    m_currentException = exception;
     464    updateCallFrameAndPauseIfNeeded(callFrame);
     465    m_currentException = JSValue();
     466    m_hasHandlerForExceptionCallback = false;
     467}
     468
     469void Debugger::atStatement(CallFrame* callFrame)
     470{
     471    if (m_isPaused)
     472        return;
     473
     474    PauseReasonDeclaration reason(*this, PausedAtStatement);
     475    updateCallFrameAndPauseIfNeeded(callFrame);
     476}
     477
     478void Debugger::callEvent(CallFrame* callFrame)
     479{
     480    if (m_isPaused)
     481        return;
     482
     483    PauseReasonDeclaration reason(*this, PausedAfterCall);
     484    updateCallFrameAndPauseIfNeeded(callFrame);
     485}
     486
     487void Debugger::returnEvent(CallFrame* callFrame)
     488{
     489    if (m_isPaused)
     490        return;
     491
     492    PauseReasonDeclaration reason(*this, PausedBeforeReturn);
     493    updateCallFrameAndPauseIfNeeded(callFrame);
     494
     495    // detach may have been called during pauseIfNeeded
     496    if (!m_currentCallFrame)
     497        return;
     498
     499    // Treat stepping over a return statement like stepping out.
     500    if (m_currentCallFrame == m_pauseOnCallFrame)
     501        m_pauseOnCallFrame = m_currentCallFrame->callerFrameSkippingVMEntrySentinel();
     502
     503    m_currentCallFrame = m_currentCallFrame->callerFrameSkippingVMEntrySentinel();
     504}
     505
     506void Debugger::willExecuteProgram(CallFrame* callFrame)
     507{
     508    if (m_isPaused)
     509        return;
     510
     511    PauseReasonDeclaration reason(*this, PausedAtStartOfProgram);
     512    // FIXME: This check for whether we're debugging a worker thread is a workaround
     513    // for https://bugs.webkit.org/show_bug.cgi?id=102637. Remove it when we rework
     514    // the debugger implementation to not require callbacks.
     515    if (!m_isInWorkerThread)
     516        updateCallFrameAndPauseIfNeeded(callFrame);
     517    else if (needsOpDebugCallbacks())
     518        updateCallFrame(callFrame);
     519}
     520
     521void Debugger::didExecuteProgram(CallFrame* callFrame)
     522{
     523    if (m_isPaused)
     524        return;
     525
     526    PauseReasonDeclaration reason(*this, PausedAtEndOfProgram);
     527    updateCallFrameAndPauseIfNeeded(callFrame);
     528
     529    // Treat stepping over the end of a program like stepping out.
     530    if (!m_currentCallFrame)
     531        return;
     532    if (m_currentCallFrame == m_pauseOnCallFrame) {
     533        m_pauseOnCallFrame = m_currentCallFrame->callerFrameSkippingVMEntrySentinel();
     534        if (!m_currentCallFrame)
     535            return;
     536    }
     537    m_currentCallFrame = m_currentCallFrame->callerFrameSkippingVMEntrySentinel();
     538}
     539
     540void Debugger::didReachBreakpoint(CallFrame* callFrame)
     541{
     542    if (m_isPaused)
     543        return;
     544
     545    PauseReasonDeclaration reason(*this, PausedForBreakpoint);
     546    m_pauseOnNextStatement = true;
     547    setShouldPause(true);
     548    updateCallFrameAndPauseIfNeeded(callFrame);
     549}
     550
     551DebuggerCallFrame* Debugger::currentDebuggerCallFrame() const
     552{
     553    ASSERT(m_currentDebuggerCallFrame);
     554    return m_currentDebuggerCallFrame.get();
    159555}
    160556
Note: See TracChangeset for help on using the changeset viewer.