Commit 8ae541b3 authored by hjk's avatar hjk

debugger: Refactor breakpoint handling.

The breakpoints are now (fairly) tightly guarded by the BreakpointHandler.
Engines and Views are only supposed to refer to them by id. They also have
individual states now. The breakpoint data is split into a "user requested"
"fixed" part in BreakpointData and the engines' acknowledged data in a new
struct BreakpointResponse.

TODO: Move m_state and m_engine members to BreakpointResponse. Fix regressions
in the marker handling.
parent 33bae0d7
This diff is collapsed.
......@@ -37,18 +37,15 @@
#include <QtGui/QIcon>
namespace Debugger {
class DebuggerEngine;
namespace Internal {
//////////////////////////////////////////////////////////////////
//
// BreakHandler
//
//////////////////////////////////////////////////////////////////
namespace Debugger {
namespace Internal {
class BreakHandler : public QAbstractTableModel
{
Q_OBJECT
......@@ -57,31 +54,28 @@ public:
BreakHandler();
~BreakHandler();
void removeAllBreakpoints();
void loadSessionData();
void saveSessionData();
QAbstractItemModel *model() { return this; }
BreakpointData *at(int index) const;
int size() const;
BreakpointIds allBreakpointIds() const;
BreakpointIds engineBreakpointIds(DebuggerEngine *engine) const;
BreakpointIds unclaimedBreakpointIds() const;
BreakpointData *breakpointById(BreakpointId id) const;
int size() const { return m_bp.size(); }
bool hasPendingBreakpoints() const;
void removeAt(int index); // This also deletes the marker.
void clear(); // This also deletes all the marker.
int indexOf(BreakpointData *data);
// Find a breakpoint matching approximately the data in needle.
BreakpointData *findSimilarBreakpoint(const BreakpointData *needle) const;
BreakpointData *findBreakpointByNumber(int bpNumber) const;
int findWatchPointIndexByAddress(quint64 address) const;
bool watchPointAt(quint64 address) const;
BreakpointId findSimilarBreakpoint(const BreakpointResponse &needle) const;
BreakpointId findBreakpointByNumber(int bpNumber) const;
BreakpointId findWatchpointByAddress(quint64 address) const;
BreakpointId findBreakpointByFunction(const QString &functionName) const;
BreakpointId findBreakpointByIndex(const QModelIndex &index) const;
void setWatchpointByAddress(quint64 address);
bool hasWatchpointAt(quint64 address) const;
void updateMarkers();
void updateMarker(BreakpointData *);
void removeMarker(BreakpointData *);
bool isActive() const;
Breakpoints takeRemovedBreakpoints(); // Owned.
Breakpoints takeEnabledBreakpoints(); // Not owned.
Breakpoints takeDisabledBreakpoints(); // Not owned.
void removeMarker(BreakpointId id);
QIcon breakpointIcon() const { return m_breakpointIcon; }
QIcon disabledBreakpointIcon() const { return m_disabledBreakpointIcon; }
......@@ -89,21 +83,60 @@ public:
QIcon emptyIcon() const { return m_emptyIcon; }
void toggleBreakpoint(const QString &fileName, int lineNumber, quint64 address = 0);
void toggleBreakpointEnabled(const QString &fileName, int lineNumber);
BreakpointData *findBreakpoint(const QString &fileName, int lineNumber,
bool useMarkerPosition = true);
BreakpointData *findBreakpoint(quint64 address) const;
BreakpointId findBreakpointByFileAndLine(const QString &fileName,
int lineNumber, bool useMarkerPosition = true);
BreakpointId findBreakpointByAddress(quint64 address) const;
void appendBreakpoint(BreakpointData *data);
void reinsertBreakpoint(BreakpointData *data);
void toggleBreakpointEnabled(BreakpointData *data);
void breakByFunction(const QString &functionName);
void removeBreakpoint(int index);
void removeBreakpoint(BreakpointData *data);
void synchronizeBreakpoints();
signals:
void breakpointSynchronizationRequested();
void removeBreakpoint(BreakpointId id);
QIcon icon(BreakpointId id) const;
void gotoLocation(BreakpointId id) const;
// Getter retrieves property value.
// Setter sets property value and triggers update if changed.
#define PROPERTY(type, getter, setter) \
type getter(BreakpointId id) const; \
void setter(BreakpointId id, const type &value);
PROPERTY(bool, useFullPath, setUseFullPath)
PROPERTY(QString, markerFileName, setMarkerFileName)
PROPERTY(int, markerLineNumber, setMarkerLineNumber)
PROPERTY(QByteArray, condition, setCondition)
PROPERTY(int, ignoreCount, setIgnoreCount)
PROPERTY(QByteArray, threadSpec, setThreadSpec)
PROPERTY(BreakpointState, state, setState)
PROPERTY(QString, fileName, setFileName)
PROPERTY(QString, functionName, setFunctionName)
PROPERTY(BreakpointType, type, setType);
PROPERTY(quint64, address, setAddress);
PROPERTY(int, lineNumber, setLineNumber);
#undef PROPERTY
bool isEnabled(BreakpointId id) const;
void setEnabled(BreakpointId id, const bool &on);
void updateEnabled(BreakpointId id, const bool &on);
void updateLineNumberFromMarker(BreakpointId id, int lineNumber);
DebuggerEngine *engine(BreakpointId id) const;
void setEngine(BreakpointId id, DebuggerEngine *engine);
BreakpointResponse response(BreakpointId id) const;
void setResponse(BreakpointId id, const BreakpointResponse &data);
// Incorporate debugger feedback. No synchronization request needed.
// Return true if something changed.
void ackCondition(BreakpointId id);
void ackIgnoreCount(BreakpointId id);
void notifyBreakpointInsertOk(BreakpointId id);
void notifyBreakpointInsertFailed(BreakpointId id);
void notifyBreakpointChangeOk(BreakpointId id);
void notifyBreakpointChangeFailed(BreakpointId id);
void notifyBreakpointRemoveOk(BreakpointId id);
void notifyBreakpointRemoveFailed(BreakpointId id);
void notifyBreakpointReleased(BreakpointId id);
// This takes ownership.
void appendBreakpoint(BreakpointData *data);
private:
friend class BreakpointMarker;
......@@ -118,8 +151,8 @@ private:
void markerUpdated(BreakpointMarker *marker, int lineNumber);
void loadBreakpoints();
void saveBreakpoints();
void removeBreakpointHelper(int index);
void append(BreakpointData *data);
void updateMarker(BreakpointId id);
void cleanupBreakpoint(BreakpointId id);
const QIcon m_breakpointIcon;
const QIcon m_disabledBreakpointIcon;
......@@ -127,17 +160,17 @@ private:
const QIcon m_emptyIcon;
const QIcon m_watchpointIcon;
Breakpoints m_inserted; // Lately inserted breakpoints.
Breakpoints m_removed; // Lately removed breakpoints.
Breakpoints m_enabled; // Lately enabled breakpoints.
Breakpoints m_disabled; // Lately disabled breakpoints.
// Hack for BreakWindow::findSimilarBreakpoint
mutable BreakpointData *m_lastFound;
mutable bool m_lastFoundQueried;
typedef QMap<BreakpointId, BreakpointData *> Breakpoints;
typedef QMap<BreakpointId, BreakpointMarker *> Markers;
typedef QMap<BreakpointId, BreakpointResponse *> Responses;
typedef Breakpoints::ConstIterator ConstIterator;
Breakpoints m_bp;
QHash<quint64, BreakpointMarker *> m_markers;
Markers m_markers;
Responses m_responses;
void scheduleSynchronization();
void timerEvent(QTimerEvent *event);
int m_syncTimerId;
};
} // namespace Internal
......
This diff is collapsed.
......@@ -36,6 +36,11 @@
#include <QtCore/QCoreApplication>
namespace Debugger {
class DebuggerEngine;
typedef quint64 BreakpointId; // FIXME: make Internal.
namespace Internal {
class BreakpointMarker;
......@@ -49,6 +54,7 @@ class BreakHandler;
enum BreakpointType
{
UnknownType,
BreakpointByFileAndLine,
BreakpointByFunction,
BreakpointByAddress,
......@@ -58,36 +64,21 @@ enum BreakpointType
enum BreakpointState
{
BreakpointNew,
BreakpointInsertionRequested, // Inferior was told about bp, not ack'ed.
BreakpointInsertRequested, // Inferior was told about bp, not ack'ed.
BreakpointInsertProceeding,
BreakpointChangeRequested,
BreakpointOk,
BreakpointRemovalRequested,
BreakpointRemoved,
BreakpointChangeProceeding,
BreakpointPending,
BreakpointInserted,
BreakpointRemoveRequested,
BreakpointRemoveProceeding,
BreakpointDead,
};
class BreakpointData
{
public:
BreakpointData();
~BreakpointData();
QString toToolTip() const;
void clear(); // Delete all generated data.
bool isLocatedAt(const QString &fileName, int lineNumber,
bool useMarkerPosition) const;
bool isSimilarTo(const BreakpointData *needle) const;
bool conditionsMatch() const;
// This copies only the static data.
BreakpointData *clone() const;
// Generic name for function to break on 'throw'
static const char *throwFunction;
static const char *catchFunction;
private:
// Intentionally unimplemented.
// Making it copyable is tricky because of the markers.
BreakpointData(const BreakpointData &);
......@@ -96,64 +87,106 @@ private:
friend class BreakHandler;
public:
quint64 id;
BreakpointState state;
bool uiDirty; // ui has changed stuff
BreakpointData();
bool enabled; // Should we talk to the debugger engine?
bool pending; // Does the debugger engine know about us already?
BreakpointType type; // Type of breakpoint.
bool isPending() const { return m_state == BreakpointPending
|| m_state == BreakpointNew; }
BreakpointType type() const { return m_type; }
quint64 address() const { return m_address; }
bool useFullPath() const { return m_useFullPath; }
QString toToolTip() const;
QString toString() const;
// This "user requested information" will get stored in the session.
QString fileName; // Short name of source file.
QByteArray condition; // Condition associated with breakpoint.
int ignoreCount; // Ignore count associated with breakpoint.
int lineNumber; // Line in source file.
quint64 address; // Address for watchpoints.
QByteArray threadSpec; // Thread specification.
// Name of containing function, special values:
// BreakpointData::throwFunction, BreakpointData::catchFunction
QString funcName;
bool useFullPath; // Should we use the full path when setting the bp?
// This is what gdb produced in response.
QByteArray bpNumber; // Breakpoint number assigned by the debugger engine.
QByteArray bpCondition; // Condition acknowledged by the debugger engine.
int bpIgnoreCount; // Ignore count acknowledged by the debugger engine.
QString bpFileName; // File name acknowledged by the debugger engine.
QString bpFullName; // Full file name acknowledged by the debugger engine.
int bpLineNumber; // Line number acknowledged by the debugger engine.
int bpCorrectedLineNumber; // Acknowledged by the debugger engine.
QByteArray bpThreadSpec; // Thread spec acknowledged by the debugger engine.
QString bpFuncName; // Function name acknowledged by the debugger engine.
quint64 bpAddress; // Address acknowledged by the debugger engine.
bool bpMultiple; // Happens in constructors/gdb.
bool bpEnabled; // Enable/disable command sent.
QByteArray bpState; // gdb: <PENDING>, <MULTIPLE>
void setMarkerFileName(const QString &fileName);
bool isLocatedAt(const QString &fileName, int lineNumber,
bool useMarkerPosition) const;
bool conditionsMatch(const QString &other) const;
BreakpointState state() const { return m_state; }
QString functionName() const { return m_functionName; }
QString markerFileName() const { return m_markerFileName; }
void setMarkerLineNumber(int lineNumber);
QString fileName() const { return m_fileName; }
int markerLineNumber() const { return m_markerLineNumber; }
int lineNumber() const { return m_lineNumber; }
int ignoreCount() const { return m_ignoreCount; }
bool isEnabled() const { return m_enabled; }
QByteArray threadSpec() const { return m_threadSpec; }
QByteArray condition() const { return m_condition; }
DebuggerEngine *engine() const { return m_engine; }
bool isWatchpoint() const { return m_type == Watchpoint; }
bool isBreakpoint() const { return m_type != Watchpoint; } // Enough for now.
// Generic name for function to break on 'throw'
static const char *throwFunction;
static const char *catchFunction;
bool isWatchpoint() const { return type == Watchpoint; }
bool isBreakpoint() const { return type != Watchpoint; } // Enough for now.
Q_DECLARE_TR_FUNCTIONS(BreakHandler)
//private:
// All setters return true on change.
bool setUseFullPath(bool on);
bool setMarkerFileName(const QString &file);
bool setMarkerLineNumber(int line);
bool setFileName(const QString &file);
bool setEnabled(bool on);
bool setIgnoreCount(bool count);
bool setFunctionName(const QString &name);
bool setLineNumber(int line);
bool setAddress(quint64 address);
bool setThreadSpec(const QByteArray &spec);
bool setType(BreakpointType type);
bool setCondition(const QByteArray &cond);
bool setState(BreakpointState state);
bool setEngine(DebuggerEngine *engine);
// TODO: move those to breakhandler
private:
// Taken from either user input or gdb responses.
DebuggerEngine *m_engine;
BreakpointType m_type; // Type of breakpoint.
BreakpointState m_state; // Current state of breakpoint.
bool m_enabled; // Should we talk to the debugger engine?
bool m_pending; // Does the debugger engine know about us already?
bool m_useFullPath; // Should we use the full path when setting the bp?
// This "user requested information" will get stored in the session.
QString m_fileName; // Short name of source file.
QByteArray m_condition; // Condition associated with breakpoint.
int m_ignoreCount; // Ignore count associated with breakpoint.
int m_lineNumber; // Line in source file.
quint64 m_address; // Address for watchpoints.
QByteArray m_threadSpec; // Thread specification.
// Name of containing function, special values:
// BreakpointData::throwFunction, BreakpointData::catchFunction
QString m_functionName;
QString m_markerFileName; // Used to locate the marker.
int m_markerLineNumber;
public:
Q_DECLARE_TR_FUNCTIONS(BreakHandler)
};
// This is what debuggers produced in response to the attempt to
// insert a breakpoint. The data might differ from the requested bits.
class BreakpointResponse
{
public:
BreakpointResponse();
QString toString() const;
public:
int bpNumber; // Breakpoint number assigned by the debugger engine.
BreakpointType bpType; // Breakpoint type used by debugger engine.
QByteArray bpCondition; // Condition acknowledged by the debugger engine.
int bpIgnoreCount; // Ignore count acknowledged by the debugger engine.
QString bpFileName; // File name acknowledged by the debugger engine.
QString bpFullName; // Full file name acknowledged by the debugger engine.
int bpLineNumber; // Line number acknowledged by the debugger engine.
//int bpCorrectedLineNumber; // Acknowledged by the debugger engine.
QByteArray bpThreadSpec; // Thread spec acknowledged by the debugger engine.
QString bpFuncName; // Function name acknowledged by the debugger engine.
quint64 bpAddress; // Address acknowledged by the debugger engine.
bool bpMultiple; // Happens in constructors/gdb.
bool bpEnabled; // Enable/disable command sent.
QByteArray bpState; // gdb: <PENDING>, <MULTIPLE>
};
typedef QList<BreakpointData *> Breakpoints;
typedef QList<BreakpointId> BreakpointIds;
} // namespace Internal
} // namespace Debugger
Q_DECLARE_METATYPE(Debugger::Internal::BreakpointData *);
#endif // DEBUGGER_BREAKPOINT_H
......@@ -29,17 +29,14 @@
#include "breakpointmarker.h"
#include "breakhandler.h"
#include "stackframe.h"
#include "debuggercore.h"
#include <texteditor/basetextmark.h>
#include <utils/qtcassert.h>
#include <QtCore/QByteArray>
#include <QtCore/QDebug>
#include <QtCore/QTextStream>
#include <QtCore/QFileInfo>
#include <QtCore/QDir>
//////////////////////////////////////////////////////////////////
//
......@@ -51,12 +48,9 @@
namespace Debugger {
namespace Internal {
BreakpointMarker::BreakpointMarker(BreakHandler *handler, BreakpointData *data,
BreakpointMarker::BreakpointMarker(BreakpointId id,
const QString &fileName, int lineNumber)
: BaseTextMark(fileName, lineNumber)
, m_handler(handler)
, m_data(data)
, m_pending(true)
: BaseTextMark(fileName, lineNumber), m_id(id)
{
//qDebug() << "CREATE MARKER " << fileName << lineNumber;
}
......@@ -64,24 +58,11 @@ BreakpointMarker::BreakpointMarker(BreakHandler *handler, BreakpointData *data,
BreakpointMarker::~BreakpointMarker()
{
//qDebug() << "REMOVE MARKER ";
m_data = 0;
}
QIcon BreakpointMarker::icon() const
{
if (!m_data->enabled)
return m_handler->disabledBreakpointIcon();
if (!m_handler->isActive())
return m_handler->emptyIcon();
return m_pending ? m_handler->pendingBreakPointIcon() : m_handler->breakpointIcon();
}
void BreakpointMarker::setPending(bool pending)
{
if (pending == m_pending)
return;
m_pending = pending;
updateMarker();
return breakHandler()->icon(m_id);
}
void BreakpointMarker::updateBlock(const QTextBlock &)
......@@ -91,38 +72,12 @@ void BreakpointMarker::updateBlock(const QTextBlock &)
void BreakpointMarker::removedFromEditor()
{
if (!m_data)
return;
m_handler->removeBreakpoint(m_data);
//handler->saveBreakpoints();
m_handler->updateMarkers();
breakHandler()->removeBreakpoint(m_id);
}
void BreakpointMarker::updateLineNumber(int lineNumber)
{
if (!m_data)
return;
//if (m_data->markerLineNumber == lineNumber)
// return;
if (m_data->markerLineNumber() != lineNumber) {
m_data->setMarkerLineNumber(lineNumber);
// FIXME: Should we tell gdb about the change?
// Ignore it for now, as we would require re-compilation
// and debugger re-start anyway.
if (0 && m_data->bpLineNumber) {
if (!m_data->bpNumber.trimmed().isEmpty()) {
m_data->pending = true;
}
}
}
// Ignore updates to the "real" line number while the debugger is
// running, as this can be triggered by moving the breakpoint to
// the next line that generated code.
// FIXME: Do we need yet another data member?
if (m_data->bpNumber.trimmed().isEmpty()) {
m_data->lineNumber = lineNumber;
m_handler->updateMarkers();
}
breakHandler()->updateLineNumberFromMarker(m_id, lineNumber);
}
} // namespace Internal
......
......@@ -45,18 +45,14 @@ class BreakpointMarker : public TextEditor::BaseTextMark
{
Q_OBJECT
public:
BreakpointMarker(BreakHandler *handler, BreakpointData *data,
const QString &fileName, int lineNumber);
BreakpointMarker(BreakpointId id, const QString &fileName, int lineNumber);
~BreakpointMarker();
QIcon icon() const;
void setPending(bool pending);
void updateBlock(const QTextBlock &);
void removedFromEditor();
void updateLineNumber(int lineNumber);
private:
BreakHandler *m_handler;
BreakpointData *m_data;
bool m_pending;
BreakpointId m_id;
friend class BreakHandler;
};
......
This diff is collapsed.
......@@ -30,13 +30,13 @@
#ifndef DEBUGGER_BREAKWINDOW_H
#define DEBUGGER_BREAKWINDOW_H
#include "breakhandler.h"
#include <QtGui/QTreeView>
namespace Debugger {
namespace Internal {
class BreakpointData;
class BreakWindow : public QTreeView
{
Q_OBJECT
......@@ -45,7 +45,7 @@ public:
explicit BreakWindow(QWidget *parent = 0);
~BreakWindow();
static bool editBreakpoint(BreakpointData *data, QWidget *parent = 0);
static bool editBreakpoint(BreakpointId id, QWidget *parent = 0);
private slots:
void resizeColumnsToContents();
......@@ -62,7 +62,6 @@ private:
void mouseDoubleClickEvent(QMouseEvent *ev);
void deleteBreakpoints(const QModelIndexList &list);
void deleteBreakpoints(QList<int> rows);
void addBreakpoint();
void editBreakpoints(const QModelIndexList &list);
void associateBreakpoint(const QModelIndexList &list, int thread);
......
......@@ -183,7 +183,7 @@ DebuggerEngine *CdbEngine::create(const DebuggerStartParameters &sp,
void CdbEnginePrivate::updateCodeLevel()
{
const CdbCore::CoreEngine::CodeLevel cl = theDebuggerBoolSetting(OperateByInstruction) ?
const CdbCore::CoreEngine::CodeLevel cl = debuggerCore()->boolSetting(OperateByInstruction) ?
CdbCore::CoreEngine::CodeLevelAssembly : CdbCore::CoreEngine::CodeLevelSource;
setCodeLevel(cl);
}
......@@ -1174,7 +1174,7 @@ void CdbEngine::activateFrame(int frameIndex)
if (showAssembler) { // Assembly code: Clean out model and force instruction mode.
watchHandler()->beginCycle();
watchHandler()->endCycle();
QAction *assemblerAction = theDebuggerAction(OperateByInstruction);
QAction *assemblerAction = debuggerCore()->action(OperateByInstruction);
if (!assemblerAction->isChecked())
assemblerAction->trigger();
success = true;
......
......@@ -294,7 +294,7 @@ WatchHandleDumperInserter &WatchHandleDumperInserter::operator=(WatchData &wd)
// -----------CdbStackFrameContext
CdbStackFrameContext::CdbStackFrameContext(const QSharedPointer<CdbDumperHelper> &dumper,
CdbSymbolGroupContext *symbolContext) :
m_useDumpers(dumper->isEnabled() && theDebuggerBoolSetting(UseDebuggingHelpers)),
m_useDumpers(dumper->isEnabled() && debuggerCore()->boolSetting(UseDebuggingHelpers)),
m_dumper(dumper),
m_symbolContext(symbolContext)
{
......
......@@ -73,7 +73,7 @@ CdbStackTraceContext::createSymbolGroup(const CdbCore::ComInterfaces & /* cif */
// Exclude uninitialized variables if desired
QStringList uninitializedVariables;
const CdbCore::StackFrame &frame = stackFrameAt(index);
if (theDebuggerAction(UseCodeModel)->isChecked())
if (debuggerCore()->action(UseCodeModel)->isChecked())
getUninitializedVariables(debuggerCore()->cppCodeModelSnapshot(),
frame.function, frame.fileName, frame.line, &uninitializedVariables);
if (debug)
......
......@@ -319,7 +319,7 @@ CdbSymbolGroupContext::CdbSymbolGroupContext(const QString &prefix,
CdbCore::SymbolGroupContext(prefix, symbolGroup,
dumper->comInterfaces()->debugDataSpaces,
uninitializedVariables),
m_useDumpers(dumper->isEnabled() && theDebuggerBoolSetting(UseDebuggingHelpers)),
m_useDumpers(dumper->isEnabled() && debuggerCore()->boolSetting(UseDebuggingHelpers)),
m_dumper(dumper)
{
setShadowedNameFormat(WatchData::shadowedNameFormat());
......
......@@ -31,7 +31,7 @@
#include "logwindow.h"
#include "debuggeractions.h"
#include "debuggerconstants.h"
#include "debuggercore.h"
#include <QtCore/QDebug>
......@@ -188,15 +188,15 @@ public:
void contextMenuEvent(QContextMenuEvent *ev)