forked from qt-creator/qt-creator
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
#include "breakpoint.h"
|
||||
#include "debuggerprotocol.h"
|
||||
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/treemodel.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
@@ -38,203 +39,295 @@ namespace Utils { class ItemViewEvent; }
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
class LocationItem;
|
||||
class BreakpointItem;
|
||||
class BreakpointMarker;
|
||||
class BreakHandler;
|
||||
class DebuggerCommand;
|
||||
class DebuggerEngine;
|
||||
class BreakpointManager;
|
||||
class GlobalBreakpointMarker;
|
||||
|
||||
// Non-owning "deletion-safe" wrapper around a BreakpointItem *
|
||||
class Breakpoint
|
||||
class SubBreakpointItem : public QObject, public Utils::TypedTreeItem<Utils::TreeItem, BreakpointItem>
|
||||
{
|
||||
public:
|
||||
QVariant data(int column, int role) const final;
|
||||
|
||||
BreakpointItem *breakpoint() const { return Utils::TypedTreeItem<Utils::TreeItem, BreakpointItem>::parent(); }
|
||||
void setParameters(const BreakpointParameters &pars) { params = pars; }
|
||||
BreakpointParameters params;
|
||||
QString responseId; //!< Breakpoint number assigned by the debugger engine.
|
||||
QString displayName; //!< Breakpoint number assigned by the debugger engine.
|
||||
};
|
||||
|
||||
using SubBreakpoint = QPointer<SubBreakpointItem>;
|
||||
|
||||
class GlobalBreakpointItem : public QObject, public Utils::TreeItem
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(Debugger::Internal::BreakHandler)
|
||||
|
||||
public:
|
||||
Breakpoint() = default;
|
||||
|
||||
bool isValid() const;
|
||||
operator const void *() const { return isValid() ? this : nullptr; }
|
||||
bool operator!() const { return !isValid(); }
|
||||
|
||||
uint hash() const;
|
||||
|
||||
const BreakpointParameters ¶meters() const;
|
||||
void addToCommand(DebuggerCommand *cmd) const;
|
||||
|
||||
BreakpointModelId id() const;
|
||||
bool isLocatedAt(const QString &fileName, int lineNumber,
|
||||
bool useMarkerPosition) const;
|
||||
explicit GlobalBreakpointItem();
|
||||
~GlobalBreakpointItem() override;
|
||||
|
||||
QVariant data(int column, int role) const override;
|
||||
QIcon icon() const;
|
||||
BreakpointState state() const;
|
||||
void setEngine(DebuggerEngine *engine);
|
||||
|
||||
// State transitions.
|
||||
void notifyBreakpointChangeAfterInsertNeeded();
|
||||
void notifyBreakpointInsertProceeding();
|
||||
void notifyBreakpointInsertOk();
|
||||
void notifyBreakpointInsertFailed();
|
||||
void notifyBreakpointChangeOk();
|
||||
void notifyBreakpointChangeProceeding();
|
||||
void notifyBreakpointChangeFailed();
|
||||
void notifyBreakpointPending();
|
||||
void notifyBreakpointRemoveProceeding();
|
||||
void notifyBreakpointRemoveOk();
|
||||
void notifyBreakpointRemoveFailed();
|
||||
void notifyBreakpointReleased();
|
||||
void notifyBreakpointNeedsReinsertion();
|
||||
void notifyBreakpointAdjusted(const BreakpointParameters ¶ms);
|
||||
void deleteBreakpoint();
|
||||
void removeBreakpointFromModel();
|
||||
|
||||
void update();
|
||||
void updateLineNumber(int lineNumber);
|
||||
void updateFileName(const Utils::FileName &fileName);
|
||||
|
||||
void gotoLocation() const;
|
||||
bool isLocatedAt(const QString &fileName, int lineNumber, bool useMarkerPosition) const;
|
||||
|
||||
// Getter retrieves property value.
|
||||
// Setter sets property value and triggers update if changed.
|
||||
// Only use setters when it is safe to assume that the breakpoint still
|
||||
// exist. That's not the case if the event loop could run after you
|
||||
// obtained the BreakpointItem pointer.
|
||||
BreakpointPathUsage pathUsage() const;
|
||||
void setPathUsage(const BreakpointPathUsage &u);
|
||||
QString condition() const;
|
||||
void setCondition(const QString &condition);
|
||||
int ignoreCount() const;
|
||||
void setIgnoreCount(const int &count);
|
||||
int threadSpec() const;
|
||||
void setThreadSpec(const int &spec);
|
||||
QString fileName() const;
|
||||
void setFileName(const QString &fileName);
|
||||
QString functionName() const;
|
||||
void setFunctionName(const QString &functionName);
|
||||
QString expression() const;
|
||||
void setExpression(const QString &expression);
|
||||
QString message() const;
|
||||
QString command() const;
|
||||
void setCommand(const QString &command);
|
||||
void setMessage(const QString &m);
|
||||
BreakpointType type() const;
|
||||
void setType(const BreakpointType &type);
|
||||
quint64 address() const;
|
||||
void setAddress(const quint64 &address);
|
||||
int lineNumber() const;
|
||||
void changeBreakpointData(const BreakpointParameters &data);
|
||||
bool isEnabled() const;
|
||||
void setEnabled(bool on) const;
|
||||
void updateFileNameFromMarker(const QString &fileName);
|
||||
void updateLineNumberFromMarker(int lineNumber);
|
||||
void changeLineNumberFromMarker(int lineNumber);
|
||||
void setMarkerFileAndLine(const QString &fileName, int lineNumber);
|
||||
bool isWatchpoint() const;
|
||||
bool isTracepoint() const;
|
||||
void setTracepoint(bool on);
|
||||
DebuggerEngine *engine() const;
|
||||
const BreakpointResponse &response() const;
|
||||
void setResponse(const BreakpointResponse &data);
|
||||
bool needsChange() const;
|
||||
bool needsChildren() const;
|
||||
QString displayName() const;
|
||||
QString markerFileName() const;
|
||||
QString toolTip() const;
|
||||
int markerLineNumber() const;
|
||||
int modelId() const;
|
||||
|
||||
bool isOneShot() const;
|
||||
void insertSubBreakpoint(const BreakpointResponse &data);
|
||||
void removeAlienBreakpoint();
|
||||
void removeBreakpoint() const;
|
||||
bool isEnabled() const { return m_params.enabled; }
|
||||
void setEnabled(bool enabled);
|
||||
|
||||
QString msgWatchpointByAddressTriggered(int number, quint64 address) const;
|
||||
QString msgWatchpointByAddressTriggered(
|
||||
int number, quint64 address, const QString &threadId) const;
|
||||
QString msgWatchpointByExpressionTriggered(int number, const QString &expr) const;
|
||||
QString msgWatchpointByExpressionTriggered(
|
||||
int number, const QString &expr, const QString &threadId) const;
|
||||
QString msgBreakpointTriggered(int number, const QString &threadId) const;
|
||||
const BreakpointParameters ¶meters() const { return m_params; }
|
||||
|
||||
private:
|
||||
void gotoState(BreakpointState target, BreakpointState assumedCurrent);
|
||||
|
||||
friend class BreakHandler;
|
||||
explicit Breakpoint(BreakpointItem *b);
|
||||
friend class BreakpointManager;
|
||||
friend class BreakpointMarker;
|
||||
friend class GlobalBreakpointMarker;
|
||||
|
||||
QPointer<BreakpointItem> b;
|
||||
void updateMarker();
|
||||
void updateMarkerIcon();
|
||||
void destroyMarker();
|
||||
void scheduleSynchronization();
|
||||
QPointer<DebuggerEngine> usingEngine() const;
|
||||
|
||||
bool isEngineRunning() const;
|
||||
|
||||
const int m_modelId;
|
||||
BreakpointParameters m_params;
|
||||
GlobalBreakpointMarker *m_marker = nullptr; // The primary marker set by the user.
|
||||
};
|
||||
|
||||
inline uint qHash(const Debugger::Internal::Breakpoint &b) { return b.hash(); }
|
||||
using GlobalBreakpoint = QPointer<GlobalBreakpointItem>;
|
||||
using GlobalBreakpoints = QList<GlobalBreakpoint>;
|
||||
|
||||
using Breakpoints = QList<Breakpoint>;
|
||||
class BreakpointItem : public QObject, public Utils::TypedTreeItem<SubBreakpointItem>
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(Debugger::Internal::BreakHandler)
|
||||
|
||||
using BreakModel = Utils::TreeModel<Utils::TypedTreeItem<BreakpointItem>, BreakpointItem, LocationItem>;
|
||||
public:
|
||||
explicit BreakpointItem(const GlobalBreakpoint &gbp);
|
||||
~BreakpointItem() final;
|
||||
|
||||
class BreakHandler : public BreakModel
|
||||
QVariant data(int column, int role) const final;
|
||||
|
||||
QIcon icon() const;
|
||||
|
||||
void setMarkerFileAndLine(const QString &fileName, int lineNumber);
|
||||
bool needsChange() const;
|
||||
|
||||
SubBreakpoint findOrCreateSubBreakpoint(const QString &responseId);
|
||||
QString markerFileName() const;
|
||||
int markerLineNumber() const;
|
||||
|
||||
const BreakpointParameters &requestedParameters() const;
|
||||
void addToCommand(DebuggerCommand *cmd) const;
|
||||
void updateFromGdbOutput(const GdbMi &bkpt);
|
||||
|
||||
int modelId() const;
|
||||
QString responseId() const { return m_responseId; }
|
||||
QString displayName() const { return m_displayName; }
|
||||
QString toolTip() const;
|
||||
QString shortToolTip() const;
|
||||
BreakpointState state() const { return m_state; }
|
||||
BreakpointType type() const { return m_parameters.type; }
|
||||
BreakpointPathUsage pathUsage() const;
|
||||
const BreakpointParameters parameters() const { return m_parameters; }
|
||||
|
||||
QString condition() const { return m_parameters.condition; }
|
||||
int ignoreCount() const { return m_parameters.ignoreCount; }
|
||||
int threadSpec() const { return m_parameters.threadSpec; }
|
||||
QString fileName() const { return m_parameters.fileName; }
|
||||
QString functionName() const { return m_parameters.functionName; }
|
||||
QString expression() const { return m_parameters.expression; }
|
||||
QString message() const { return m_parameters.message; }
|
||||
QString command() const { return m_parameters.command; }
|
||||
quint64 address() const { return m_parameters.address; }
|
||||
int lineNumber() const { return m_parameters.lineNumber; }
|
||||
bool isEnabled() const { return m_parameters.enabled; }
|
||||
bool isWatchpoint() const { return m_parameters.isWatchpoint(); }
|
||||
bool isTracepoint() const { return m_parameters.isTracepoint(); }
|
||||
bool isOneShot() const { return m_parameters.oneShot; }
|
||||
bool isPending() const { return m_parameters.pending; }
|
||||
|
||||
void setLineNumber(int lineNumber) { m_parameters.lineNumber = lineNumber; }
|
||||
void setFileName(const QString &fileName) { m_parameters.fileName = fileName; }
|
||||
void setFunctionName(const QString &functionName) { m_parameters.functionName = functionName; }
|
||||
void setPending(bool pending);
|
||||
void setResponseId(const QString &str) { m_responseId = str; }
|
||||
void setDisplayName(const QString &name) { m_displayName = name; }
|
||||
void setParameters(const BreakpointParameters &value);
|
||||
void setAddress(quint64 address) { m_parameters.address = address; }
|
||||
void setEnabled(bool on);
|
||||
void setHitCount(int hitCount) { m_parameters.hitCount = hitCount; }
|
||||
void setThreadSpec(int threadSpec) { m_parameters.threadSpec = threadSpec; }
|
||||
void setIgnoreCount(int count) { m_parameters.ignoreCount = count; }
|
||||
void setCommand(const QString &command) { m_parameters.command = command; }
|
||||
void setCondition(const QString &condition) { m_parameters.condition = condition; }
|
||||
|
||||
QString msgWatchpointByAddressTriggered(quint64 address) const;
|
||||
QString msgWatchpointByAddressTriggered(quint64 address, const QString &threadId) const;
|
||||
QString msgWatchpointByExpressionTriggered(const QString &expr) const;
|
||||
QString msgWatchpointByExpressionTriggered(const QString &expr, const QString &threadId) const;
|
||||
QString msgBreakpointTriggered(const QString &threadId) const;
|
||||
|
||||
friend class BreakpointManager;
|
||||
friend class BreakHandler;
|
||||
friend class DebuggerEngine;
|
||||
|
||||
void adjustMarker();
|
||||
|
||||
void deleteBreakpoint();
|
||||
void deleteGlobalOrThisBreakpoint();
|
||||
|
||||
void updateLineNumber(int lineNumber);
|
||||
void updateFileName(const Utils::FileName &fileName);
|
||||
|
||||
const GlobalBreakpoint globalBreakpoint() const;
|
||||
void gotoState(BreakpointState target, BreakpointState assumedCurrent);
|
||||
|
||||
private:
|
||||
void destroyMarker();
|
||||
void updateMarker();
|
||||
void updateMarkerIcon();
|
||||
void setState(BreakpointState state);
|
||||
|
||||
const GlobalBreakpoint m_globalBreakpoint; // Origin, or null for aliens.
|
||||
BreakpointParameters m_requestedParameters; // May differ from global value over lifetime of breakpoint.
|
||||
BreakpointParameters m_parameters;
|
||||
BreakpointState m_state = BreakpointNew; // Current state of breakpoint.
|
||||
BreakpointMarker *m_marker = nullptr;
|
||||
QString m_responseId; //!< Breakpoint number or id assigne by or used in the debugger backend.
|
||||
QString m_displayName;
|
||||
};
|
||||
|
||||
using Breakpoint = QPointer<BreakpointItem>;
|
||||
using Breakpoints = const QList<Breakpoint>;
|
||||
using SubBreakpoints = const QList<SubBreakpoint>;
|
||||
|
||||
using BreakHandlerModel = Utils::TreeModel<Utils::TypedTreeItem<BreakpointItem>, BreakpointItem, SubBreakpointItem>;
|
||||
using BreakpointManagerModel = Utils::TreeModel<Utils::TypedTreeItem<GlobalBreakpointItem>, GlobalBreakpointItem>;
|
||||
|
||||
inline uint qHash(const Debugger::Internal::SubBreakpoint &b) { return qHash(b.data()); }
|
||||
inline uint qHash(const Debugger::Internal::Breakpoint &b) { return qHash(b.data()); }
|
||||
inline uint qHash(const Debugger::Internal::GlobalBreakpoint &b) { return qHash(b.data()); }
|
||||
|
||||
class BreakHandler : public BreakHandlerModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
BreakHandler();
|
||||
explicit BreakHandler(DebuggerEngine *engine);
|
||||
|
||||
QAbstractItemModel *model() { return this; }
|
||||
const Breakpoints breakpoints() const;
|
||||
|
||||
void loadSessionData();
|
||||
void saveSessionData();
|
||||
|
||||
QAbstractItemModel *model() { return this; }
|
||||
bool tryClaimBreakpoint(const GlobalBreakpoint &gbp);
|
||||
void releaseAllBreakpoints();
|
||||
|
||||
// The only way to add a new breakpoint.
|
||||
void appendBreakpoint(const BreakpointParameters &data);
|
||||
void handleAlienBreakpoint(const BreakpointResponse &response, DebuggerEngine *engine);
|
||||
void handleAlienBreakpoint(const QString &responseId, const BreakpointParameters &response);
|
||||
void removeAlienBreakpoint(const QString &responseId);
|
||||
void requestBreakpointInsertion(const Breakpoint &bp);
|
||||
void requestBreakpointUpdate(const Breakpoint &bp);
|
||||
void requestBreakpointRemoval(const Breakpoint &bp);
|
||||
void requestBreakpointEnabling(const Breakpoint &bp, bool enabled);
|
||||
void requestSubBreakpointEnabling(const SubBreakpoint &sbp, bool enabled);
|
||||
|
||||
const Breakpoints allBreakpoints() const;
|
||||
const Breakpoints engineBreakpoints(DebuggerEngine *engine) const;
|
||||
const Breakpoints unclaimedBreakpoints() const;
|
||||
QStringList engineBreakpointPaths(DebuggerEngine *engine) const;
|
||||
void removeBreakpoint(const Breakpoint &bp);
|
||||
void editBreakpoint(const Breakpoint &bp, QWidget *parent);
|
||||
|
||||
// Find a breakpoint matching approximately the data in needle.
|
||||
Breakpoint findSimilarBreakpoint(const BreakpointResponse &needle) const;
|
||||
Breakpoint findBreakpointByResponseId(const BreakpointResponseId &resultId) const;
|
||||
Breakpoint findBreakpointByResponseId(const QString &responseId) const;
|
||||
SubBreakpoint findSubBreakpointByResponseId(const QString &responseId) const;
|
||||
Breakpoint findWatchpoint(const BreakpointParameters &data) const;
|
||||
Breakpoint findBreakpointByFunction(const QString &functionName) const;
|
||||
Breakpoint findBreakpointByIndex(const QModelIndex &index) const;
|
||||
Breakpoints findBreakpointsByIndex(const QList<QModelIndex> &list) const;
|
||||
void updateMarkers();
|
||||
Breakpoint findBreakpointByModelId(int modelId) const;
|
||||
|
||||
Breakpoint findBreakpointByFileAndLine(const QString &fileName,
|
||||
int lineNumber, bool useMarkerPosition = true);
|
||||
Breakpoint findBreakpointByAddress(quint64 address) const;
|
||||
|
||||
void breakByFunction(const QString &functionName);
|
||||
static QString displayFromThreadSpec(int spec);
|
||||
static int threadSpecFromDisplay(const QString &str);
|
||||
|
||||
// Convenience.
|
||||
void setWatchpointAtAddress(quint64 address, unsigned size);
|
||||
void setWatchpointAtExpression(const QString &exp);
|
||||
void setBreakpointEnabled(const Breakpoint &bp, bool on);
|
||||
|
||||
Breakpoint breakpointById(BreakpointModelId id) const;
|
||||
void editBreakpoint(Breakpoint bp, QWidget *parent);
|
||||
void updateDisassemblerMarker(const Breakpoint &bp);
|
||||
void removeDisassemblerMarker(const Breakpoint &bp);
|
||||
|
||||
private:
|
||||
QVariant data(const QModelIndex &idx, int role) const override;
|
||||
bool setData(const QModelIndex &idx, const QVariant &value, int role) override;
|
||||
void timerEvent(QTimerEvent *event) override;
|
||||
Breakpoint findBreakpointByIndex(const QModelIndex &index) const;
|
||||
Breakpoints findBreakpointsByIndex(const QList<QModelIndex> &list) const;
|
||||
SubBreakpoint findSubBreakpointByIndex(const QModelIndex &index) const;
|
||||
SubBreakpoints findSubBreakpointsByIndex(const QList<QModelIndex> &list) const;
|
||||
void editBreakpoints(const Breakpoints &bps, QWidget *parent);
|
||||
|
||||
void gotoState(Breakpoint bp, BreakpointState target, BreakpointState assumedCurrent);
|
||||
void gotoLocation(const Breakpoint &bp) const;
|
||||
|
||||
QVariant data(const QModelIndex &idx, int role) const final;
|
||||
bool setData(const QModelIndex &idx, const QVariant &value, int role) final;
|
||||
|
||||
bool contextMenuEvent(const Utils::ItemViewEvent &ev);
|
||||
|
||||
friend class BreakpointItem;
|
||||
friend class Breakpoint;
|
||||
|
||||
void loadBreakpoints();
|
||||
void saveBreakpoints();
|
||||
DebuggerEngine * const m_engine;
|
||||
};
|
||||
|
||||
void appendBreakpointInternal(const BreakpointParameters &data);
|
||||
void deleteBreakpoints(const Breakpoints &bps);
|
||||
void deleteAllBreakpoints();
|
||||
void setBreakpointsEnabled(const Breakpoints &bps, bool enabled);
|
||||
void addBreakpoint();
|
||||
void editBreakpoints(const Breakpoints &bps, QWidget *parent);
|
||||
class BreakpointManager : public BreakpointManagerModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_SLOT void changeLineNumberFromMarkerHelper(Debugger::Internal::BreakpointModelId id);
|
||||
Q_SLOT void deletionHelper(Debugger::Internal::BreakpointModelId id);
|
||||
public:
|
||||
BreakpointManager();
|
||||
|
||||
void scheduleSynchronization();
|
||||
static QAbstractItemModel *model();
|
||||
|
||||
int m_syncTimerId;
|
||||
static const GlobalBreakpoints globalBreakpoints();
|
||||
static void loadSessionData();
|
||||
static void saveSessionData();
|
||||
static void aboutToUnloadSession();
|
||||
|
||||
static GlobalBreakpoint createBreakpoint(const BreakpointParameters &data);
|
||||
|
||||
static GlobalBreakpoint findBreakpointByLocation(const ContextData &location);
|
||||
// Find a breakpoint matching approximately the data in needle.
|
||||
static GlobalBreakpoint findSimilarBreakpoint(const BreakpointParameters &needle);
|
||||
static GlobalBreakpoint findWatchpoint(const BreakpointParameters &data);
|
||||
static GlobalBreakpoint findBreakpointByFunction(const QString &functionName);
|
||||
|
||||
static void claimBreakpointsForEngine(DebuggerEngine *engine);
|
||||
static void toggleBreakpoint(const ContextData &location, const QString &tracePointMessage = QString());
|
||||
static void createBreakpointForEngine(const BreakpointParameters &data, DebuggerEngine *engine);
|
||||
|
||||
static void executeAddBreakpointDialog();
|
||||
static void executeDeleteAllBreakpointsDialog();
|
||||
|
||||
private:
|
||||
static GlobalBreakpoint createBreakpointHelper(const BreakpointParameters &data);
|
||||
static GlobalBreakpoint findBreakpointByIndex(const QModelIndex &index);
|
||||
static GlobalBreakpoints findBreakpointsByIndex(const QList<QModelIndex> &list);
|
||||
|
||||
QVariant data(const QModelIndex &idx, int role) const final;
|
||||
bool setData(const QModelIndex &idx, const QVariant &value, int role) final;
|
||||
|
||||
bool contextMenuEvent(const Utils::ItemViewEvent &ev);
|
||||
void gotoLocation(const GlobalBreakpoint &gbp) const;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
Q_DECLARE_METATYPE(Debugger::Internal::Breakpoint)
|
||||
|
||||
Reference in New Issue
Block a user