Changeset 170677 in webkit


Ignore:
Timestamp:
Jul 1, 2014 4:40:32 PM (10 years ago)
Author:
mark.lam@apple.com
Message:

Debugger's breakpoint list should not be a Vector.
<https://webkit.org/b/134514>

Reviewed by Geoffrey Garen.

The debugger currently stores breakpoint data as entries in a Vector (see
BreakpointsInLine). It also keeps a fast map look up of breakpoint IDs to
the breakpoint data (see m_breakpointIDToBreakpoint). Because a Vector can
compact or reallocate its backing store, this can causes all sorts of havoc.
The m_breakpointIDToBreakpoint map assumes that the breakpoint data doesn't
move in memory.

The fix is to replace the BreakpointsInLine Vector with a BreakpointsList
doubly linked list.

  • debugger/Breakpoint.h:

(JSC::Breakpoint::Breakpoint):
(JSC::BreakpointsList::~BreakpointsList):

  • debugger/Debugger.cpp:

(JSC::Debugger::setBreakpoint):
(JSC::Debugger::removeBreakpoint):
(JSC::Debugger::hasBreakpoint):

  • debugger/Debugger.h:
Location:
trunk/Source/JavaScriptCore
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r170601 r170677  
     12014-07-01  Mark Lam  <mark.lam@apple.com>
     2
     3        Debugger's breakpoint list should not be a Vector.
     4        <https://webkit.org/b/134514>
     5
     6        Reviewed by Geoffrey Garen.
     7
     8        The debugger currently stores breakpoint data as entries in a Vector (see
     9        BreakpointsInLine).  It also keeps a fast map look up of breakpoint IDs to
     10        the breakpoint data (see m_breakpointIDToBreakpoint).  Because a Vector can
     11        compact or reallocate its backing store, this can causes all sorts of havoc.
     12        The m_breakpointIDToBreakpoint map assumes that the breakpoint data doesn't
     13        move in memory.
     14
     15        The fix is to replace the BreakpointsInLine Vector with a BreakpointsList
     16        doubly linked list.
     17
     18        * debugger/Breakpoint.h:
     19        (JSC::Breakpoint::Breakpoint):
     20        (JSC::BreakpointsList::~BreakpointsList):
     21        * debugger/Debugger.cpp:
     22        (JSC::Debugger::setBreakpoint):
     23        (JSC::Debugger::removeBreakpoint):
     24        (JSC::Debugger::hasBreakpoint):
     25        * debugger/Debugger.h:
     26
    1272014-06-30  Michael Saboff  <msaboff@apple.com>
    228
  • trunk/Source/JavaScriptCore/debugger/Breakpoint.h

    r162970 r170677  
    2828
    2929#include "DebuggerPrimitives.h"
     30#include <wtf/DoublyLinkedList.h>
     31#include <wtf/RefCounted.h>
    3032#include <wtf/text/WTFString.h>
    3133
    3234namespace JSC {
    3335
    34 struct Breakpoint {
     36struct Breakpoint : public DoublyLinkedListNode<Breakpoint> {
    3537    Breakpoint()
    3638        : id(noBreakpointID)
     
    5254    }
    5355
     56    Breakpoint(const Breakpoint& other)
     57        : id(other.id)
     58        , sourceID(other.sourceID)
     59        , line(other.line)
     60        , column(other.column)
     61        , condition(other.condition)
     62        , autoContinue(other.autoContinue)
     63    {
     64    }
     65
    5466    BreakpointID id;
    5567    SourceID sourceID;
     
    6072
    6173    static const unsigned unspecifiedColumn = UINT_MAX;
     74
     75private:
     76    Breakpoint* m_prev;
     77    Breakpoint* m_next;
     78
     79    friend class WTF::DoublyLinkedListNode<Breakpoint>;
     80};
     81
     82class BreakpointsList : public DoublyLinkedList<Breakpoint>,
     83    public RefCounted<BreakpointsList> {
     84public:
     85    ~BreakpointsList()
     86    {
     87        Breakpoint* breakpoint;
     88        while ((breakpoint = removeHead()))
     89            delete breakpoint;
     90        ASSERT(isEmpty());
     91    }
    6292};
    6393
  • trunk/Source/JavaScriptCore/debugger/Debugger.cpp

    r167396 r170677  
    358358    LineToBreakpointsMap::iterator breaksIt = it->value.find(line);
    359359    if (breaksIt == it->value.end())
    360         breaksIt = it->value.set(line, BreakpointsInLine()).iterator;
    361 
    362     BreakpointsInLine& breakpoints = breaksIt->value;
    363     unsigned breakpointsCount = breakpoints.size();
    364     for (unsigned i = 0; i < breakpointsCount; i++)
    365         if (breakpoints[i].column == column) {
     360        breaksIt = it->value.set(line, adoptRef(new BreakpointsList)).iterator;
     361
     362    BreakpointsList& breakpoints = *breaksIt->value;
     363    for (Breakpoint* current = breakpoints.head(); current; current = current->next()) {
     364        if (current->column == column) {
    366365            // The breakpoint already exists. We're not allowed to create a new
    367366            // breakpoint at this location. Rather than returning the breakpointID
     
    370369            return noBreakpointID;
    371370        }
     371    }
    372372
    373373    BreakpointID id = ++m_topBreakpointID;
     
    378378    actualColumn = column;
    379379
    380     breakpoints.append(breakpoint);
    381     m_breakpointIDToBreakpoint.set(id, &breakpoints.last());
     380    Breakpoint* newBreakpoint = new Breakpoint(breakpoint);
     381    breakpoints.append(newBreakpoint);
     382    m_breakpointIDToBreakpoint.set(id, newBreakpoint);
    382383
    383384    toggleBreakpoint(breakpoint, BreakpointEnabled);
     
    392393    BreakpointIDToBreakpointMap::iterator idIt = m_breakpointIDToBreakpoint.find(id);
    393394    ASSERT(idIt != m_breakpointIDToBreakpoint.end());
    394     Breakpoint& breakpoint = *idIt->value;
    395 
    396     SourceID sourceID = breakpoint.sourceID;
     395    Breakpoint* breakpoint = idIt->value;
     396
     397    SourceID sourceID = breakpoint->sourceID;
    397398    ASSERT(sourceID);
    398399    SourceIDToBreakpointsMap::iterator it = m_sourceIDToBreakpoints.find(sourceID);
    399400    ASSERT(it != m_sourceIDToBreakpoints.end());
    400     LineToBreakpointsMap::iterator breaksIt = it->value.find(breakpoint.line);
     401    LineToBreakpointsMap::iterator breaksIt = it->value.find(breakpoint->line);
    401402    ASSERT(breaksIt != it->value.end());
    402403
    403     toggleBreakpoint(breakpoint, BreakpointDisabled);
    404 
    405     BreakpointsInLine& breakpoints = breaksIt->value;
    406     unsigned breakpointsCount = breakpoints.size();
    407     for (unsigned i = 0; i < breakpointsCount; i++) {
    408         if (breakpoints[i].id == breakpoint.id) {
    409             breakpoints.remove(i);
    410             m_breakpointIDToBreakpoint.remove(idIt);
    411 
    412             if (breakpoints.isEmpty()) {
    413                 it->value.remove(breaksIt);
    414                 if (it->value.isEmpty())
    415                     m_sourceIDToBreakpoints.remove(it);
    416             }
    417             break;
    418         }
     404    toggleBreakpoint(*breakpoint, BreakpointDisabled);
     405
     406    BreakpointsList& breakpoints = *breaksIt->value;
     407#if !ASSERT_DISABLED
     408    bool found = false;
     409    for (Breakpoint* current = breakpoints.head(); current && !found; current = current->next()) {
     410        if (current->id == breakpoint->id)
     411            found = true;
     412    }
     413    ASSERT(found);
     414#endif
     415
     416    m_breakpointIDToBreakpoint.remove(idIt);
     417    breakpoints.remove(breakpoint);
     418    delete breakpoint;
     419
     420    if (breakpoints.isEmpty()) {
     421        it->value.remove(breaksIt);
     422        if (it->value.isEmpty())
     423            m_sourceIDToBreakpoints.remove(it);
    419424    }
    420425}
     
    437442
    438443    bool hit = false;
    439     const BreakpointsInLine& breakpoints = breaksIt->value;
    440     unsigned breakpointsCount = breakpoints.size();
    441     unsigned i;
    442     for (i = 0; i < breakpointsCount; i++) {
    443         unsigned breakLine = breakpoints[i].line;
    444         unsigned breakColumn = breakpoints[i].column;
     444    const BreakpointsList& breakpoints = *breaksIt->value;
     445    Breakpoint* breakpoint;
     446    for (breakpoint = breakpoints.head(); breakpoint; breakpoint = breakpoint->next()) {
     447        unsigned breakLine = breakpoint->line;
     448        unsigned breakColumn = breakpoint->column;
    445449        // Since frontend truncates the indent, the first statement in a line must match the breakpoint (line,0).
    446450        ASSERT(this == m_currentCallFrame->codeBlock()->globalObject()->debugger());
     
    455459
    456460    if (hitBreakpoint)
    457         *hitBreakpoint = breakpoints[i];
    458 
    459     if (breakpoints[i].condition.isEmpty())
     461        *hitBreakpoint = *breakpoint;
     462
     463    if (breakpoint->condition.isEmpty())
    460464        return true;
    461465
     
    466470    JSValue exception;
    467471    DebuggerCallFrame* debuggerCallFrame = currentDebuggerCallFrame();
    468     JSValue result = debuggerCallFrame->evaluate(breakpoints[i].condition, exception);
     472    JSValue result = debuggerCallFrame->evaluate(breakpoint->condition, exception);
    469473
    470474    // We can lose the debugger while executing JavaScript.
  • trunk/Source/JavaScriptCore/debugger/Debugger.h

    r165005 r170677  
    3030#include <wtf/HashSet.h>
    3131#include <wtf/RefPtr.h>
    32 #include <wtf/Vector.h>
    3332#include <wtf/text/TextPosition.h>
    3433
     
    129128    typedef HashMap<BreakpointID, Breakpoint*> BreakpointIDToBreakpointMap;
    130129
    131     typedef Vector<Breakpoint> BreakpointsInLine;
    132     typedef HashMap<unsigned, BreakpointsInLine, WTF::IntHash<int>, WTF::UnsignedWithZeroKeyHashTraits<int>> LineToBreakpointsMap;
     130    typedef HashMap<unsigned, RefPtr<BreakpointsList>, WTF::IntHash<int>, WTF::UnsignedWithZeroKeyHashTraits<int>> LineToBreakpointsMap;
    133131    typedef HashMap<SourceID, LineToBreakpointsMap, WTF::IntHash<SourceID>, WTF::UnsignedWithZeroKeyHashTraits<SourceID>> SourceIDToBreakpointsMap;
    134132
Note: See TracChangeset for help on using the changeset viewer.