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:
hjk
2018-07-31 12:30:48 +02:00
parent d6911fd10c
commit 3b5ecac238
58 changed files with 6543 additions and 6100 deletions

View File

@@ -282,12 +282,12 @@ ClangTidyClazyTool::ClangTidyClazyTool()
action->setEnabled(m_startAction->isEnabled());
});
perspective->addToolbarAction(m_startAction);
perspective->addToolbarAction(m_stopAction);
perspective->addToolbarAction(m_goBack);
perspective->addToolbarAction(m_goNext);
perspective->addToolbarWidget(m_filterLineEdit);
perspective->addToolbarWidget(m_applyFixitsButton);
perspective->addToolBarAction(m_startAction);
perspective->addToolBarAction(m_stopAction);
perspective->addToolBarAction(m_goBack);
perspective->addToolBarAction(m_goNext);
perspective->addToolBarWidget(m_filterLineEdit);
perspective->addToolBarWidget(m_applyFixitsButton);
Debugger::registerPerspective(perspective);

View File

@@ -32,9 +32,8 @@
#include <projectexplorer/runconfiguration.h>
#include <QWidget>
#include <QCoreApplication>
#include <QWidget>
#include <functional>
@@ -63,14 +62,11 @@ DEBUGGER_EXPORT void showCannotStartDialog(const QString &toolName);
// Register a tool for a given start mode.
DEBUGGER_EXPORT void registerPerspective(Utils::Perspective *perspective);
DEBUGGER_EXPORT void destroyDynamicPerspective(const QByteArray &perspectiveId);
DEBUGGER_EXPORT void setPerspectiveEnabled(const QByteArray &perspectiveId, bool enable);
DEBUGGER_EXPORT void enableMainWindow(bool on);
DEBUGGER_EXPORT QWidget *mainWindow();
DEBUGGER_EXPORT void selectPerspective(const QByteArray &perspectiveId);
DEBUGGER_EXPORT QByteArray currentPerspective();
// Convenience functions.
DEBUGGER_EXPORT void showStatusMessage(const QString &message, int timeoutMS = 10000);

File diff suppressed because it is too large Load Diff

View File

@@ -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 &parameters() 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 &params);
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 &parameters() 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)

View File

@@ -25,89 +25,20 @@
#include "breakpoint.h"
#include "debuggeractions.h"
#include "debuggercore.h"
#include "debuggerprotocol.h"
#include <projectexplorer/abi.h>
#include <utils/qtcassert.h>
#include <QDebug>
#include <QFileInfo>
#include <QDir>
namespace Debugger {
namespace Internal {
/*!
\class Debugger::Internal::BreakpointIdBase
Convenience base class for BreakpointModelId and
BreakpointResponseId.
*/
QDebug operator<<(QDebug d, const BreakpointIdBase &id)
{
d << qPrintable(id.toString());
return d;
}
QString BreakpointIdBase::toString() const
{
if (!isValid())
return QLatin1String("<invalid bkpt>");
if (isMinor())
return QString("%1.%2").arg(m_majorPart).arg(m_minorPart);
return QString::number(m_majorPart);
}
/*!
\class Debugger::Internal::BreakpointModelId
This identifies a breakpoint in the \c BreakHandler. The
major parts are strictly increasing over time.
The minor part identifies a multiple breakpoint
set for example by gdb in constructors.
*/
BreakpointModelId::BreakpointModelId(const QString &ba)
{
int pos = ba.indexOf('\'');
if (pos == -1) {
m_majorPart = ba.toUShort();
m_minorPart = 0;
} else {
m_majorPart = ba.left(pos).toUShort();
m_minorPart = ba.mid(pos + 1).toUShort();
}
}
/*!
\class Debugger::Internal::BreakpointResponseId
This is what the external debuggers use to identify a breakpoint.
It is only valid for one debugger run.
In gdb, the breakpoint number is used, which is constant
during a session. CDB's breakpoint numbers vary if breakpoints
are deleted, so, the ID is used.
*/
BreakpointResponseId::BreakpointResponseId(const QString &ba)
{
int pos = ba.indexOf('.');
if (pos == -1) {
m_majorPart = ba.toInt();
m_minorPart = 0;
} else {
m_majorPart = ba.left(pos).toInt();
m_minorPart = ba.mid(pos + 1).toInt();
}
}
//////////////////////////////////////////////////////////////////
//
// BreakpointParameters
//
//////////////////////////////////////////////////////////////////
/*!
\class Debugger::Internal::BreakpointParameters
@@ -215,25 +146,32 @@ void BreakpointParameters::updateLocation(const QString &location)
}
}
bool BreakpointParameters::isCppBreakpoint() const
bool BreakpointParameters::isQmlFileAndLineBreakpoint() const
{
// Qml specific breakpoint types.
if (type == BreakpointAtJavaScriptThrow
|| type == BreakpointOnQmlSignalEmit)
if (type != BreakpointByFileAndLine)
return false;
// Qml is currently only file.
if (type == BreakpointByFileAndLine) {
auto qmlExtensionString = QString::fromLocal8Bit(qgetenv("QTC_QMLDEBUGGER_FILEEXTENSIONS"));
QString qmlExtensionString = QString::fromLocal8Bit(qgetenv("QTC_QMLDEBUGGER_FILEEXTENSIONS"));
if (qmlExtensionString.isEmpty())
qmlExtensionString = ".qml;.js";
const auto qmlFileExtensions = qmlExtensionString.splitRef(';', QString::SkipEmptyParts);
for (QStringRef extension : qmlFileExtensions) {
if (fileName.endsWith(extension, Qt::CaseInsensitive))
return true;
}
return false;
}
}
}
bool BreakpointParameters::isCppBreakpoint() const
{
// Qml specific breakpoint types.
if (type == BreakpointAtJavaScriptThrow || type == BreakpointOnQmlSignalEmit)
return false;
// Qml is currently only file.
if (type == BreakpointByFileAndLine)
return !isQmlFileAndLineBreakpoint();
return true;
}
@@ -283,55 +221,176 @@ QString BreakpointParameters::toString() const
ts << " Command: " << command;
if (!message.isEmpty())
ts << " Message: " << message;
return result;
}
//////////////////////////////////////////////////////////////////
//
// BreakpointResponse
//
//////////////////////////////////////////////////////////////////
/*!
\class Debugger::Internal::BreakpointResponse
This is what debuggers produce in response to the attempt to
insert a breakpoint. The data might differ from the requested bits.
*/
BreakpointResponse::BreakpointResponse()
{
pending = true;
hitCount = 0;
multiple = false;
correctedLineNumber = 0;
}
QString BreakpointResponse::toString() const
{
QString result = BreakpointParameters::toString();
QTextStream ts(&result);
ts << " Number: " << id.toString();
if (pending)
ts << " [pending]";
if (!functionName.isEmpty())
ts << " Function: " << functionName;
if (multiple)
ts << " Multiple: " << multiple;
if (correctedLineNumber)
ts << " CorrectedLineNumber: " << correctedLineNumber;
ts << " Hit: " << hitCount << " times";
ts << ' ';
return result + BreakpointParameters::toString();
return result;
}
void BreakpointResponse::fromParameters(const BreakpointParameters &p)
static QString cleanupFullName(const QString &fileName)
{
BreakpointParameters::operator=(p);
id = BreakpointResponseId();
multiple = false;
correctedLineNumber = 0;
hitCount = 0;
QString cleanFilePath = fileName;
// Gdb running on windows often delivers "fullnames" which
// (a) have no drive letter and (b) are not normalized.
if (ProjectExplorer::Abi::hostAbi().os() == ProjectExplorer::Abi::WindowsOS) {
if (fileName.isEmpty())
return QString();
QFileInfo fi(fileName);
if (fi.isReadable())
cleanFilePath = QDir::cleanPath(fi.absoluteFilePath());
}
// if (!boolSetting(AutoEnrichParameters))
// return cleanFilePath;
// const QString sysroot = runParameters().sysRoot;
// if (QFileInfo(cleanFilePath).isReadable())
// return cleanFilePath;
// if (!sysroot.isEmpty() && fileName.startsWith('/')) {
// cleanFilePath = sysroot + fileName;
// if (QFileInfo(cleanFilePath).isReadable())
// return cleanFilePath;
// }
// if (m_baseNameToFullName.isEmpty()) {
// QString debugSource = sysroot + "/usr/src/debug";
// if (QFileInfo(debugSource).isDir()) {
// QDirIterator it(debugSource, QDirIterator::Subdirectories);
// while (it.hasNext()) {
// it.next();
// QString name = it.fileName();
// if (!name.startsWith('.')) {
// QString path = it.filePath();
// m_baseNameToFullName.insert(name, path);
// }
// }
// }
// }
// cleanFilePath.clear();
// const QString base = FileName::fromString(fileName).fileName();
// QMap<QString, QString>::const_iterator jt = m_baseNameToFullName.constFind(base);
// while (jt != m_baseNameToFullName.constEnd() && jt.key() == base) {
// // FIXME: Use some heuristics to find the "best" match.
// return jt.value();
// //++jt;
// }
return cleanFilePath;
}
void BreakpointParameters::updateFromGdbOutput(const GdbMi &bkpt)
{
QTC_ASSERT(bkpt.isValid(), return);
QString originalLocation;
QString file;
QString fullName;
QString internalId;
enabled = true;
pending = false;
condition.clear();
for (const GdbMi &child : bkpt.children()) {
if (child.hasName("number")) {
// Handled on caller side.
} else if (child.hasName("func")) {
functionName = child.data();
} else if (child.hasName("addr")) {
// <MULTIPLE> happens in constructors, inline functions, and
// at other places like 'foreach' lines. In this case there are
// fields named "addr" in the response and/or the address
// is called <MULTIPLE>.
//qDebug() << "ADDR: " << child.data() << (child.data() == "<MULTIPLE>");
if (child.data().startsWith("0x"))
address = child.toAddress();
} else if (child.hasName("file")) {
file = child.data();
} else if (child.hasName("fullname")) {
fullName = child.data();
} else if (child.hasName("line")) {
lineNumber = child.toInt();
} else if (child.hasName("cond")) {
// gdb 6.3 likes to "rewrite" conditions. Just accept that fact.
condition = child.data();
} else if (child.hasName("enabled")) {
enabled = (child.data() == "y");
} else if (child.hasName("disp")) {
oneShot = child.data() == "del";
} else if (child.hasName("pending")) {
// Any content here would be interesting only if we did accept
// spontaneously appearing breakpoints (user using gdb commands).
if (file.isEmpty())
file = child.data();
pending = true;
} else if (child.hasName("at")) {
// Happens with gdb 6.4 symbianelf.
QString ba = child.data();
if (ba.startsWith('<') && ba.endsWith('>'))
ba = ba.mid(1, ba.size() - 2);
functionName = ba;
} else if (child.hasName("thread")) {
threadSpec = child.toInt();
} else if (child.hasName("type")) {
// "breakpoint", "hw breakpoint", "tracepoint", "hw watchpoint"
// {bkpt={number="2",type="hw watchpoint",disp="keep",enabled="y",
// what="*0xbfffed48",times="0",original-location="*0xbfffed48"}}
if (child.data().contains("tracepoint")) {
tracepoint = true;
} else if (child.data() == "hw watchpoint" || child.data() == "watchpoint") {
QString what = bkpt["what"].data();
if (what.startsWith("*0x")) {
type = WatchpointAtAddress;
address = what.mid(1).toULongLong(0, 0);
} else {
type = WatchpointAtExpression;
expression = what;
}
} else if (child.data() == "breakpoint") {
QString catchType = bkpt["catch-type"].data();
if (catchType == "throw")
type = BreakpointAtThrow;
else if (catchType == "catch")
type = BreakpointAtCatch;
else if (catchType == "fork")
type = BreakpointAtFork;
else if (catchType == "exec")
type = BreakpointAtExec;
else if (catchType == "syscall")
type = BreakpointAtSysCall;
}
} else if (child.hasName("times")) {
hitCount = child.toInt();
} else if (child.hasName("original-location")) {
originalLocation = child.data();
}
// This field is not present. Contents needs to be parsed from
// the plain "ignore"
//else if (child.hasName("ignore"))
// ignoreCount = child.data();
}
QString name;
if (!fullName.isEmpty()) {
name = cleanupFullName(fullName);
fileName = name;
//if (data->markerFileName().isEmpty())
// data->setMarkerFileName(name);
} else {
name = file;
// Use fullName() once we have a mapping which is more complete than
// gdb's own. No point in assigning markerFileName for now.
}
if (!name.isEmpty())
fileName = name;
if (fileName.isEmpty())
updateLocation(originalLocation);
}
} // namespace Internal

View File

@@ -31,52 +31,7 @@
namespace Debugger {
namespace Internal {
//////////////////////////////////////////////////////////////////
//
// BreakpointIds
//
//////////////////////////////////////////////////////////////////
class BreakpointIdBase
{
public:
BreakpointIdBase() = default;
bool isValid() const { return m_majorPart != 0; }
bool isMajor() const { return m_majorPart != 0 && m_minorPart == 0; }
bool isMinor() const { return m_majorPart != 0 && m_minorPart != 0; }
bool operator!() const { return !isValid(); }
operator const void*() const { return isValid() ? this : nullptr; }
quint32 toInternalId() const { return m_majorPart | (m_minorPart << 16); }
QString toString() const;
bool operator==(const BreakpointIdBase &id) const
{ return m_majorPart == id.m_majorPart && m_minorPart == id.m_minorPart; }
quint16 majorPart() const { return m_majorPart; }
quint16 minorPart() const { return m_minorPart; }
protected:
quint16 m_majorPart = 0;
quint16 m_minorPart = 0;
};
class BreakpointModelId : public BreakpointIdBase
{
public:
BreakpointModelId() { m_majorPart = m_minorPart = 0; }
explicit BreakpointModelId(quint16 ma) { m_majorPart = ma; m_minorPart = 0; }
BreakpointModelId(quint16 ma, quint16 mi) { m_majorPart = ma; m_minorPart = mi; }
explicit BreakpointModelId(const QString &ba); // "21.2"
};
class BreakpointResponseId : public BreakpointIdBase
{
public:
BreakpointResponseId() { m_majorPart = m_minorPart = 0; }
explicit BreakpointResponseId(quint16 ma) { m_majorPart = ma; m_minorPart = 0; }
BreakpointResponseId(quint16 ma, quint16 mi) { m_majorPart = ma; m_minorPart = mi; }
explicit BreakpointResponseId(const QString &ba); // "21.2"
};
class GdbMi;
//////////////////////////////////////////////////////////////////
//
@@ -86,7 +41,7 @@ public:
//! \enum Debugger::Internal::BreakpointType
// Note: Keep synchronized with similar definitions in bridge.py
// Note: Keep synchronized with similar definitions in dumper.py
enum BreakpointType
{
UnknownBreakpointType,
@@ -110,11 +65,11 @@ enum BreakpointType
enum BreakpointState
{
BreakpointNew,
BreakpointInsertRequested, //!< Inferior was told about bp, not ack'ed.
BreakpointInsertProceeding,
BreakpointChangeRequested,
BreakpointChangeProceeding,
BreakpointInsertionRequested, //!< Inferior was told about bp, not ack'ed.
BreakpointInsertionProceeding,
BreakpointInserted,
BreakpointUpdateRequested,
BreakpointUpdateProceeding,
BreakpointRemoveRequested,
BreakpointRemoveProceeding,
BreakpointDead
@@ -185,12 +140,15 @@ public:
bool conditionsMatch(const QString &other) const;
bool isWatchpoint() const
{ return type == WatchpointAtAddress || type == WatchpointAtExpression; }
bool isLocatedAt(const QString &fileName, int lineNumber, const QString &markerFileName) const;
// Enough for now.
bool isBreakpoint() const { return !isWatchpoint() && !isTracepoint(); }
bool isTracepoint() const { return tracepoint; }
bool isCppBreakpoint() const;
bool isQmlFileAndLineBreakpoint() const;
QString toString() const;
void updateLocation(const QString &location); // file.cpp:42
void updateFromGdbOutput(const GdbMi &bkpt);
bool operator==(const BreakpointParameters &p) const { return equals(p); }
bool operator!=(const BreakpointParameters &p) const { return !equals(p); }
@@ -214,31 +172,10 @@ public:
QString message; //!< message
bool tracepoint;
bool oneShot; //!< Should this breakpoint trigger only once?
bool pending = true; //!< Breakpoint not fully resolved.
int hitCount = 0; //!< Number of times this has been hit.
};
class BreakpointResponse : public BreakpointParameters
{
public:
BreakpointResponse();
QString toString() const;
public:
void fromParameters(const BreakpointParameters &p);
BreakpointResponseId id; //!< Breakpoint number assigned by the debugger engine.
bool pending; //!< Breakpoint not fully resolved.
int hitCount; //!< Number of times this has been hit.
bool multiple; //!< Happens in constructors/gdb.
int correctedLineNumber; //!< Line number as seen by gdb.
};
inline uint qHash(const Debugger::Internal::BreakpointModelId &id)
{
return id.toInternalId();
}
} // namespace Internal
} // namespace Debugger
Q_DECLARE_METATYPE(Debugger::Internal::BreakpointModelId)
Q_DECLARE_METATYPE(Debugger::Internal::BreakpointResponseId)

View File

@@ -182,6 +182,7 @@ CdbEngine::CdbEngine() :
m_extensionCommandPrefix("!" QT_CREATOR_CDB_EXT ".")
{
setObjectName("CdbEngine");
setDebuggerName("CDB");
DisplayFormats stringFormats;
stringFormats.append(SimpleFormat);
@@ -225,7 +226,7 @@ void CdbEngine::init()
m_stopMode = NoStopRequested;
m_nextCommandToken = 0;
m_currentBuiltinResponseToken = -1;
m_operateByInstruction = true;
m_operateByInstruction = action(OperateByInstruction)->isChecked();
m_hasDebuggee = false;
m_sourceStepInto = false;
m_watchPointX = m_watchPointY = 0;
@@ -238,8 +239,6 @@ void CdbEngine::init()
m_currentBuiltinResponse.clear();
m_extensionMessageBuffer.clear();
m_pendingBreakpointMap.clear();
m_insertSubBreakpointMap.clear();
m_pendingSubBreakpointMap.clear();
m_interrupCallbacks.clear();
m_symbolAddressCache.clear();
m_coreStopReason.reset();
@@ -526,15 +525,17 @@ void CdbEngine::handleInitialSessionIdle()
operateByInstructionTriggered(action(OperateByInstruction)->isChecked());
// QmlCppEngine expects the QML engine to be connected before any breakpoints are hit
// (attemptBreakpointSynchronization() will be directly called then)
attemptBreakpointSynchronization();
if (rp.breakOnMain) {
const BreakpointParameters bp(BreakpointAtMain);
BreakpointModelId id(quint16(-1));
QString function = cdbAddBreakpointCommand(bp, m_sourcePathMappings, id, true);
runCommand({function, BuiltinCommand,
[this, id](const DebuggerResponse &r) { handleBreakInsert(r, id); }});
// FIXME:
// const BreakpointParameters bp(BreakpointAtMain);
// BreakpointModelId id(quint16(-1));
// QString function = cdbAddBreakpointCommand(bp, m_sourcePathMappings, id, true);
// runCommand({function, BuiltinCommand,
// [this, id](const DebuggerResponse &r) { handleBreakInsert(r, id); }});
}
// Take ownership of the breakpoint. Requests insertion. TODO: Cpp only?
BreakpointManager::claimBreakpointsForEngine(this);
runCommand({".symopt+0x8000"}); // disable searching public symbol table - improving the symbol lookup speed
runCommand({"sxn 0x4000001f", NoFlags}); // Do not break on WowX86 exceptions.
runCommand({"sxn ibp", NoFlags}); // Do not break on initial breakpoints.
@@ -608,7 +609,7 @@ void CdbEngine::runEngine()
runCommand({"sxe " + breakEvent, NoFlags});
// Break functions: each function must be fully qualified,
// else the debugger will slow down considerably.
const auto cb = [this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); };
const auto cb = [this](const DebuggerResponse &r) { handleBreakInsert(r, Breakpoint()); };
if (boolSetting(CdbBreakOnCrtDbgReport)) {
Abi::OSFlavor flavor = runParameters().toolChainAbi.osFlavor();
// CrtDebugReport can not be safely resolved for vc 19
@@ -853,13 +854,8 @@ void CdbEngine::doInterruptInferior(const InterruptCallback &callback)
showMessage(QString("Interrupting process %1...").arg(inferiorPid()), LogMisc);
QTC_ASSERT(!m_signalOperation, notifyInferiorStopFailed(); return);
if (DebuggerRunTool *rt = runTool()) {
IDevice::ConstPtr device = rt->device();
if (!device)
device = runParameters().inferior.device;
if (device)
m_signalOperation = device->signalOperation();
}
QTC_ASSERT(device(), notifyInferiorRunFailed(); return);
m_signalOperation = device()->signalOperation();
QTC_ASSERT(m_signalOperation, notifyInferiorStopFailed(); return;);
connect(m_signalOperation.data(), &DeviceProcessSignalOperation::finished,
this, &CdbEngine::handleDoInterruptInferior);
@@ -881,8 +877,8 @@ void CdbEngine::executeRunToLine(const ContextData &data)
bp.lineNumber = data.lineNumber;
}
runCommand({cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointModelId(), true), BuiltinCommand,
[this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); }});
runCommand({cdbAddBreakpointCommand(bp, m_sourcePathMappings, {}, true), BuiltinCommand,
[this](const DebuggerResponse &r) { handleBreakInsert(r, Breakpoint()); }});
continueInferior();
}
@@ -891,8 +887,8 @@ void CdbEngine::executeRunToFunction(const QString &functionName)
// Add one-shot breakpoint
BreakpointParameters bp(BreakpointByFunction);
bp.functionName = functionName;
runCommand({cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointModelId(), true), BuiltinCommand,
[this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); }});
runCommand({cdbAddBreakpointCommand(bp, m_sourcePathMappings, {}, true), BuiltinCommand,
[this](const DebuggerResponse &r) { handleBreakInsert(r, Breakpoint()); }});
continueInferior();
}
@@ -1014,9 +1010,8 @@ void CdbEngine::handleThreads(const DebuggerResponse &response)
}
}
void CdbEngine::executeDebuggerCommand(const QString &command, DebuggerLanguages languages)
void CdbEngine::executeDebuggerCommand(const QString &command)
{
if (languages & CppLanguage)
runCommand({command, NoFlags});
}
@@ -1465,8 +1460,7 @@ void CdbEngine::fetchMemory(MemoryAgent *agent, quint64 address, quint64 length)
StringInputStream str(args);
str << address << ' ' << length;
cmd.args = args;
cmd.callback = [this, agent = QPointer<MemoryAgent>(agent), address, length]
(const DebuggerResponse &response) {
cmd.callback = [&](const DebuggerResponse &response) {
if (!agent)
return;
if (response.resultClass == ResultDone) {
@@ -1507,7 +1501,8 @@ void CdbEngine::requestModuleSymbols(const QString &moduleName)
void CdbEngine::reloadRegisters()
{
QTC_ASSERT(threadsHandler()->currentThreadIndex() >= 0, return);
if (!(threadsHandler()->currentThreadIndex() >= 0))
return;
runCommand({"registers", ExtensionCommand, CB(handleRegistersExt)});
}
@@ -1680,19 +1675,18 @@ enum StopActionFlags
StopShutdownInProgress = 0x80 // Shutdown in progress
};
static inline QString msgTracePointTriggered(BreakpointModelId id, const int number,
static inline QString msgTracePointTriggered(const Breakpoint &, const QString &displayName,
const QString &threadId)
{
return CdbEngine::tr("Trace point %1 (%2) in thread %3 triggered.")
.arg(id.toString()).arg(number).arg(threadId);
return CdbEngine::tr("Trace point %1 in thread %2 triggered.")
.arg(displayName).arg(threadId);
}
static inline QString msgCheckingConditionalBreakPoint(BreakpointModelId id, const int number,
const QString &condition,
static inline QString msgCheckingConditionalBreakPoint(const Breakpoint &bp, const QString &displayName,
const QString &threadId)
{
return CdbEngine::tr("Conditional breakpoint %1 (%2) in thread %3 triggered, examining expression \"%4\".")
.arg(id.toString()).arg(number).arg(threadId, condition);
return CdbEngine::tr("Conditional breakpoint %1 in thread %2 triggered, examining expression \"%3\".")
.arg(displayName).arg(threadId, bp->condition());
}
unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
@@ -1722,52 +1716,55 @@ unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
if (reason == "breakpoint") {
// Note: Internal breakpoints (run to line) are reported with id=0.
// Step out creates temporary breakpoints with id 10000.
int number = 0;
BreakpointModelId id = cdbIdToBreakpointModelId(stopReason["breakpointId"]);
Breakpoint bp = breakHandler()->breakpointById(id);
const QString responseId = stopReason["breakpointId"].data();
QString displayName;
Breakpoint bp = breakHandler()->findBreakpointByResponseId(responseId);
if (!bp) {
if (const SubBreakpoint sub = breakHandler()->findSubBreakpointByResponseId(responseId)) {
bp = sub->breakpoint();
displayName = sub->displayName;
}
} else {
displayName = bp->displayName();
}
if (bp) {
if (bp.engine() == this) {
const BreakpointResponse parameters = bp.response();
if (!parameters.message.isEmpty()) {
showMessage(parameters.message + '\n', AppOutput);
showMessage(parameters.message, LogMisc);
if (!bp->message().isEmpty()) {
showMessage(bp->message() + '\n', AppOutput);
showMessage(bp->message(), LogMisc);
}
// Trace point? Just report.
number = parameters.id.majorPart();
if (parameters.tracepoint) {
*message = msgTracePointTriggered(id, number, QString::number(threadId));
if (bp->isTracepoint()) {
*message = msgTracePointTriggered(bp, displayName, QString::number(threadId));
return StopReportLog|StopIgnoreContinue;
}
// Trigger evaluation of BP expression unless we are already in the response.
if (!conditionalBreakPointTriggered && !parameters.condition.isEmpty()) {
*message = msgCheckingConditionalBreakPoint(id, number, parameters.condition,
QString::number(threadId));
QString args = parameters.condition;
if (!conditionalBreakPointTriggered && !bp->condition().isEmpty()) {
*message = msgCheckingConditionalBreakPoint(bp, displayName, QString::number(threadId));
QString args = bp->condition();
if (args.contains(' ') && !args.startsWith('"')) {
args.prepend('"');
args.append('"');
}
DebuggerCommand cmd("expression", ExtensionCommand);
cmd.args = args;
cmd.callback = [this, id, stopReason](const DebuggerResponse &response) {
handleExpression(response, id, stopReason);
cmd.callback = [this, bp, stopReason](const DebuggerResponse &response) {
handleExpression(response, bp, stopReason);
};
runCommand(cmd);
return StopReportLog;
}
} else {
bp = Breakpoint();
}
}
QString tid = QString::number(threadId);
if (bp.type() == WatchpointAtAddress)
*message = bp.msgWatchpointByAddressTriggered(number, bp.address(), tid);
else if (bp.type() == WatchpointAtExpression)
*message = bp.msgWatchpointByExpressionTriggered(number, bp.expression(), tid);
if (bp->type() == WatchpointAtAddress)
*message = bp->msgWatchpointByAddressTriggered(bp->address(), tid);
else if (bp->type() == WatchpointAtExpression)
*message = bp->msgWatchpointByExpressionTriggered(bp->expression(), tid);
else
*message = bp.msgBreakpointTriggered(number, tid);
*message = bp->msgBreakpointTriggered(tid);
rc |= StopReportStatusMessage|StopNotifyStop;
}
return rc;
}
if (reason == "exception") {
@@ -1881,11 +1878,11 @@ void CdbEngine::processStop(const GdbMi &stopReason, bool conditionalBreakPointT
showMessage(stopReason["threaderror"].data(), LogError);
}
// Fire off remaining commands asynchronously
if (!m_pendingBreakpointMap.isEmpty() && !m_pendingSubBreakpointMap.isEmpty())
if (!m_pendingBreakpointMap.isEmpty())
listBreakpoints();
if (Internal::isRegistersWindowVisible())
if (isRegistersWindowVisible())
reloadRegisters();
if (Internal::isModulesWindowVisible())
if (isModulesWindowVisible())
reloadModules();
}
// After the sequence has been sent off and CDB is pondering the commands,
@@ -1894,7 +1891,7 @@ void CdbEngine::processStop(const GdbMi &stopReason, bool conditionalBreakPointT
showStoppedByExceptionMessageBox(exceptionBoxMessage);
}
void CdbEngine::handleBreakInsert(const DebuggerResponse &response, const BreakpointModelId &bpId)
void CdbEngine::handleBreakInsert(const DebuggerResponse &response, const Breakpoint &bp)
{
const QStringList reply = response.data.data().split('\n');
if (reply.isEmpty())
@@ -1913,16 +1910,19 @@ void CdbEngine::handleBreakInsert(const DebuggerResponse &response, const Breakp
// Matched: untitled123!<lambda_4956dbaf7bce78acbc6759af75f3884a>::operator QString (__cdecl*)(void) (000007f6`be2f27b0)
// Matched: untitled123!<lambda_4956dbaf7bce78acbc6759af75f3884a>::<helper_func_vectorcall> (000007f6`be2f27d0)
// Matched: untitled123!<lambda_4956dbaf7bce78acbc6759af75f3884a>::operator QString (__vectorcall*)(void) (000007f6`be2f2850)
// Ambiguous symbol error at '`untitled2!C:\dev\src\tmp\untitled2\main.cpp:18`'
// Ambiguous symbol error at '`untitled2!C:\dev\src\tmp\unttitled2\main.cpp:18`'
// ^ Extra character error in 'bu1004 `untitled2!C:\dev\src\tmp\untitled2\main.cpp:18`'
if (!bpId.isValid())
// Happens regularly for Run to Line and Jump to Line.
if (!bp)
return;
Breakpoint bp = breakHandler()->breakpointById(bpId);
// add break point for every match
const int parentResponseId = bp->responseId().toInt();
quint16 subBreakPointID = 0;
const QLatin1String matchPrefix("Matched: ");
for (auto line = reply.constBegin(), end = reply.constEnd(); line != end; ++line) {
if (!line->startsWith("Matched: "))
if (!line->startsWith(matchPrefix))
continue;
const int addressStartPos = line->lastIndexOf('(') + 1;
const int addressEndPos = line->indexOf(')', addressStartPos);
@@ -1936,16 +1936,25 @@ void CdbEngine::handleBreakInsert(const DebuggerResponse &response, const Breakp
if (!ok)
continue;
BreakpointModelId id(bpId.majorPart(), ++subBreakPointID);
BreakpointResponse res = bp.response();
res.type = BreakpointByAddress;
res.address = address;
m_insertSubBreakpointMap.insert(id, res);
++subBreakPointID;
const QString responseId(QString::number(parentResponseId + subBreakPointID));
SubBreakpoint sub = bp->findOrCreateSubBreakpoint(responseId);
sub->responseId = responseId;
sub->params = bp->parameters();
sub->params.type = BreakpointByAddress;
sub->params.address = address;
QString functionName(line->mid(matchPrefix.size(),
addressStartPos - 1 - matchPrefix.size()));
const int functionStart = functionName.indexOf('!') + 1;
const int functionOffset = functionName.lastIndexOf('+');
if (functionOffset > 0)
functionName.truncate(functionOffset);
if (functionStart > 0)
functionName = functionName.mid(functionStart);
sub->params.functionName = functionName;
sub->displayName = bp->displayName() + '.' + QString::number(subBreakPointID);
runCommand({cdbAddBreakpointCommand(sub->params, m_sourcePathMappings, sub->responseId, false), NoFlags});
}
if (subBreakPointID == 0)
return;
attemptBreakpointSynchronization();
}
void CdbEngine::handleCheckWow64(const DebuggerResponse &response, const GdbMi &stack)
@@ -2413,10 +2422,10 @@ bool CdbEngine::stateAcceptsBreakpointChanges() const
return false;
}
bool CdbEngine::acceptsBreakpoint(Breakpoint bp) const
bool CdbEngine::acceptsBreakpoint(const BreakpointParameters &bp) const
{
if (bp.parameters().isCppBreakpoint()) {
switch (bp.type()) {
if (bp.isCppBreakpoint()) {
switch (bp.type) {
case UnknownBreakpointType:
case LastBreakpointType:
case BreakpointAtFork:
@@ -2490,139 +2499,90 @@ unsigned BreakpointCorrectionContext::fixLineNumber(const QString &fileName,
return correctedLine;
}
void CdbEngine::attemptBreakpointSynchronization()
void CdbEngine::insertBreakpoint(const Breakpoint &bp)
{
if (debug)
qDebug("attemptBreakpointSynchronization in %s", qPrintable(stateName(state())));
// Check if there is anything to be done at all.
BreakHandler *handler = breakHandler();
// Take ownership of the breakpoint. Requests insertion. TODO: Cpp only?
for (Breakpoint bp : handler->unclaimedBreakpoints())
if (acceptsBreakpoint(bp))
bp.setEngine(this);
// Quick check: is there a need to change something? - Populate module cache
bool changed = !m_insertSubBreakpointMap.isEmpty();
const Breakpoints bps = handler->engineBreakpoints(this);
if (!changed) {
for (Breakpoint bp : bps) {
switch (bp.state()) {
case BreakpointInsertRequested:
case BreakpointRemoveRequested:
case BreakpointChangeRequested:
changed = true;
break;
case BreakpointInserted: {
// Collect the new modules matching the files.
// In the future, that information should be obtained from the build system.
const BreakpointParameters &data = bp.parameters();
if (data.type == BreakpointByFileAndLine && !data.module.isEmpty())
m_fileNameModuleHash.insert(data.fileName, data.module);
}
break;
default:
break;
}
}
}
if (debugBreakpoints)
qDebug("attemptBreakpointSynchronizationI %dms accessible=%d, %s %d breakpoints, changed=%d",
elapsedLogTime(), m_accessible, qPrintable(stateName(state())), bps.size(), changed);
if (!changed)
return;
// Add/Change breakpoints and store pending ones in map, since
// Breakhandler::setResponse() on pending breakpoints clears the pending flag.
// handleBreakPoints will the complete that information and set it on the break handler.
bool addedChanged = false;
QScopedPointer<BreakpointCorrectionContext> lineCorrection;
for (Breakpoint bp : bps) {
BreakpointParameters parameters = bp.parameters();
BreakpointModelId id = bp.id();
const auto handleBreakInsertCB = [this, id](const DebuggerResponse &r) { handleBreakInsert(r, id); };
BreakpointResponse response;
response.fromParameters(parameters);
response.id = BreakpointResponseId(id.majorPart(), id.minorPart());
// If we encountered that file and have a module for it: Add it.
if (parameters.type == BreakpointByFileAndLine && parameters.module.isEmpty()) {
const QHash<QString, QString>::const_iterator it = m_fileNameModuleHash.constFind(parameters.fileName);
if (it != m_fileNameModuleHash.constEnd())
parameters.module = it.value();
}
switch (bp.state()) {
case BreakpointInsertRequested:
BreakpointParameters parameters = bp->requestedParameters();
const auto handleBreakInsertCB = [this, bp](const DebuggerResponse &r) { handleBreakInsert(r, bp); };
BreakpointParameters response = parameters;
auto responseId = QString::number(breakPointIdToCdbId(bp));
QScopedPointer<BreakpointCorrectionContext> lineCorrection(
new BreakpointCorrectionContext(m_codeModelSnapshot, CppTools::CppModelManager::instance()->workingCopy()));
if (!m_autoBreakPointCorrection
&& parameters.type == BreakpointByFileAndLine
&& boolSetting(CdbBreakPointCorrection)) {
if (lineCorrection.isNull())
lineCorrection.reset(new BreakpointCorrectionContext(m_codeModelSnapshot,
CppTools::CppModelManager::instance()->workingCopy()));
response.lineNumber = int(lineCorrection->fixLineNumber(
parameters.fileName, unsigned(parameters.lineNumber)));
QString cmd = cdbAddBreakpointCommand(response, m_sourcePathMappings, id, false);
QString cmd = cdbAddBreakpointCommand(response, m_sourcePathMappings, responseId, false);
runCommand({cmd, BuiltinCommand, handleBreakInsertCB});
} else {
QString cmd = cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false);
QString cmd = cdbAddBreakpointCommand(parameters, m_sourcePathMappings, responseId, false);
runCommand({cmd, BuiltinCommand, handleBreakInsertCB});
}
if (!parameters.enabled)
runCommand({"bd " + QString::number(breakPointIdToCdbId(id)), NoFlags});
bp.notifyBreakpointInsertProceeding();
bp.notifyBreakpointInsertOk();
m_pendingBreakpointMap.insert(id, response);
addedChanged = true;
runCommand({"bd " + responseId, NoFlags});
// Ensure enabled/disabled is correct in handler and line number is there.
bp.setResponse(response);
bp->setParameters(response);
bp->setResponseId(responseId);
bp->setDisplayName(QString::number(bp->modelId()));
notifyBreakpointInsertProceeding(bp);
notifyBreakpointInsertOk(bp);
m_pendingBreakpointMap.insert(bp);
if (debugBreakpoints)
qDebug("Adding %d %s\n", id.toInternalId(),
qPrintable(response.toString()));
break;
case BreakpointChangeRequested:
bp.notifyBreakpointChangeProceeding();
qDebug("Adding %d %s\n", bp->modelId(), qPrintable(response.toString()));
listBreakpoints();
}
void CdbEngine::removeBreakpoint(const Breakpoint &bp)
{
runCommand({cdbClearBreakpointCommand(bp), NoFlags});
notifyBreakpointRemoveProceeding(bp);
notifyBreakpointRemoveOk(bp);
m_pendingBreakpointMap.remove(bp);
}
static QString enableBreakpointCommand(const QString &responseId, bool on)
{
const QString command(on ? QString("be") : QString("bd"));
return command + ' ' + responseId;
}
void CdbEngine::updateBreakpoint(const Breakpoint &bp)
{
BreakpointParameters parameters = bp->requestedParameters();
const auto handleBreakInsertCB = [this, bp](const DebuggerResponse &r) { handleBreakInsert(r, bp); };
BreakpointParameters response = parameters;
auto responseId = QString::number(breakPointIdToCdbId(bp));
notifyBreakpointChangeProceeding(bp);
if (debugBreakpoints)
qDebug("Changing %d:\n %s\nTo %s\n", id.toInternalId(),
qPrintable(bp.response().toString()),
qDebug("Changing %d:\n %s\nTo %s\n", bp->modelId(),
qPrintable(bp->parameters().toString()),
qPrintable(parameters.toString()));
if (parameters.enabled != bp.response().enabled) {
if (parameters.enabled != bp->isEnabled()) {
// Change enabled/disabled breakpoints without triggering update.
if (parameters.enabled)
runCommand({"be " + QString::number(breakPointIdToCdbId(id)), NoFlags});
else
runCommand({"bd " + QString::number(breakPointIdToCdbId(id)), NoFlags});
bp->forFirstLevelChildren([this, parameters](SubBreakpointItem *sbp){
breakHandler()->requestSubBreakpointEnabling({sbp}, parameters.enabled);
});
if (!bp->hasChildren())
runCommand({enableBreakpointCommand(bp->responseId(), parameters.enabled), NoFlags});
response.pending = false;
response.enabled = parameters.enabled;
bp.setResponse(response);
bp->setParameters(response);
} else {
// Delete and re-add, triggering update
addedChanged = true;
runCommand({cdbClearBreakpointCommand(id), NoFlags});
QString cmd(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false));
runCommand({cdbClearBreakpointCommand(bp), NoFlags});
QString cmd = cdbAddBreakpointCommand(parameters, m_sourcePathMappings, responseId, false);
runCommand({cmd, BuiltinCommand, handleBreakInsertCB});
m_pendingBreakpointMap.insert(id, response);
}
bp.notifyBreakpointChangeOk();
break;
case BreakpointRemoveRequested:
runCommand({cdbClearBreakpointCommand(id), NoFlags});
bp.notifyBreakpointRemoveProceeding();
bp.notifyBreakpointRemoveOk();
m_pendingBreakpointMap.remove(id);
break;
default:
break;
}
}
foreach (BreakpointModelId id, m_insertSubBreakpointMap.keys()) {
addedChanged = true;
const BreakpointResponse &response = m_insertSubBreakpointMap.value(id);
runCommand({cdbAddBreakpointCommand(response, m_sourcePathMappings, id, false), NoFlags});
m_insertSubBreakpointMap.remove(id);
m_pendingSubBreakpointMap.insert(id, response);
}
// List breakpoints and send responses
if (addedChanged)
m_pendingBreakpointMap.insert(bp);
listBreakpoints();
}
notifyBreakpointChangeOk(bp);
}
void CdbEngine::enableSubBreakpoint(const SubBreakpoint &sbp, bool on)
{
runCommand({enableBreakpointCommand(sbp->responseId, on), NoFlags});
if (on && !sbp->breakpoint()->isEnabled())
sbp->breakpoint()->setEnabled(true);
}
// Pass a file name through source mapping and normalize upper/lower case (for the editor
@@ -2852,7 +2812,7 @@ void CdbEngine::handleStackTrace(const DebuggerResponse &response)
}
}
void CdbEngine::handleExpression(const DebuggerResponse &response, BreakpointModelId id, const GdbMi &stopReason)
void CdbEngine::handleExpression(const DebuggerResponse &response, const Breakpoint &bp, const GdbMi &stopReason)
{
int value = 0;
if (response.resultClass == ResultDone)
@@ -2862,9 +2822,9 @@ void CdbEngine::handleExpression(const DebuggerResponse &response, BreakpointMod
// Is this a conditional breakpoint?
const QString message = value ?
tr("Value %1 obtained from evaluating the condition of breakpoint %2, stopping.").
arg(value).arg(id.toString()) :
arg(value).arg(bp->displayName()) :
tr("Value 0 obtained from evaluating the condition of breakpoint %1, continuing.").
arg(id.toString());
arg(bp->displayName());
showMessage(message, LogMisc);
// Stop if evaluation is true, else continue
if (value)
@@ -2905,10 +2865,9 @@ void CdbEngine::handleWidgetAt(const DebuggerResponse &response)
m_watchPointX = m_watchPointY = 0;
}
static inline void formatCdbBreakPointResponse(BreakpointModelId id, const BreakpointResponse &r,
QTextStream &str)
static void formatCdbBreakPointResponse(int modelId, const QString &responseId, const BreakpointParameters &r, QTextStream &str)
{
str << "Obtained breakpoint " << id << " (#" << r.id.majorPart() << ')';
str << "Obtained breakpoint " << modelId << " (#" << responseId << ')';
if (r.pending) {
str << ", pending";
} else {
@@ -2946,46 +2905,62 @@ void CdbEngine::handleBreakPoints(const DebuggerResponse &response)
QTextStream str(&message);
BreakHandler *handler = breakHandler();
foreach (const GdbMi &breakPointG, response.data.children()) {
BreakpointResponse reportedResponse;
// Might not be valid if there is not id
const QString responseId = breakPointG["id"].data();
BreakpointParameters reportedResponse;
parseBreakPoint(breakPointG, &reportedResponse);
if (debugBreakpoints)
qDebug(" Parsed %d: pending=%d %s\n", reportedResponse.id.majorPart(),
qDebug(" Parsed %s: pending=%d %s\n", qPrintable(responseId),
reportedResponse.pending,
qPrintable(reportedResponse.toString()));
if (reportedResponse.id.isValid() && !reportedResponse.pending) {
Breakpoint bp = handler->findBreakpointByResponseId(reportedResponse.id);
if (!responseId.isEmpty() && !reportedResponse.pending) {
Breakpoint bp = handler->findBreakpointByResponseId(responseId);
if (!bp && reportedResponse.type == BreakpointByFunction)
continue; // Breakpoints from options, CrtDbgReport() and others.
QTC_ASSERT(bp, continue);
const auto it = m_pendingBreakpointMap.find(bp.id());
const auto subIt = m_pendingSubBreakpointMap.find(
BreakpointModelId(reportedResponse.id.majorPart(),
reportedResponse.id.minorPart()));
if (it != m_pendingBreakpointMap.end() || subIt != m_pendingSubBreakpointMap.end()) {
if (bp) {
if (!bp->isPending())
continue;
QTC_ASSERT(m_pendingBreakpointMap.contains(bp), continue);
// Complete the response and set on handler.
BreakpointResponse currentResponse = it != m_pendingBreakpointMap.end()
? it.value()
: subIt.value();
currentResponse.id = reportedResponse.id;
BreakpointParameters currentResponse = bp->parameters();
currentResponse.address = reportedResponse.address;
currentResponse.module = reportedResponse.module;
currentResponse.pending = reportedResponse.pending;
currentResponse.enabled = reportedResponse.enabled;
currentResponse.fileName = reportedResponse.fileName;
currentResponse.lineNumber = reportedResponse.lineNumber;
formatCdbBreakPointResponse(bp.id(), currentResponse, str);
formatCdbBreakPointResponse(bp->modelId(), responseId, currentResponse, str);
if (debugBreakpoints)
qDebug(" Setting for %d: %s\n", currentResponse.id.majorPart(),
qDebug(" Setting for %s: %s\n", qPrintable(responseId),
qPrintable(currentResponse.toString()));
if (it != m_pendingBreakpointMap.end()) {
bp.setResponse(currentResponse);
m_pendingBreakpointMap.erase(it);
}
if (subIt != m_pendingSubBreakpointMap.end()) {
bp.insertSubBreakpoint(currentResponse);
m_pendingSubBreakpointMap.erase(subIt);
bp->setParameters(currentResponse);
m_pendingBreakpointMap.remove(bp);
continue;
}
SubBreakpoint sub = handler->findSubBreakpointByResponseId(responseId);
if (sub) {
BreakpointParameters currentResponse = sub->params;
currentResponse.address = reportedResponse.address;
currentResponse.module = reportedResponse.module;
currentResponse.pending = reportedResponse.pending;
currentResponse.enabled = reportedResponse.enabled;
currentResponse.fileName = reportedResponse.fileName;
currentResponse.lineNumber = reportedResponse.lineNumber;
Breakpoint bp = sub->breakpoint();
QTC_ASSERT(bp, continue);
formatCdbBreakPointResponse(bp->modelId(), responseId, currentResponse, str);
m_pendingBreakpointMap.remove(bp);
if (bp->isPending() && !reportedResponse.pending)
bp->setPending(false);
if (debugBreakpoints)
qDebug(" Setting for %s: %s\n", qPrintable(responseId),
qPrintable(currentResponse.toString()));
// SubBreakpointItem *loc = bp->findOrCreateSubBreakpoint(reportedResponse.responseId);
sub->setParameters(currentResponse);
continue;
}
QTC_ASSERT(false, qDebug() << "bp not found in either of the pending maps");
} // not pending reported
} // foreach
if (m_pendingBreakpointMap.empty())

View File

@@ -52,12 +52,8 @@ public:
explicit CdbEngine();
~CdbEngine() override;
// Factory function that returns 0 if the debug engine library cannot be found.
bool canHandleToolTip(const DebuggerToolTipContext &context) const override;
DebuggerEngine *cppEngine() override { return this; }
void setupEngine() override;
void runEngine() override;
void shutdownInferior() override;
@@ -81,14 +77,18 @@ public:
void executeRunToFunction(const QString &functionName) override;
void executeJumpToLine(const ContextData &data) override;
void assignValueInDebugger(WatchItem *w, const QString &expr, const QVariant &value) override;
void executeDebuggerCommand(const QString &command, DebuggerLanguages languages) override;
void executeDebuggerCommand(const QString &command) override;
void activateFrame(int index) override;
void selectThread(ThreadId threadId) override;
bool stateAcceptsBreakpointChanges() const override;
bool acceptsBreakpoint(Breakpoint bp) const override;
void attemptBreakpointSynchronization() override;
bool acceptsBreakpoint(const BreakpointParameters &params) const override;
void insertBreakpoint(const Breakpoint &bp) override;
void removeBreakpoint(const Breakpoint &bp) override;
void updateBreakpoint(const Breakpoint &bp) override;
void enableSubBreakpoint(const SubBreakpoint &sbp, bool on) override;
void fetchDisassembler(DisassemblerAgent *agent) override;
void fetchMemory(MemoryAgent *, quint64 addr, quint64 length) override;
@@ -119,8 +119,7 @@ private:
void handleDoInterruptInferior(const QString &errorMessage);
using PendingBreakPointMap = QHash<BreakpointModelId, BreakpointResponse>;
using SourcePathMapping = QPair<QString, QString>;
typedef QPair<QString, QString> SourcePathMapping;
struct NormalizedSourceFileName // Struct for caching mapped/normalized source files.
{
NormalizedSourceFileName(const QString &fn = QString(), bool e = false) : fileName(fn), exists(e) {}
@@ -176,10 +175,10 @@ private:
void handleStackTrace(const DebuggerResponse &);
void handleRegisters(const DebuggerResponse &);
void handleJumpToLineAddressResolution(const DebuggerResponse &response, const ContextData &context);
void handleExpression(const DebuggerResponse &command, BreakpointModelId id, const GdbMi &stopReason);
void handleExpression(const DebuggerResponse &command, const Breakpoint &bp, const GdbMi &stopReason);
void handleResolveSymbol(const DebuggerResponse &command, const QString &symbol, DisassemblerAgent *agent);
void handleResolveSymbolHelper(const QList<quint64> &addresses, DisassemblerAgent *agent);
void handleBreakInsert(const DebuggerResponse &response, const BreakpointModelId &bpId);
void handleBreakInsert(const DebuggerResponse &response, const Breakpoint &bp);
void handleCheckWow64(const DebuggerResponse &response, const GdbMi &stack);
void ensureUsing32BitStackInWow64(const DebuggerResponse &response, const GdbMi &stack);
void handleSwitchWow64Stack(const DebuggerResponse &response);
@@ -232,11 +231,8 @@ private:
bool m_sourceStepInto = false;
int m_watchPointX = 0;
int m_watchPointY = 0;
PendingBreakPointMap m_pendingBreakpointMap;
PendingBreakPointMap m_insertSubBreakpointMap;
PendingBreakPointMap m_pendingSubBreakpointMap;
QSet<Breakpoint> m_pendingBreakpointMap;
bool m_autoBreakPointCorrection = false;
QHash<QString, QString> m_fileNameModuleHash;
QMultiHash<QString, quint64> m_symbolAddressCache;
bool m_ignoreCdbOutput = false;
QList<InterruptCallback> m_interrupCallbacks;

View File

@@ -27,6 +27,7 @@
#include "stringinputstream.h"
#include <debugger/breakhandler.h>
#include <debugger/debuggerprotocol.h>
#include <debugger/disassemblerlines.h>
#include <debugger/shared/hostutils.h>
@@ -77,14 +78,14 @@ QString cdbSourcePathMapping(QString fileName,
// Determine file name to be used for breakpoints. Convert to native and, unless short path
// is set, perform reverse lookup in the source path mappings.
static inline QString cdbBreakPointFileName(const BreakpointParameters &bp,
static inline QString cdbBreakPointFileName(const BreakpointParameters &params,
const QList<QPair<QString, QString> > &sourcePathMapping)
{
if (bp.fileName.isEmpty())
return bp.fileName;
if (bp.pathUsage == BreakpointUseShortPath)
return Utils::FileName::fromString(bp.fileName).fileName();
return cdbSourcePathMapping(QDir::toNativeSeparators(bp.fileName), sourcePathMapping, SourceToDebugger);
if (params.fileName.isEmpty())
return params.fileName;
if (params.pathUsage == BreakpointUseShortPath)
return Utils::FileName::fromString(params.fileName).fileName();
return cdbSourcePathMapping(QDir::toNativeSeparators(params.fileName), sourcePathMapping, SourceToDebugger);
}
static BreakpointParameters fixWinMSVCBreakpoint(const BreakpointParameters &p)
@@ -127,69 +128,46 @@ static BreakpointParameters fixWinMSVCBreakpoint(const BreakpointParameters &p)
return p;
}
int breakPointIdToCdbId(const BreakpointModelId &id)
int breakPointIdToCdbId(const Breakpoint &bp)
{
return cdbBreakPointStartId + id.majorPart() * cdbBreakPointIdMinorPart + id.minorPart();
// return cdbBreakPointStartId + bp.majorPart() * cdbBreakPointIdMinorPart + bp.minorPart();
if (!bp->responseId().isEmpty())
return bp->responseId().toInt();
return cdbBreakPointStartId + bp->modelId() * cdbBreakPointIdMinorPart;
}
template <class ModelId>
inline ModelId cdbIdToBreakpointId(const int &id)
{
if (id >= cdbBreakPointStartId) {
int major = (id - cdbBreakPointStartId) / cdbBreakPointIdMinorPart;
int minor = id % cdbBreakPointIdMinorPart;
if (minor)
return ModelId(major, minor);
else
return ModelId(major);
}
return ModelId();
}
template <class ModelId>
inline ModelId cdbIdToBreakpointId(const GdbMi &data)
{
if (data.isValid()) { // Might not be valid if there is not id
bool ok;
const int id = data.data().toInt(&ok);
if (ok)
return cdbIdToBreakpointId<ModelId>(id);
}
return ModelId();
}
BreakpointModelId cdbIdToBreakpointModelId(const GdbMi &id)
{
return cdbIdToBreakpointId<BreakpointModelId>(id);
}
BreakpointResponseId cdbIdToBreakpointResponseId(const GdbMi &id)
{
return cdbIdToBreakpointId<BreakpointResponseId>(id);
}
//static int cdbIdToBreakpointModel(int cdbid)
//{
// if (cdbid >= cdbBreakPointStartId) {
// int major = (cdbid - cdbBreakPointStartId) / cdbBreakPointIdMinorPart;
// int minor = cdbid % cdbBreakPointIdMinorPart;
// (void) minor;
// return major;
// }
// return 0;
//}
QString cdbAddBreakpointCommand(const BreakpointParameters &bpIn,
const QList<QPair<QString, QString> > &sourcePathMapping,
BreakpointModelId id /* = BreakpointId() */,
const QString &responseId,
bool oneshot)
{
const BreakpointParameters bp = fixWinMSVCBreakpoint(bpIn);
const BreakpointParameters params = fixWinMSVCBreakpoint(bpIn);
QString rc;
StringInputStream str(rc);
if (bp.threadSpec >= 0)
str << '~' << bp.threadSpec << ' ';
if (params.threadSpec >= 0)
str << '~' << params.threadSpec << ' ';
// Currently use 'bu' so that the offset expression (including file name)
// is kept when reporting back breakpoints (which is otherwise discarded
// when resolving).
str << (bp.type == WatchpointAtAddress ? "ba" : "bu");
if (id.isValid())
str << breakPointIdToCdbId(id);
str << ' ';
str << (params.type == WatchpointAtAddress ? "ba" : "bu")
<< responseId
<< ' ';
if (oneshot)
str << "/1 ";
switch (bp.type) {
switch (params.type) {
case BreakpointAtFork:
case BreakpointAtExec:
case WatchpointAtExpression:
@@ -204,39 +182,41 @@ QString cdbAddBreakpointCommand(const BreakpointParameters &bpIn,
QTC_ASSERT(false, return QString());
break;
case BreakpointByAddress:
str << hex << hexPrefixOn << bp.address << hexPrefixOff << dec;
str << hex << hexPrefixOn << params.address << hexPrefixOff << dec;
break;
case BreakpointByFunction:
if (!bp.module.isEmpty())
str << bp.module << '!';
str << bp.functionName;
if (!params.module.isEmpty())
str << params.module << '!';
str << params.functionName;
break;
case BreakpointByFileAndLine:
str << '`';
if (!bp.module.isEmpty())
str << bp.module << '!';
str << cdbBreakPointFileName(bp, sourcePathMapping) << ':' << bp.lineNumber << '`';
if (!params.module.isEmpty())
str << params.module << '!';
str << cdbBreakPointFileName(params, sourcePathMapping) << ':' << params.lineNumber << '`';
break;
case WatchpointAtAddress: { // Read/write, no space here
const unsigned size = bp.size ? bp.size : 1;
str << 'r' << size << ' ' << hex << hexPrefixOn << bp.address << hexPrefixOff << dec;
const unsigned size = params.size ? params.size : 1;
str << 'r' << size << ' ' << hex << hexPrefixOn << params.address << hexPrefixOff << dec;
}
break;
}
if (bp.ignoreCount)
str << " 0n" << (bp.ignoreCount + 1);
if (params.ignoreCount)
str << " 0n" << (params.ignoreCount + 1);
// Condition currently unsupported.
if (!bp.command.isEmpty())
str << " \"" << bp.command << '"';
if (!params.command.isEmpty())
str << " \"" << params.command << '"';
return rc;
}
QString cdbClearBreakpointCommand(const BreakpointModelId &id)
QString cdbClearBreakpointCommand(const Breakpoint &bp)
{
const int firstBreakPoint = breakPointIdToCdbId(id);
if (id.isMinor())
return "bc " + QString::number(firstBreakPoint);
// FIME: Check
// const int firstBreakPoint = breakPointIdToCdbId(id);
// if (id.isMinor())
// return "bc " + QString::number(firstBreakPoint);
// If this is a major break point we also want to delete all sub break points
const int firstBreakPoint = cdbBreakPointStartId + bp->modelId() * cdbBreakPointIdMinorPart;
const int lastBreakPoint = firstBreakPoint + cdbBreakPointIdMinorPart - 1;
return "bc " + QString::number(firstBreakPoint) + '-' + QString::number(lastBreakPoint);
}
@@ -270,14 +250,11 @@ static inline bool gdbmiChildToBool(const GdbMi &parent, const char *childName,
// Parse extension command listing breakpoints.
// Note that not all fields are returned, since file, line, function are encoded
// in the expression (that is in addition deleted on resolving for a bp-type breakpoint).
void parseBreakPoint(const GdbMi &gdbmi, BreakpointResponse *r,
void parseBreakPoint(const GdbMi &gdbmi, BreakpointParameters *r,
QString *expression /* = 0 */)
{
gdbmiChildToBool(gdbmi, "enabled", &(r->enabled));
gdbmiChildToBool(gdbmi, "deferred", &(r->pending));
r->id = BreakpointResponseId();
// Might not be valid if there is not id
r->id = cdbIdToBreakpointResponseId(gdbmi["id"]);
const GdbMi moduleG = gdbmi["module"];
if (moduleG.isValid())
r->module = moduleG.data();

View File

@@ -25,7 +25,7 @@
#pragma once
#include <debugger/breakpoint.h>
#include <debugger/breakhandler.h>
#include <QPair>
@@ -36,7 +36,6 @@ QT_END_NAMESPACE
namespace Debugger {
namespace Internal {
class BreakpointData;
class BreakpointParameters;
struct ThreadData;
class Register;
@@ -54,19 +53,17 @@ QString cdbSourcePathMapping(QString fileName,
enum { cdbBreakPointStartId = 100000,
cdbBreakPointIdMinorPart = 100};
int breakPointIdToCdbId(const BreakpointModelId &id);
BreakpointModelId cdbIdToBreakpointModelId(const GdbMi &id);
BreakpointResponseId cdbIdToBreakpointResponseId(const GdbMi &id);
int breakPointIdToCdbId(const Breakpoint &bp);
// Convert breakpoint in CDB syntax (applying source path mappings using native paths).
QString cdbAddBreakpointCommand(const BreakpointParameters &d,
const QList<QPair<QString, QString> > &sourcePathMapping,
BreakpointModelId id = BreakpointModelId(quint16(-1)), bool oneshot = false);
QString cdbClearBreakpointCommand(const BreakpointModelId &id);
const QString &responseId = QString(), bool oneshot = false);
QString cdbClearBreakpointCommand(const Breakpoint &bp);
// Parse extension command listing breakpoints.
// Note that not all fields are returned, since file, line, function are encoded
// in the expression (that is in addition deleted on resolving for a bp-type breakpoint).
void parseBreakPoint(const GdbMi &gdbmi, BreakpointResponse *r, QString *expression = nullptr);
void parseBreakPoint(const GdbMi &gdbmi, BreakpointParameters *r, QString *expression = nullptr);
// Write memory (f ...).
QString cdbWriteMemoryCommand(quint64 addr, const QByteArray &data);

View File

@@ -41,7 +41,6 @@ HEADERS += \
procinterrupt.h \
registerhandler.h \
snapshothandler.h \
snapshotwindow.h \
sourceagent.h \
sourcefileshandler.h \
sourceutils.h \
@@ -89,7 +88,6 @@ SOURCES += \
procinterrupt.cpp \
registerhandler.cpp \
snapshothandler.cpp \
snapshotwindow.cpp \
sourceagent.cpp \
sourcefileshandler.cpp \
sourceutils.cpp \

View File

@@ -72,7 +72,6 @@ Project {
"procinterrupt.cpp", "procinterrupt.h",
"registerhandler.cpp", "registerhandler.h",
"snapshothandler.cpp", "snapshothandler.h",
"snapshotwindow.cpp", "snapshotwindow.h",
"sourceagent.cpp", "sourceagent.h",
"sourcefileshandler.cpp", "sourcefileshandler.h",
"sourceutils.cpp", "sourceutils.h",
@@ -143,7 +142,6 @@ Project {
prefix: "qml/"
files: [
"interactiveinterpreter.cpp", "interactiveinterpreter.h",
"qmlcppengine.cpp", "qmlcppengine.h",
"qmlengine.cpp", "qmlengine.h",
"qmlengineutils.cpp", "qmlengineutils.h",
"qmlinspectoragent.cpp", "qmlinspectoragent.h",

View File

@@ -101,8 +101,12 @@ void GlobalDebuggerOptions::fromSettings()
//
//////////////////////////////////////////////////////////////////////////
static DebuggerSettings *theDebuggerSettings = nullptr;
DebuggerSettings::DebuggerSettings()
{
theDebuggerSettings = this;
const QString debugModeGroup = QLatin1String(debugModeSettingsGroupC);
const QString cdbSettingsGroup = QLatin1String(cdbSettingsGroupC);
@@ -580,6 +584,7 @@ DebuggerSettings::DebuggerSettings()
item->setText(tr("Enable Reverse Debugging"));
item->setCheckable(true);
item->setDefaultValue(false);
item->setIcon(Icons::REVERSE_MODE.icon());
insertItem(EnableReverseDebugging, item);
#ifdef Q_OS_WIN
@@ -693,10 +698,10 @@ SavedAction *DebuggerSettings::item(int code) const
return m_items.value(code, 0);
}
QString DebuggerSettings::dump() const
QString DebuggerSettings::dump()
{
QStringList settings;
foreach (SavedAction *item, m_items) {
foreach (SavedAction *item, theDebuggerSettings->m_items) {
QString key = item->settingsKey();
if (!key.isEmpty()) {
const QString current = item->value().toString();

View File

@@ -61,7 +61,7 @@ public:
void insertItem(int code, Utils::SavedAction *item);
Utils::SavedAction *item(int code) const;
QString dump() const;
static QString dump();
void readSettings();
void writeSettings() const;

View File

@@ -38,8 +38,8 @@ const char C_DEBUGMODE[] = "Debugger.DebugMode";
const char C_CPPDEBUGGER[] = "Gdb Debugger";
const char C_QMLDEBUGGER[] = "Qml/JavaScript Debugger";
const char CppPerspectiveId[] = "Debugger.Perspective.Cpp";
const char QmlPerspectiveId[] = "Debugger.Perspective.Qml";
const char PRESET_PERSPRECTIVE_ID[] = "Debugger.Perspective.Preset";
const char PERSPECTIVE_ID[] = "Debugger.Perspective";
// Menu Groups
const char G_GENERAL[] = "Debugger.Group.General";

View File

@@ -27,6 +27,7 @@
#include "debuggerconstants.h"
#include <coreplugin/id.h>
#include <projectexplorer/abi.h>
#include <QObject>
@@ -43,7 +44,10 @@ QT_END_NAMESPACE
namespace CPlusPlus { class Snapshot; }
namespace Utils { class SavedAction; }
namespace Utils {
class BaseTreeView;
class SavedAction;
}
namespace Debugger {
@@ -51,12 +55,9 @@ class DebuggerRunTool;
namespace Internal {
class BreakHandler;
class DebuggerEngine;
class Symbol;
class Section;
class GlobalDebuggerOptions;
class WatchTreeView;
enum TestCases
{
@@ -65,34 +66,14 @@ enum TestCases
};
// Some convenience.
void updateState(DebuggerRunTool *runTool);
void updateLocalsWindow(bool showReturn);
bool hasSnapshots();
void openTextEditor(const QString &titlePattern, const QString &contents);
// void runTest(const QString &fileName);
void showMessage(const QString &msg, int channel, int timeout = -1);
bool isReverseDebugging();
void runControlStarted(DebuggerRunTool *runTool);
void runControlFinished(DebuggerRunTool *runTool);
void displayDebugger(DebuggerRunTool *runTool);
void synchronizeBreakpoints();
void saveModeToRestore();
QWidget *mainWindow();
void raiseWatchersWindow();
bool isRegistersWindowVisible();
bool isModulesWindowVisible();
void showModuleSymbols(const QString &moduleName, const QVector<Internal::Symbol> &symbols);
void showModuleSections(const QString &moduleName, const QVector<Internal::Section> &sections);
void openMemoryEditor();
void setThreadBoxContents(const QStringList &list, int index);
QSharedPointer<Internal::GlobalDebuggerOptions> globalDebuggerOptions();
WatchTreeView *inspectorView();
QVariant sessionValue(const QByteArray &name);
void setSessionValue(const QByteArray &name, const QVariant &value);
QVariant configValue(const QString &name);
@@ -105,9 +86,6 @@ bool boolSetting(int code);
QString stringSetting(int code);
QStringList stringListSetting(int code);
BreakHandler *breakHandler();
DebuggerEngine *currentEngine();
QMessageBox *showMessageBox(int icon, const QString &title,
const QString &text, int buttons = 0);
@@ -124,6 +102,7 @@ QAction *addCheckableAction(QMenu *menu, const QString &display, bool on, bool c
QStringList qtBuildPaths();
void addDebugInfoTask(unsigned id, const QString &cmd);
QWidget *addSearch(Utils::BaseTreeView *treeView);
} // namespace Internal
} // namespace Debugger

File diff suppressed because it is too large Load Diff

View File

@@ -29,7 +29,9 @@
#include "debuggerconstants.h"
#include "debuggeritem.h"
#include "debuggerprotocol.h"
#include "breakhandler.h"
#include <coreplugin/icontext.h>
#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/runconfiguration.h>
#include <texteditor/textmark.h>
@@ -41,12 +43,14 @@
QT_BEGIN_NAMESPACE
class QDebug;
class QPoint;
class QAbstractItemModel;
QT_END_NAMESPACE
namespace Core { class IOptionsPage; }
namespace Utils { class MacroExpander; }
namespace Utils {
class MacroExpander;
class Perspective;
} // Utils
namespace Debugger {
@@ -62,7 +66,9 @@ class DisassemblerAgent;
class MemoryAgent;
class WatchItem;
class BreakHandler;
class BreakpointParameters;
class LocationMark;
class LogWindow;
class ModulesHandler;
class RegisterHandler;
class StackHandler;
@@ -70,8 +76,7 @@ class StackFrame;
class SourceFilesHandler;
class ThreadsHandler;
class WatchHandler;
class Breakpoint;
class QmlCppEngine;
class WatchTreeView;
class DebuggerToolTipContext;
class MemoryViewSetupData;
class TerminalRunner;
@@ -157,7 +162,6 @@ public:
bool isCppDebugging() const;
bool isNativeMixedDebugging() const;
void validateExecutable();
Utils::MacroExpander *macroExpander = nullptr;
@@ -219,32 +223,21 @@ private:
quint64 m_address = 0;
};
enum LocationType { UnknownLocation, LocationByFile, LocationByAddress };
class ContextData
{
public:
bool isValid() const { return type != UnknownLocation; }
public:
LocationType type = UnknownLocation;
QString fileName;
int lineNumber = 0;
quint64 address = 0;
};
class DebuggerEngine : public QObject
{
Q_OBJECT
public:
explicit DebuggerEngine();
DebuggerEngine();
~DebuggerEngine() override;
const DebuggerRunParameters &runParameters() const;
void setRunTool(DebuggerRunTool *runTool);
void setRunParameters(const DebuggerRunParameters &runParameters);
virtual void setRunTool(DebuggerRunTool *runTool);
DebuggerRunTool *runTool() const;
const DebuggerRunParameters &runParameters() const;
bool isStartupRunConfiguration() const;
void setCompanionEngine(DebuggerEngine *engine);
void setSecondaryEngine();
void start();
@@ -269,6 +262,7 @@ public:
void updateWatchData(const QString &iname); // FIXME: Merge with above.
virtual void selectWatchData(const QString &iname);
virtual void validateExecutable() {}
virtual void prepareForRestart() {}
virtual void abortDebuggerProcess() {} // second attempt
@@ -308,39 +302,39 @@ public:
virtual void updateAll();
virtual void updateLocals();
virtual Core::Context languageContext() const { return {}; }
virtual QString displayName() const;
virtual bool stateAcceptsBreakpointChanges() const { return true; }
virtual void attemptBreakpointSynchronization();
virtual bool acceptsBreakpoint(Breakpoint bp) const = 0;
virtual void insertBreakpoint(Breakpoint bp); // FIXME: make pure
virtual void removeBreakpoint(Breakpoint bp); // FIXME: make pure
virtual void changeBreakpoint(Breakpoint bp); // FIXME: make pure
virtual void enableSubBreakpoint(const QString &locid, bool on);
virtual bool acceptsBreakpoint(const BreakpointParameters &bp) const = 0;
virtual void insertBreakpoint(const Breakpoint &bp) = 0;
virtual void removeBreakpoint(const Breakpoint &bp) = 0;
virtual void updateBreakpoint(const Breakpoint &bp) = 0;
virtual void enableSubBreakpoint(const SubBreakpoint &sbp, bool enabled);
virtual bool acceptsDebuggerCommands() const { return true; }
virtual void executeDebuggerCommand(const QString &command, DebuggerLanguages languages);
virtual void executeDebuggerCommand(const QString &command);
virtual void assignValueInDebugger(WatchItem *item,
const QString &expr, const QVariant &value);
virtual void selectThread(Internal::ThreadId threadId) = 0;
virtual Internal::ModulesHandler *modulesHandler() const;
virtual Internal::RegisterHandler *registerHandler() const;
virtual Internal::StackHandler *stackHandler() const;
virtual Internal::ThreadsHandler *threadsHandler() const;
virtual Internal::WatchHandler *watchHandler() const;
virtual Internal::SourceFilesHandler *sourceFilesHandler() const;
virtual Internal::BreakHandler *breakHandler() const;
virtual void executeRecordReverse(bool) {}
virtual void executeReverse(bool) {}
virtual QAbstractItemModel *modulesModel() const;
virtual QAbstractItemModel *registerModel() const;
virtual QAbstractItemModel *stackModel() const;
virtual QAbstractItemModel *threadsModel() const;
virtual QAbstractItemModel *watchModel() const;
virtual QAbstractItemModel *sourceFilesModel() const;
ModulesHandler *modulesHandler() const;
RegisterHandler *registerHandler() const;
StackHandler *stackHandler() const;
ThreadsHandler *threadsHandler() const;
WatchHandler *watchHandler() const;
SourceFilesHandler *sourceFilesHandler() const;
BreakHandler *breakHandler() const;
LogWindow *logWindow() const;
DisassemblerAgent *disassemblerAgent() const;
void progressPing();
bool debuggerActionsEnabled() const;
static bool debuggerActionsEnabled(DebuggerState state);
virtual bool companionPreventsActions() const;
DebuggerState state() const;
bool isDying() const;
@@ -349,7 +343,13 @@ public:
void notifyInferiorPid(const Utils::ProcessHandle &pid);
qint64 inferiorPid() const;
bool isReverseDebugging() const;
void handleBeginOfRecordingReached();
void handleRecordingFailed();
void handleRecordReverse(bool);
void handleReverseDirection(bool);
void handleCommand(int role, const QVariant &value);
// Convenience
@@ -359,30 +359,32 @@ public:
virtual void resetLocation();
virtual void gotoLocation(const Internal::Location &location);
void gotoCurrentLocation();
virtual void quitDebugger(); // called when pressing the stop button
void abortDebugger();
void updateViews();
bool isSlaveEngine() const;
bool isMasterEngine() const;
DebuggerEngine *masterEngine();
virtual DebuggerEngine *activeEngine() { return this; }
virtual DebuggerEngine *cppEngine() { return nullptr; }
bool isPrimaryEngine() const;
virtual bool canDisplayTooltip() const;
virtual void notifyInferiorIll();
QString toFileInProject(const QUrl &fileUrl);
void updateBreakpointMarker(const Breakpoint &bp);
void removeBreakpointMarker(const Breakpoint &bp);
QString expand(const QString &string) const;
QString nativeStartupCommands() const;
Utils::Perspective *perspective() const;
void updateMarkers();
signals:
void engineStarted();
void engineFinished();
void requestRunControlFinish();
void requestRunControlStop();
void attachToCoreRequested(const QString &coreFile);
void appendMessageRequested(const QString &msg,
Utils::OutputFormat format,
bool appendNewLine) const;
protected:
// The base notify*() function implementation should be sufficient
// in most cases, but engines are free to override them to do some
// engine specific cleanup like stopping timers etc.
void notifyEngineSetupOk();
void notifyEngineSetupFailed();
void notifyEngineRunFailed();
@@ -398,14 +400,60 @@ protected:
void notifyInferiorRunOk();
void notifyInferiorRunFailed();
void notifyInferiorIll();
void notifyInferiorExited();
void notifyInferiorStopOk();
void notifyInferiorSpontaneousStop();
void notifyInferiorStopFailed();
public: // FIXME: Remove, currently needed for Android.
void notifyInferiorExited();
public:
void updateState(bool alsoUpdateCompanion);
QString formatStartParameters() const;
WatchTreeView *inspectorView();
void updateLocalsWindow(bool showReturn);
void raiseWatchersWindow();
protected:
bool isRegistersWindowVisible() const;
bool isModulesWindowVisible() const;
void setThreadBoxContents(const QStringList &list, int index);
void openMemoryEditor();
void handleExecDetach();
void handleExecContinue();
void handleExecInterrupt();
void handleUserStop();
void handleAbort();
void handleReset();
void handleExecStep();
void handleExecNext();
void handleExecStepOut();
void handleExecReturn();
void handleExecJumpToLine();
void handleExecRunToLine();
void handleExecRunToSelectedFunction();
void handleAddToWatchWindow();
void handleFrameDown();
void handleFrameUp();
void handleOperateByInstructionTriggered(bool operateByInstructionTriggered);
// Breakpoint state transitions
void notifyBreakpointInsertProceeding(const Breakpoint &bp);
void notifyBreakpointInsertOk(const Breakpoint &bp);
void notifyBreakpointInsertFailed(const Breakpoint &bp);
void notifyBreakpointChangeOk(const Breakpoint &bp);
void notifyBreakpointChangeProceeding(const Breakpoint &bp);
void notifyBreakpointChangeFailed(const Breakpoint &bp);
void notifyBreakpointPending(const Breakpoint &bp);
void notifyBreakpointRemoveProceeding(const Breakpoint &bp);
void notifyBreakpointRemoveOk(const Breakpoint &bp);
void notifyBreakpointRemoveFailed(const Breakpoint &bp);
void notifyBreakpointNeedsReinsertion(const Breakpoint &bp);
protected:
void setDebuggerName(const QString &name);
void notifyDebuggerProcessFinished(int exitCode, QProcess::ExitStatus exitStatus,
const QString &backendName);
@@ -424,29 +472,27 @@ protected:
virtual void shutdownEngine() = 0;
virtual void resetInferior() {}
virtual void detachDebugger();
virtual void executeStep();
virtual void executeStepOut();
virtual void executeNext();
virtual void executeStepI();
virtual void executeNextI();
virtual void executeReturn();
virtual void detachDebugger() {}
virtual void executeStep() {}
virtual void executeStepOut() {}
virtual void executeNext() {}
virtual void executeStepI() {}
virtual void executeNextI() {}
virtual void executeReturn() {}
virtual void continueInferior();
virtual void interruptInferior();
virtual void continueInferior() {}
virtual void interruptInferior() {}
void requestInterruptInferior();
virtual void executeRunToLine(const Internal::ContextData &data);
virtual void executeRunToFunction(const QString &functionName);
virtual void executeJumpToLine(const Internal::ContextData &data);
virtual void executeRunToLine(const Internal::ContextData &) {}
virtual void executeRunToFunction(const QString &) {}
virtual void executeJumpToLine(const Internal::ContextData &) {}
virtual void frameUp();
virtual void frameDown();
virtual void doUpdateLocals(const UpdateParameters &params);
void setMasterEngine(DebuggerEngine *masterEngine);
TerminalRunner *terminal() const;
static QString msgStopped(const QString &reason = QString());
@@ -457,35 +503,43 @@ protected:
bool showStoppedBySignalMessageBox(const QString meaning, QString name);
void showStoppedByExceptionMessageBox(const QString &description);
virtual void setupSlaveEngine();
virtual void runSlaveEngine();
virtual void shutdownSlaveEngine();
virtual void slaveEngineStateChanged(DebuggerEngine *engine,
DebuggerState state);
void updateLocalsView(const GdbMi &all);
void checkState(DebuggerState state, const char *file, int line);
bool isNativeMixedEnabled() const;
bool isNativeMixedActive() const;
bool isNativeMixedActiveFrame() const;
void startDying() const;
protected:
DebuggerRunParameters &mutableRunParameters() const;
ProjectExplorer::IDevice::ConstPtr device() const;
DebuggerEngine *companionEngine() const;
private:
// Wrapper engine needs access to state of its subengines.
friend class QmlCppEngine;
friend class DebuggerPluginPrivate;
friend class DebuggerEnginePrivate;
friend class LocationMark;
DebuggerEnginePrivate *d;
};
class CppDebuggerEngine : public DebuggerEngine
{
public:
CppDebuggerEngine() {}
~CppDebuggerEngine() override {}
void validateExecutable() override;
Core::Context languageContext() const override;
};
class LocationMark : public TextEditor::TextMark
{
public:
LocationMark(DebuggerEngine *engine, const Utils::FileName &file, int line);
void removedFromEditor() override { updateLineNumber(0); }
void updateIcon();
private:
bool isDraggable() const override;
void dragToLine(int line) override;

View File

@@ -81,9 +81,18 @@ const Icon DEBUG_EXIT_SMALL_TOOLBAR({
const Icon LOCATION({
{":/debugger/images/location_background.png", Theme::IconsCodeModelOverlayForegroundColor},
{":/debugger/images/location.png", Theme::IconsWarningToolBarColor}}, Icon::Tint);
const Icon REVERSE_LOCATION({
{":/debugger/images/debugger_reversemode_background.png", Theme::IconsCodeModelOverlayForegroundColor},
{":/debugger/images/debugger_reversemode.png", Theme::IconsWarningToolBarColor}}, Icon::Tint);
const Icon REVERSE_MODE({
{":/debugger/images/debugger_reversemode_background.png", Theme::IconsCodeModelOverlayForegroundColor},
{":/debugger/images/debugger_reversemode.png", Theme::IconsInfoColor}}, Icon::Tint);
const Icon DIRECTION_BACKWARD({
{":/debugger/images/debugger_reversemode_background.png", Theme::IconsCodeModelOverlayForegroundColor},
{":/debugger/images/debugger_reversemode.png", Theme::IconsInfoColor}}, Icon::Tint);
const Icon DIRECTION_FORWARD({
{":/debugger/images/location_background.png", Theme::IconsCodeModelOverlayForegroundColor},
{":/debugger/images/location.png", Theme::IconsInfoColor}}, Icon::Tint);
const Icon APP_ON_TOP({
{":/utils/images/app-on-top.png", Theme::PanelTextColorMid}}, Icon::MenuTintedStyle);
const Icon APP_ON_TOP_TOOLBAR({

View File

@@ -55,7 +55,10 @@ extern const Utils::Icon DEBUG_INTERRUPT_SMALL_TOOLBAR;
extern const Utils::Icon DEBUG_EXIT_SMALL;
extern const Utils::Icon DEBUG_EXIT_SMALL_TOOLBAR;
extern const Utils::Icon LOCATION;
extern const Utils::Icon REVERSE_LOCATION;
extern const Utils::Icon REVERSE_MODE;
extern const Utils::Icon DIRECTION_FORWARD;
extern const Utils::Icon DIRECTION_BACKWARD;
extern const Utils::Icon APP_ON_TOP;
extern const Utils::Icon APP_ON_TOP_TOOLBAR;
extern const Utils::Icon SELECT;

View File

@@ -31,16 +31,18 @@ namespace Debugger {
namespace Internal {
// DebuggerMainWindow dock widget names
const char DOCKWIDGET_BREAKPOINTMANAGER[] = "Debugger.Docks.BreakpointManager";
const char DOCKWIDGET_ENGINEMANAGER[] = "Debugger.Docks.Snapshots";
const char DOCKWIDGET_GLOBALLOG[] = "Debugger.Docks.GlobalLog";
const char DOCKWIDGET_BREAK[] = "Debugger.Docks.Break";
const char DOCKWIDGET_MODULES[] = "Debugger.Docks.Modules";
const char DOCKWIDGET_REGISTER[] = "Debugger.Docks.Register";
const char DOCKWIDGET_OUTPUT[] = "Debugger.Docks.Output";
const char DOCKWIDGET_SNAPSHOTS[] = "Debugger.Docks.Snapshots";
const char DOCKWIDGET_STACK[] = "Debugger.Docks.Stack";
const char DOCKWIDGET_SOURCE_FILES[] = "Debugger.Docks.SourceFiles";
const char DOCKWIDGET_THREADS[] = "Debugger.Docks.Threads";
const char DOCKWIDGET_LOCALS_AND_INSPECTOR[] = "Debugger.Docks.LocalsAndInspector";
const char DOCKWIDGET_WATCHERS[] = "Debugger.Docks.Watchers";
} // namespace Internal

View File

@@ -49,6 +49,7 @@
#include <QAction>
#include <QComboBox>
#include <QDebug>
#include <QDockWidget>
#include <QHBoxLayout>
#include <QMenu>
@@ -64,196 +65,154 @@ namespace Utils {
const char LAST_PERSPECTIVE_KEY[] = "LastPerspective";
DebuggerMainWindow::DebuggerMainWindow()
static DebuggerMainWindow *theMainWindow = nullptr;
class ToolbarOperation
{
public:
void attachToToolbar(QWidget *parent)
{
QLayout *layout = parent->layout();
if (toolBarWidget) {
toolBarWidget->setParent(parent);
layout->addWidget(toolBarWidget);
}
if (toolBarAction) {
toolBarAction->m_toolButton = new QToolButton(parent);
toolBarAction->m_toolButton->setToolButtonStyle(toolBarAction->m_toolButtonStyle);
toolBarAction->m_toolButton->setProperty("panelwidget", true);
toolBarAction->m_toolButton->setDefaultAction(toolBarAction);
toolBarAction->m_toolButton->setVisible(toolBarAction->isVisible());
layout->addWidget(toolBarAction->m_toolButton);
}
if (toolBarPlainAction) {
toolBarPlainActionButton = new QToolButton(parent);
toolBarPlainActionButton->setProperty("panelwidget", true);
toolBarPlainActionButton->setDefaultAction(toolBarPlainAction);
layout->addWidget(toolBarPlainActionButton);
}
if (separator) {
separator->setParent(parent);
layout->addWidget(separator);
}
}
void detachFromToolbar()
{
if (toolBarWidget) {
toolBarWidget->setParent(nullptr);
}
if (toolBarAction) {
delete toolBarAction->m_toolButton;
toolBarAction->m_toolButton = nullptr;
}
if (toolBarPlainAction) {
delete toolBarPlainActionButton;
toolBarPlainActionButton = nullptr;
}
if (separator) {
separator->setParent(nullptr);
}
}
QPointer<QAction> toolBarPlainAction; // Owned by plugin if present.
QPointer<OptionalAction> toolBarAction; // Owned by plugin if present.
QPointer<QWidget> toolBarWidget; // Owned by plugin if present.
// Helper/wrapper widget owned by us if present.
QPointer<QToolButton> toolBarPlainActionButton;
QPointer<QWidget> toolBarWidgetParent;
QPointer<QWidget> separator;
};
class DockOperation
{
public:
QPointer<QWidget> widget;
QByteArray anchorDockId;
Perspective::OperationType operationType = Perspective::Raise;
bool visibleByDefault = true;
Qt::DockWidgetArea area = Qt::BottomDockWidgetArea;
};
class PerspectivePrivate
{
public:
~PerspectivePrivate() { destroyToolBar(); }
void showToolBar();
void hideToolBar();
void destroyToolBar();
QString m_id;
QString m_name;
QByteArray m_parentPerspective;
QVector<DockOperation> m_dockOperations;
QVector<ToolbarOperation> m_toolBarOperations;
QPointer<QWidget> m_centralWidget;
Perspective::Callback m_aboutToActivateCallback;
QPointer<QWidget> m_toolButtonBox;
};
class DebuggerMainWindowPrivate : public QObject
{
public:
DebuggerMainWindowPrivate(DebuggerMainWindow *parent);
~DebuggerMainWindowPrivate();
void ensureToolBarDockExists();
void restorePerspective(Perspective *perspective);
void loadPerspectiveHelper(Perspective *perspective, bool fromStoredSettings = true);
void savePerspectiveHelper(const Perspective *perspective);
void destroyPerspective(Perspective *perspective);
void registerPerspective(Perspective *perspective);
void increaseChooserWidthIfNecessary(const QString &visibleName);
void resetCurrentPerspective();
Perspective *findPerspective(const QByteArray &perspectiveId) const;
int indexInChooser(Perspective *perspective) const;
DebuggerMainWindow *q = nullptr;
Perspective *m_currentPerspective = nullptr;
QComboBox *m_perspectiveChooser = nullptr;
QStackedWidget *m_centralWidgetStack = nullptr;
QHBoxLayout *m_toolBarLayout = nullptr;
QWidget *m_editorPlaceHolder = nullptr;
Utils::StatusLabel *m_statusLabel = nullptr;
QDockWidget *m_toolBarDock = nullptr;
QHash<QByteArray, QDockWidget *> m_dockForDockId;
QList<Perspective *> m_perspectives;
};
DebuggerMainWindowPrivate::DebuggerMainWindowPrivate(DebuggerMainWindow *parent)
: q(parent)
{
m_centralWidgetStack = new QStackedWidget;
m_statusLabel = new Utils::StatusLabel;
m_statusLabel->setProperty("panelwidget", true);
m_editorPlaceHolder = new EditorManagerPlaceHolder;
m_perspectiveChooser = new QComboBox;
m_perspectiveChooser->setObjectName(QLatin1String("PerspectiveChooser"));
m_perspectiveChooser->setProperty("panelwidget", true);
connect(m_perspectiveChooser, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated),
this, [this](int item) {
restorePerspective(findPerspective(m_perspectiveChooser->itemData(item).toByteArray()));
restorePerspective(q->findPerspective(m_perspectiveChooser->itemData(item).toByteArray()));
});
}
DebuggerMainWindow::DebuggerMainWindow()
: d(new DebuggerMainWindowPrivate(this))
{
theMainWindow = this;
setDockNestingEnabled(true);
setDockActionsVisible(false);
setDocumentMode(true);
connect(this, &FancyMainWindow::resetLayout,
this, &DebuggerMainWindow::resetCurrentPerspective);
}
DebuggerMainWindow::~DebuggerMainWindow()
{
savePerspectiveHelper(m_currentPerspective);
delete m_editorPlaceHolder;
m_editorPlaceHolder = nullptr;
// As we have to setParent(0) on dock widget that are not selected,
// we keep track of all and make sure we don't leak any
foreach (QDockWidget *dock, m_dockForDockId) {
if (dock && !dock->parentWidget())
delete dock;
}
foreach (const Perspective *perspective, m_perspectives)
delete perspective;
}
void DebuggerMainWindow::registerPerspective(Perspective *perspective)
{
m_perspectives.append(perspective);
QByteArray parentPerspective = perspective->parentPerspective();
// Add "main" perspectives to the chooser.
if (parentPerspective.isEmpty()) {
m_perspectiveChooser->addItem(perspective->name(), perspective->m_id);
increaseChooserWidthIfNecessary(perspective->name());
}
}
void DebuggerMainWindow::increaseChooserWidthIfNecessary(const QString &visibleName)
{
const int oldWidth = m_perspectiveChooser->width();
const int contentWidth = m_perspectiveChooser->fontMetrics().width(visibleName);
QStyleOptionComboBox option;
option.initFrom(m_perspectiveChooser);
const QSize sz(contentWidth, 1);
const int width = m_perspectiveChooser->style()->sizeFromContents(
QStyle::CT_ComboBox, &option, sz).width();
if (width > oldWidth)
m_perspectiveChooser->setFixedWidth(width);
}
void DebuggerMainWindow::destroyDynamicPerspective(Perspective *perspective)
{
QTC_ASSERT(perspective, return);
savePerspectiveHelper(perspective);
m_perspectives.removeAll(perspective);
// Dynamic perspectives are currently not visible in the chooser.
// This might change in the future, make sure we notice.
const int idx = indexInChooser(perspective);
QTC_ASSERT(idx == -1, m_perspectiveChooser->removeItem(idx));
QByteArray parentPerspective = perspective->parentPerspective();
delete perspective;
// All dynamic perspectives currently have a static parent perspective.
// This might change in the future, make sure we notice.
QTC_CHECK(!parentPerspective.isEmpty());
restorePerspective(findPerspective(parentPerspective));
}
void DebuggerMainWindow::showStatusMessage(const QString &message, int timeoutMS)
{
m_statusLabel->showStatusMessage(message, timeoutMS);
}
void DebuggerMainWindow::raiseDock(const QByteArray &dockId)
{
QDockWidget *dock = m_dockForDockId.value(dockId);
QTC_ASSERT(dock, return);
QAction *act = dock->toggleViewAction();
if (!act->isChecked())
QTimer::singleShot(1, act, [act] { act->trigger(); });
dock->raise();
}
QByteArray DebuggerMainWindow::currentPerspective() const
{
return m_currentPerspective ? m_currentPerspective->m_id : QByteArray();
}
void DebuggerMainWindow::onModeChanged(Core::Id mode)
{
if (mode == Debugger::Constants::MODE_DEBUG) {
setDockActionsVisible(true);
restorePerspective(nullptr);
} else {
setDockActionsVisible(false);
// Hide dock widgets manually in case they are floating.
foreach (QDockWidget *dockWidget, dockWidgets()) {
if (dockWidget->isFloating())
dockWidget->hide();
}
}
}
void DebuggerMainWindow::setPerspectiveEnabled(const QByteArray &perspectiveId, bool enabled)
{
Perspective *perspective = findPerspective(perspectiveId);
const int index = indexInChooser(perspective);
QTC_ASSERT(index != -1, return);
auto model = qobject_cast<QStandardItemModel*>(m_perspectiveChooser->model());
QTC_ASSERT(model, return);
QStandardItem *item = model->item(index, 0);
item->setFlags(enabled ? item->flags() | Qt::ItemIsEnabled : item->flags() & ~Qt::ItemIsEnabled );
}
Perspective *DebuggerMainWindow::findPerspective(const QByteArray &perspectiveId) const
{
return Utils::findOr(m_perspectives, nullptr, [&](Perspective *perspective) {
return perspective->m_id == perspectiveId;
});
}
void DebuggerMainWindow::resetCurrentPerspective()
{
loadPerspectiveHelper(m_currentPerspective, false);
}
int DebuggerMainWindow::indexInChooser(Perspective *perspective) const
{
return perspective ? m_perspectiveChooser->findData(perspective->m_id) : -1;
}
void DebuggerMainWindow::restorePerspective(Perspective *perspective)
{
loadPerspectiveHelper(perspective, true);
const int index = indexInChooser(m_currentPerspective);
if (index != -1)
m_perspectiveChooser->setCurrentIndex(index);
}
void DebuggerMainWindow::finalizeSetup()
{
auto viewButton = new QToolButton;
viewButton->setText(tr("&Views"));
auto closeButton = new QToolButton();
closeButton->setIcon(Utils::Icons::CLOSE_SPLIT_BOTTOM.icon());
closeButton->setToolTip(tr("Leave Debug Mode"));
auto toolbuttonBox = new QWidget;
m_toolbuttonBoxLayout = new QHBoxLayout(toolbuttonBox);
m_toolbuttonBoxLayout->setMargin(0);
m_toolbuttonBoxLayout->setSpacing(0);
auto toolbar = new Utils::StyledBar;
toolbar->setProperty("topBorder", true);
auto hbox = new QHBoxLayout(toolbar);
hbox->setMargin(0);
hbox->setSpacing(0);
hbox->addWidget(m_perspectiveChooser);
hbox->addWidget(toolbuttonBox);
hbox->addStretch(3);
hbox->addWidget(m_statusLabel);
hbox->addStretch(1);
hbox->addWidget(new Utils::StyledSeparator);
hbox->addWidget(viewButton);
hbox->addWidget(closeButton);
connect(viewButton, &QAbstractButton::clicked, [this, viewButton] {
QMenu menu;
addDockActionsToMenu(&menu);
menu.exec(viewButton->mapToGlobal(QPoint()));
});
connect(closeButton, &QAbstractButton::clicked, [] {
ModeManager::activateMode(Core::Constants::MODE_EDIT);
});
d, &DebuggerMainWindowPrivate::resetCurrentPerspective);
Context debugcontext(Debugger::Constants::C_DEBUGMODE);
@@ -279,6 +238,156 @@ void DebuggerMainWindow::finalizeSetup()
"Debugger.Views.ResetSimple", debugcontext);
cmd->setAttribute(Command::CA_Hide);
viewsMenu->addAction(cmd, Core::Constants::G_DEFAULT_THREE);
}
DebuggerMainWindow::~DebuggerMainWindow()
{
delete d;
theMainWindow = nullptr;
}
DebuggerMainWindowPrivate::~DebuggerMainWindowPrivate()
{
savePerspectiveHelper(m_currentPerspective);
delete m_editorPlaceHolder;
m_editorPlaceHolder = nullptr;
}
void DebuggerMainWindow::registerPerspective(Perspective *perspective)
{
d->registerPerspective(perspective);
}
void DebuggerMainWindowPrivate::registerPerspective(Perspective *perspective)
{
m_perspectives.append(perspective);
QByteArray parentPerspective = perspective->d->m_parentPerspective;
// Add "main" perspectives to the chooser.
if (parentPerspective.isEmpty()) {
m_perspectiveChooser->addItem(perspective->name(), perspective->id());
increaseChooserWidthIfNecessary(perspective->name());
}
}
void DebuggerMainWindowPrivate::increaseChooserWidthIfNecessary(const QString &visibleName)
{
const int oldWidth = m_perspectiveChooser->width();
const int contentWidth = m_perspectiveChooser->fontMetrics().width(visibleName);
QStyleOptionComboBox option;
option.initFrom(m_perspectiveChooser);
const QSize sz(contentWidth, 1);
const int width = m_perspectiveChooser->style()->sizeFromContents(
QStyle::CT_ComboBox, &option, sz).width();
if (width > oldWidth)
m_perspectiveChooser->setFixedWidth(width);
}
void DebuggerMainWindowPrivate::destroyPerspective(Perspective *perspective)
{
savePerspectiveHelper(perspective);
m_perspectives.removeAll(perspective);
// Dynamic perspectives are currently not visible in the chooser.
// This might change in the future, make sure we notice.
const int idx = indexInChooser(perspective);
QTC_ASSERT(idx == -1, m_perspectiveChooser->removeItem(idx));
// All dynamic perspectives currently have a static parent perspective.
// This might change in the future, make sure we notice.
QTC_CHECK(!perspective->d->m_parentPerspective.isEmpty());
restorePerspective(findPerspective(perspective->d->m_parentPerspective));
}
void DebuggerMainWindow::showStatusMessage(const QString &message, int timeoutMS)
{
d->m_statusLabel->showStatusMessage(message, timeoutMS);
}
void DebuggerMainWindow::onModeChanged(Core::Id mode)
{
if (mode == Debugger::Constants::MODE_DEBUG) {
setDockActionsVisible(true);
d->restorePerspective(nullptr);
} else {
setDockActionsVisible(false);
// Hide dock widgets manually in case they are floating.
foreach (QDockWidget *dockWidget, dockWidgets()) {
if (dockWidget->isFloating())
dockWidget->hide();
}
}
}
Perspective *DebuggerMainWindow::findPerspective(const QByteArray &perspectiveId) const
{
return d->findPerspective(perspectiveId);
}
QWidget *DebuggerMainWindow::centralWidgetStack()
{
return d->m_centralWidgetStack;
}
Perspective *DebuggerMainWindowPrivate::findPerspective(const QByteArray &perspectiveId) const
{
return Utils::findOr(m_perspectives, nullptr, [&](Perspective *perspective) {
return perspective->d->m_id.toUtf8() == perspectiveId;
});
}
void DebuggerMainWindow::closeEvent(QCloseEvent *)
{
d->savePerspectiveHelper(d->m_currentPerspective);
}
void DebuggerMainWindowPrivate::resetCurrentPerspective()
{
loadPerspectiveHelper(m_currentPerspective, false);
}
int DebuggerMainWindowPrivate::indexInChooser(Perspective *perspective) const
{
return perspective ? m_perspectiveChooser->findData(perspective->d->m_id) : -1;
}
void DebuggerMainWindowPrivate::restorePerspective(Perspective *perspective)
{
loadPerspectiveHelper(perspective, true);
const int index = indexInChooser(m_currentPerspective);
if (index != -1)
m_perspectiveChooser->setCurrentIndex(index);
}
void DebuggerMainWindowPrivate::ensureToolBarDockExists()
{
if (m_toolBarDock)
return;
auto viewButton = new QToolButton;
viewButton->setText(tr("&Views"));
auto closeButton = new QToolButton();
closeButton->setIcon(Utils::Icons::CLOSE_SPLIT_BOTTOM.icon());
closeButton->setToolTip(tr("Leave Debug Mode"));
auto toolbar = new Utils::StyledBar;
toolbar->setProperty("topBorder", true);
auto hbox = new QHBoxLayout(toolbar);
m_toolBarLayout = hbox;
hbox->setMargin(0);
hbox->setSpacing(0);
hbox->addWidget(m_perspectiveChooser);
// <- All perspective toolbars will get inserted here, but only
// the current perspective's toolbar is set visible.
hbox->addStretch(3);
hbox->addWidget(m_statusLabel);
hbox->addStretch(1);
hbox->addWidget(new Utils::StyledSeparator);
hbox->addWidget(viewButton);
hbox->addWidget(closeButton);
auto dock = new QDockWidget(tr("Toolbar"));
dock->setObjectName(QLatin1String("Toolbar"));
@@ -287,9 +396,18 @@ void DebuggerMainWindow::finalizeSetup()
dock->setTitleBarWidget(new QWidget(dock)); // hide title bar
dock->setProperty("managed_dockwidget", QLatin1String("true"));
dock->setWidget(toolbar);
m_toolbarDock = dock;
m_toolBarDock = dock;
q->addDockWidget(Qt::BottomDockWidgetArea, m_toolBarDock);
addDockWidget(Qt::BottomDockWidgetArea, dock);
connect(viewButton, &QAbstractButton::clicked, [this, viewButton] {
QMenu menu;
q->addDockActionsToMenu(&menu);
menu.exec(viewButton->mapToGlobal(QPoint()));
});
connect(closeButton, &QAbstractButton::clicked, [] {
ModeManager::activateMode(Core::Constants::MODE_EDIT);
});
}
QWidget *createModeWindow(const Core::Id &mode, DebuggerMainWindow *mainWindow)
@@ -342,13 +460,12 @@ QWidget *createModeWindow(const Core::Id &mode, DebuggerMainWindow *mainWindow)
return splitter;
}
void DebuggerMainWindow::loadPerspectiveHelper(Perspective *perspective, bool fromStoredSettings)
void DebuggerMainWindowPrivate::loadPerspectiveHelper(Perspective *perspective, bool fromStoredSettings)
{
// Clean up old perspective.
if (m_currentPerspective) {
savePerspectiveHelper(m_currentPerspective);
for (QDockWidget *dock : m_dockForDockId) {
QTC_ASSERT(dock, continue);
dock->setParent(nullptr);
dock->widget()->setParent(nullptr);
ActionManager::unregisterAction(dock->toggleViewAction(),
@@ -357,22 +474,11 @@ void DebuggerMainWindow::loadPerspectiveHelper(Perspective *perspective, bool fr
}
m_dockForDockId.clear();
ICore::removeAdditionalContext(Context(Id::fromName(m_currentPerspective->m_id)));
ICore::removeAdditionalContext(m_currentPerspective->context());
QWidget *central = m_currentPerspective->centralWidget();
m_centralWidgetStack->removeWidget(central ? central : m_editorPlaceHolder);
// Detach potentially re-used widgets to prevent deletion.
for (const Perspective::ToolbarOperation &op : m_currentPerspective->m_toolbarOperations) {
if (op.widget)
op.widget->setParent(nullptr);
if (op.toolbutton)
op.toolbutton->setParent(nullptr);
if (op.separator)
op.separator->setParent(nullptr);
}
while (QLayoutItem *item = m_toolbuttonBoxLayout->takeAt(0))
delete item;
m_currentPerspective->d->destroyToolBar();
}
if (perspective) {
@@ -380,33 +486,25 @@ void DebuggerMainWindow::loadPerspectiveHelper(Perspective *perspective, bool fr
} else {
const QSettings *settings = ICore::settings();
m_currentPerspective = findPerspective(settings->value(QLatin1String(LAST_PERSPECTIVE_KEY)).toByteArray());
if (!m_currentPerspective)
m_currentPerspective = findPerspective(Debugger::Constants::CppPerspectiveId);
// If we don't find a perspective with the stored name, pick any.
// This can happen e.g. when a plugin was disabled that provided
// the stored perspective, or when the save file was modified externally.
if (!m_currentPerspective && !m_perspectives.isEmpty())
m_currentPerspective = m_perspectives.first();
}
QTC_ASSERT(m_currentPerspective, return);
ICore::addAdditionalContext(Context(Id::fromName(m_currentPerspective->m_id)));
ICore::addAdditionalContext(m_currentPerspective->context());
m_currentPerspective->aboutToActivate();
for (const Perspective::ToolbarOperation &op : m_currentPerspective->m_toolbarOperations) {
if (op.widget)
m_toolbuttonBoxLayout->addWidget(op.widget);
if (op.toolbutton)
m_toolbuttonBoxLayout->addWidget(op.toolbutton);
if (op.separator)
m_toolbuttonBoxLayout->addWidget(op.separator);
}
for (const Perspective::Operation &op : m_currentPerspective->m_operations) {
for (const DockOperation &op : m_currentPerspective->d->m_dockOperations) {
QTC_ASSERT(op.widget, continue);
const QByteArray dockId = op.widget->objectName().toUtf8();
QDockWidget *dock = m_dockForDockId.value(dockId);
if (!dock) {
QTC_CHECK(!dockId.isEmpty());
dock = addDockForWidget(op.widget);
dock = q->addDockForWidget(op.widget);
m_dockForDockId[dockId] = dock;
QAction *toggleViewAction = dock->toggleViewAction();
@@ -414,167 +512,268 @@ void DebuggerMainWindow::loadPerspectiveHelper(Perspective *perspective, bool fr
Command *cmd = ActionManager::registerAction(toggleViewAction,
Id("Dock.").withSuffix(dock->objectName()),
Context(Id::fromName(m_currentPerspective->m_id)));
m_currentPerspective->context());
cmd->setAttribute(Command::CA_Hide);
ActionManager::actionContainer(Core::Constants::M_WINDOW_VIEWS)->addAction(cmd);
}
// Restore parent/child relation, so that the widget hierarchy is clear.
dock->setParent(this);
dock->setParent(q);
if (op.operationType == Perspective::Raise) {
dock->raise();
continue;
}
addDockWidget(op.area, dock);
q->addDockWidget(op.area, dock);
QDockWidget *anchor = m_dockForDockId.value(op.anchorDockId);
if (!anchor && op.area == Qt::BottomDockWidgetArea)
anchor = m_toolbarDock;
if (!anchor && op.area == Qt::BottomDockWidgetArea) {
ensureToolBarDockExists();
anchor = m_toolBarDock;
}
if (anchor) {
switch (op.operationType) {
case Perspective::AddToTab:
tabifyDockWidget(anchor, dock);
q->tabifyDockWidget(anchor, dock);
break;
case Perspective::SplitHorizontal:
splitDockWidget(anchor, dock, Qt::Horizontal);
q->splitDockWidget(anchor, dock, Qt::Horizontal);
break;
case Perspective::SplitVertical:
splitDockWidget(anchor, dock, Qt::Vertical);
q->splitDockWidget(anchor, dock, Qt::Vertical);
break;
default:
break;
}
}
if (!op.visibleByDefault)
dock->hide();
else
dock->show();
dock->setVisible(op.visibleByDefault);
}
m_currentPerspective->d->showToolBar();
if (fromStoredSettings) {
QSettings *settings = ICore::settings();
settings->beginGroup(QString::fromLatin1(m_currentPerspective->m_id));
settings->beginGroup(m_currentPerspective->d->m_id);
if (settings->value(QLatin1String("ToolSettingsSaved"), false).toBool())
restoreSettings(settings);
q->restoreSettings(settings);
settings->endGroup();
} else {
// By default, show the central widget
showCentralWidgetAction()->setChecked(true);
q->showCentralWidgetAction()->setChecked(true);
}
QWidget *central = m_currentPerspective->centralWidget();
m_centralWidgetStack->addWidget(central ? central : m_editorPlaceHolder);
showCentralWidgetAction()->setText(central ? central->windowTitle() : tr("Editor"));
q->showCentralWidgetAction()->setText(central ? central->windowTitle() : tr("Editor"));
m_statusLabel->clear();
}
void DebuggerMainWindow::savePerspectiveHelper(const Perspective *perspective)
void DebuggerMainWindowPrivate::savePerspectiveHelper(const Perspective *perspective)
{
if (!perspective)
return;
QSettings *settings = ICore::settings();
settings->beginGroup(QString::fromLatin1(perspective->m_id));
saveSettings(settings);
settings->beginGroup(perspective->d->m_id);
q->saveSettings(settings);
settings->setValue(QLatin1String("ToolSettingsSaved"), true);
settings->endGroup();
settings->setValue(QLatin1String(LAST_PERSPECTIVE_KEY), perspective->m_id);
settings->setValue(QLatin1String(LAST_PERSPECTIVE_KEY), perspective->d->m_id);
}
// Perspective
Perspective::Perspective(const QByteArray &id, const QString &name)
: m_id(id), m_name(name)
Perspective::Perspective(const QString &id, const QString &name)
: d(new PerspectivePrivate)
{
d->m_id = id;
d->m_name = name;
}
Perspective::~Perspective()
{
for (const ToolbarOperation &op : m_toolbarOperations) {
// op.widget and op.actions are owned by the plugins
delete op.toolbutton;
delete op.separator;
}
delete d;
}
void Perspective::setCentralWidget(QWidget *centralWidget)
{
QTC_ASSERT(m_centralWidget == nullptr, return);
m_centralWidget = centralWidget;
QTC_ASSERT(d->m_centralWidget == nullptr, return);
d->m_centralWidget = centralWidget;
}
QString Perspective::name() const
{
return m_name;
return d->m_name;
}
QString Perspective::id() const
{
return d->m_id;
}
void Perspective::setAboutToActivateCallback(const Perspective::Callback &cb)
{
m_aboutToActivateCallback = cb;
d->m_aboutToActivateCallback = cb;
}
void Perspective::aboutToActivate() const
{
if (m_aboutToActivateCallback)
m_aboutToActivateCallback();
}
QByteArray Perspective::parentPerspective() const
{
return m_parentPerspective;
if (d->m_aboutToActivateCallback)
d->m_aboutToActivateCallback();
}
void Perspective::setParentPerspective(const QByteArray &parentPerspective)
{
m_parentPerspective = parentPerspective;
d->m_parentPerspective = parentPerspective;
}
QToolButton *Perspective::addToolbarAction(QAction *action, const QIcon &toolbarIcon)
void Perspective::setEnabled(bool enabled)
{
ToolbarOperation op;
op.action = action;
op.icon = toolbarIcon;
op.toolbutton = new QToolButton;
// QStyle::polish is called before it is added to the toolbar, explicitly make it a panel widget
op.toolbutton->setProperty("panelwidget", true);
op.toolbutton->setDefaultAction(toolbarIcon.isNull()
? action : ProxyAction::proxyActionWithIcon(action, toolbarIcon));
m_toolbarOperations.append(op);
return op.toolbutton;
const int index = theMainWindow->d->indexInChooser(this);
QTC_ASSERT(index != -1, return);
auto model = qobject_cast<QStandardItemModel*>(theMainWindow->d->m_perspectiveChooser->model());
QTC_ASSERT(model, return);
QStandardItem *item = model->item(index, 0);
item->setFlags(enabled ? item->flags() | Qt::ItemIsEnabled : item->flags() & ~Qt::ItemIsEnabled );
}
void Perspective::addToolbarWidget(QWidget *widget)
void Perspective::addToolBarAction(QAction *action)
{
auto toolButton = new QToolButton;
ToolbarOperation op;
op.toolBarPlainAction = action;
op.toolBarPlainActionButton = toolButton;
d->m_toolBarOperations.append(op);
}
void Perspective::addToolBarAction(OptionalAction *action)
{
ToolbarOperation op;
op.widget = widget;
op.toolBarAction = action;
d->m_toolBarOperations.append(op);
}
void Perspective::addToolBarWidget(QWidget *widget)
{
ToolbarOperation op;
op.toolBarWidget = widget;
// QStyle::polish is called before it is added to the toolbar, explicitly make it a panel widget
op.widget->setProperty("panelwidget", true);
m_toolbarOperations.append(op);
op.toolBarWidget->setProperty("panelwidget", true);
d->m_toolBarOperations.append(op);
}
void Perspective::addToolbarSeparator()
{
ToolbarOperation op;
op.separator = new StyledSeparator;
m_toolbarOperations.append(op);
d->m_toolBarOperations.append(op);
}
QWidget *Perspective::centralWidget() const
{
return m_centralWidget;
return d->m_centralWidget;
}
void Perspective::addWindow(QWidget *widget, OperationType type, QWidget *anchorWidget,
bool visibleByDefault, Qt::DockWidgetArea area)
void Perspective::destroy()
{
Operation op;
theMainWindow->d->destroyPerspective(this);
}
Perspective *Perspective::currentPerspective()
{
return theMainWindow ? theMainWindow->d->m_currentPerspective : nullptr;
}
Context Perspective::context() const
{
return Context(Id::fromName(d->m_id.toUtf8()));
}
void PerspectivePrivate::showToolBar()
{
if (!m_toolButtonBox) {
m_toolButtonBox = new QWidget;
theMainWindow->d->m_toolBarLayout->insertWidget(1, m_toolButtonBox);
auto hbox = new QHBoxLayout(m_toolButtonBox);
hbox->setMargin(0);
hbox->setSpacing(0);
for (ToolbarOperation &op : m_toolBarOperations)
op.attachToToolbar(m_toolButtonBox);
}
for (ToolbarOperation &op : m_toolBarOperations) {
if (op.toolBarAction)
op.toolBarAction->m_toolButton->setVisible(op.toolBarAction->isVisible());
}
m_toolButtonBox->setVisible(true);
}
void PerspectivePrivate::hideToolBar()
{
if (m_toolButtonBox)
m_toolButtonBox->setVisible(false);
}
void PerspectivePrivate::destroyToolBar()
{
// Detach potentially re-used widgets to prevent deletion.
for (ToolbarOperation &op : m_toolBarOperations)
op.detachFromToolbar();
delete m_toolButtonBox;
m_toolButtonBox = nullptr;
}
void Perspective::addWindow(QWidget *widget,
Perspective::OperationType type,
QWidget *anchorWidget,
bool visibleByDefault,
Qt::DockWidgetArea area)
{
DockOperation op;
op.widget = widget;
if (anchorWidget)
op.anchorDockId = anchorWidget->objectName().toUtf8();
op.operationType = type;
op.visibleByDefault = visibleByDefault;
op.area = area;
m_operations.append(op);
d->m_dockOperations.append(op);
}
void Perspective::select()
{
if (ModeManager::currentModeId() == Debugger::Constants::MODE_DEBUG && currentPerspective() == this) {
// Prevents additional show events triggering modules and register updates.
return;
}
ModeManager::activateMode(Debugger::Constants::MODE_DEBUG);
theMainWindow->d->restorePerspective(this);
}
// ToolbarAction
OptionalAction::OptionalAction(const QString &text)
: QAction(text)
{
}
OptionalAction::~OptionalAction()
{
delete m_toolButton;
}
void OptionalAction::setVisible(bool on)
{
QAction::setVisible(on);
if (m_toolButton)
m_toolButton->setVisible(on);
}
void OptionalAction::setToolButtonStyle(Qt::ToolButtonStyle style)
{
m_toolButtonStyle = style;
}
} // Utils

View File

@@ -30,28 +30,44 @@
#include <utils/fancymainwindow.h>
#include <utils/statuslabel.h>
#include <QHBoxLayout>
#include <QAction>
#include <QPointer>
#include <QSet>
#include <QToolButton>
#include <functional>
QT_BEGIN_NAMESPACE
class QComboBox;
class QToolButton;
class QStackedWidget;
QT_END_NAMESPACE
namespace Core { class Id; }
namespace Core {
class Context;
class Id;
} // Core
namespace Utils {
class DEBUGGER_EXPORT Perspective
// To be used for actions that need hideable toolbuttons.
class DEBUGGER_EXPORT OptionalAction : public QAction
{
Q_OBJECT
public:
OptionalAction(const QString &text = QString());
~OptionalAction() override;
void setVisible(bool on);
void setToolButtonStyle(Qt::ToolButtonStyle style);
public:
QPointer<QToolButton> m_toolButton;
Qt::ToolButtonStyle m_toolButtonStyle = Qt::ToolButtonIconOnly;
};
class DEBUGGER_EXPORT Perspective : public QObject
{
Q_OBJECT
public:
enum OperationType { SplitVertical, SplitHorizontal, AddToTab, Raise };
explicit Perspective(const QByteArray &id, const QString &name);
explicit Perspective(const QString &id, const QString &name);
~Perspective();
void setCentralWidget(QWidget *centralWidget);
@@ -61,54 +77,36 @@ public:
bool visibleByDefault = true,
Qt::DockWidgetArea area = Qt::BottomDockWidgetArea);
QToolButton *addToolbarAction(QAction *action, const QIcon &toolbarIcon = QIcon());
void addToolbarWidget(QWidget *widget);
void addToolBarAction(QAction *action);
void addToolBarAction(OptionalAction *action);
void addToolBarWidget(QWidget *widget);
void addToolbarSeparator();
QWidget *centralWidget() const;
QString name() const;
QString id() const;
using Callback = std::function<void()>;
void setAboutToActivateCallback(const Callback &cb);
void aboutToActivate() const;
QByteArray parentPerspective() const;
void setParentPerspective(const QByteArray &parentPerspective);
void setEnabled(bool enabled);
void destroy();
void select();
static Perspective *currentPerspective();
Core::Context context() const;
void showToolBar();
void hideToolBar();
private:
Perspective(const Perspective &) = delete;
void operator=(const Perspective &) = delete;
friend class DebuggerMainWindow;
friend class DebuggerMainWindowPrivate;
class PerspectivePrivate *d = nullptr;
class Operation
{
public:
QPointer<QWidget> widget;
QByteArray anchorDockId;
OperationType operationType = Raise;
bool visibleByDefault = true;
Qt::DockWidgetArea area = Qt::BottomDockWidgetArea;
};
class ToolbarOperation
{
public:
QPointer<QWidget> widget; // Owned by plugin if present
QPointer<QAction> action; // Owned by plugin if present
QPointer<QToolButton> toolbutton; // Owned here in case action is used
QPointer<QWidget> separator;
QIcon icon;
};
const QByteArray m_id;
QString m_name;
QByteArray m_parentPerspective;
QVector<Operation> m_operations;
QPointer<QWidget> m_centralWidget;
Callback m_aboutToActivateCallback;
QVector<ToolbarOperation> m_toolbarOperations;
};
class DEBUGGER_EXPORT DebuggerMainWindow : public FancyMainWindow
@@ -120,42 +118,18 @@ public:
~DebuggerMainWindow() override;
void registerPerspective(Perspective *perspective);
void destroyDynamicPerspective(Perspective *perspective);
void resetCurrentPerspective();
void restorePerspective(Perspective *perspective);
void finalizeSetup();
void showStatusMessage(const QString &message, int timeoutMS);
void raiseDock(const QByteArray &dockId);
QByteArray currentPerspective() const;
QStackedWidget *centralWidgetStack() const { return m_centralWidgetStack; }
void onModeChanged(Core::Id mode);
void setPerspectiveEnabled(const QByteArray &perspectiveId, bool enabled);
Perspective *findPerspective(const QByteArray &perspectiveId) const;
QWidget *centralWidgetStack();
private:
void closeEvent(QCloseEvent *) final { savePerspectiveHelper(m_currentPerspective); }
void closeEvent(QCloseEvent *) final;
void loadPerspectiveHelper(Perspective *perspective, bool fromStoredSettings = true);
void savePerspectiveHelper(const Perspective *perspective);
void increaseChooserWidthIfNecessary(const QString &visibleName);
int indexInChooser(Perspective *perspective) const;
Perspective *m_currentPerspective = nullptr;
QComboBox *m_perspectiveChooser = nullptr;
QHBoxLayout *m_toolbuttonBoxLayout = nullptr;
QStackedWidget *m_centralWidgetStack = nullptr;
QWidget *m_editorPlaceHolder = nullptr;
Utils::StatusLabel *m_statusLabel = nullptr;
QDockWidget *m_toolbarDock = nullptr;
QHash<QByteArray, QDockWidget *> m_dockForDockId;
QList<Perspective *> m_perspectives;
friend class Perspective;
friend class PerspectivePrivate;
class DebuggerMainWindowPrivate *d = nullptr;
};
DEBUGGER_EXPORT QWidget *createModeWindow(const Core::Id &mode, DebuggerMainWindow *mainWindow);

File diff suppressed because it is too large Load Diff

View File

@@ -301,6 +301,20 @@ const char DisplayImageFile[] = "imagefile:separate";
const char DisplayPlotData[] = "plotdata:separate";
const char DisplayArrayData[] = "arraydata:separate";
enum LocationType { UnknownLocation, LocationByFile, LocationByAddress };
class ContextData
{
public:
bool isValid() const { return type != UnknownLocation; }
public:
LocationType type = UnknownLocation;
QString fileName;
int lineNumber = 0;
quint64 address = 0;
};
} // namespace Internal
} // namespace Debugger

View File

@@ -36,6 +36,7 @@
#include "debuggerrunconfigurationaspect.h"
#include "breakhandler.h"
#include "shared/peutils.h"
#include "snapshothandler.h"
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/devicesupport/deviceprocessesdialog.h>
@@ -60,9 +61,11 @@
#include <utils/temporaryfile.h>
#include <utils/url.h>
#include <coreplugin/icontext.h>
#include <coreplugin/icore.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/messagebox.h>
#include <qmldebug/qmldebugcommandlinearguments.h>
#include <qtsupport/qtkitinformation.h>
@@ -70,7 +73,9 @@
#include <ssh/sshconnection.h>
#include <QTcpServer>
#include <QTimer>
using namespace Core;
using namespace Debugger::Internal;
using namespace ProjectExplorer;
using namespace Utils;
@@ -84,7 +89,6 @@ DebuggerEngine *createCdbEngine();
DebuggerEngine *createGdbEngine();
DebuggerEngine *createPdbEngine();
DebuggerEngine *createQmlEngine();
DebuggerEngine *createQmlCppEngine(DebuggerEngine *cppEngine);
DebuggerEngine *createLldbEngine();
class LocalProcessRunner : public RunWorker
@@ -92,8 +96,8 @@ class LocalProcessRunner : public RunWorker
Q_DECLARE_TR_FUNCTIONS(Debugger::Internal::LocalProcessRunner)
public:
LocalProcessRunner(RunControl *runControl, const Runnable &runnable)
: RunWorker(runControl), m_runnable(runnable)
LocalProcessRunner(DebuggerRunTool *runTool, const Runnable &runnable)
: RunWorker(runTool->runControl()), m_runTool(runTool), m_runnable(runnable)
{
connect(&m_proc, &QProcess::errorOccurred,
this, &LocalProcessRunner::handleError);
@@ -120,16 +124,14 @@ public:
{
const QByteArray ba = m_proc.readAllStandardOutput();
const QString msg = QString::fromLocal8Bit(ba, ba.length());
showMessage(msg, LogOutput);
showMessage(msg, AppOutput);
m_runTool->appendMessage(msg, StdOutFormatSameLine);
}
void handleStandardError()
{
const QByteArray ba = m_proc.readAllStandardError();
const QString msg = QString::fromLocal8Bit(ba, ba.length());
showMessage(msg, LogOutput);
showMessage(msg, AppError);
m_runTool->appendMessage(msg, StdErrFormatSameLine);
}
void handleFinished()
@@ -172,10 +174,11 @@ public:
"This is the default return value of error().");
}
showMessage(msg, StatusBar);
m_runTool->showMessage(msg, StatusBar);
Core::AsynchronousMessageBox::critical(tr("Error"), msg);
}
QPointer<DebuggerRunTool> m_runTool;
Runnable m_runnable;
Utils::QtcProcess m_proc;
};
@@ -245,10 +248,14 @@ private:
class DebuggerRunToolPrivate
{
public:
QPointer<TerminalRunner> terminalRunner;
bool useTerminal = false;
QPointer<CoreUnpacker> coreUnpacker;
QPointer<GdbServerPortsGatherer> portsGatherer;
bool addQmlServerInferiorCommandLineArgumentIfNeeded = false;
TerminalRunner *terminalRunner = nullptr;
int snapshotCounter = 0;
int engineStartsNeeded = 0;
int engineStopsNeeded = 0;
};
} // namespace Internal
@@ -372,7 +379,7 @@ void DebuggerRunTool::setUseTerminal(bool on)
&& boolSetting(UseCdbConsole);
if (on && !d->terminalRunner && !useCdbConsole) {
d->terminalRunner = new TerminalRunner(this);
d->terminalRunner = new TerminalRunner(runControl(), m_runParameters.inferior);
addStartDependency(d->terminalRunner);
}
if (!on && d->terminalRunner) {
@@ -398,7 +405,7 @@ void DebuggerRunTool::setServerStartScript(const QString &serverStartScript)
serverStarter.executable = serverStartScript;
QtcProcess::addArg(&serverStarter.commandLineArguments, m_runParameters.inferior.executable);
QtcProcess::addArg(&serverStarter.commandLineArguments, m_runParameters.remoteChannel);
addStartDependency(new LocalProcessRunner(runControl(), serverStarter));
addStartDependency(new LocalProcessRunner(this, serverStarter));
}
}
@@ -509,8 +516,6 @@ void DebuggerRunTool::addSearchDirectory(const QString &dir)
void DebuggerRunTool::start()
{
Debugger::Internal::saveModeToRestore();
Debugger::selectPerspective(Debugger::Constants::CppPerspectiveId);
TaskHub::clearTasks(Debugger::Constants::TASK_CATEGORY_DEBUGGER_DEBUGINFO);
TaskHub::clearTasks(Debugger::Constants::TASK_CATEGORY_DEBUGGER_RUNTIME);
@@ -560,25 +565,25 @@ void DebuggerRunTool::start()
runControl()->setDisplayName(m_runParameters.displayName);
DebuggerEngine *cppEngine = nullptr;
if (!m_engine) {
if (m_runParameters.isCppDebugging()) {
switch (m_runParameters.cppEngineType) {
case GdbEngineType:
cppEngine = createGdbEngine();
m_engine = createGdbEngine();
break;
case CdbEngineType:
if (!HostOsInfo::isWindowsHost()) {
reportFailure(tr("Unsupported CDB host system."));
return;
}
cppEngine = createCdbEngine();
m_engine = createCdbEngine();
break;
case LldbEngineType:
cppEngine = createLldbEngine();
m_engine = createLldbEngine();
break;
case PdbEngineType: // FIXME: Yes, Python counts as C++...
cppEngine = createPdbEngine();
QTC_CHECK(false); // Called from DebuggerRunTool constructor already.
// m_engine = createPdbEngine();
break;
default:
if (!m_runParameters.isQmlDebugging) {
@@ -592,12 +597,11 @@ void DebuggerRunTool::start()
}
if (m_runParameters.isQmlDebugging) {
if (cppEngine)
m_engine = createQmlCppEngine(cppEngine);
else
m_engine = createQmlEngine();
if (m_engine) {
m_engine2 = createQmlEngine();
} else {
m_engine = cppEngine;
m_engine = createQmlEngine();
}
}
}
@@ -607,12 +611,57 @@ void DebuggerRunTool::start()
}
m_engine->setRunTool(this);
m_engine->setRunParameters(m_runParameters);
m_engine->setCompanionEngine(m_engine2);
connect(m_engine, &DebuggerEngine::requestRunControlFinish,
runControl(), &RunControl::initiateFinish);
connect(m_engine, &DebuggerEngine::requestRunControlStop,
runControl(), &RunControl::initiateStop);
connect(m_engine, &DebuggerEngine::engineStarted,
this, [this] { handleEngineStarted(m_engine); });
connect(m_engine, &DebuggerEngine::engineFinished,
this, [this] { handleEngineFinished(m_engine); });
connect(m_engine, &DebuggerEngine::appendMessageRequested,
this, &DebuggerRunTool::appendMessage);
++d->engineStartsNeeded;
++d->engineStopsNeeded;
connect(m_engine, &DebuggerEngine::attachToCoreRequested, this, [this](const QString &coreFile) {
auto runConfig = runControl()->runConfiguration();
QTC_ASSERT(runConfig, return);
auto rc = new RunControl(runConfig, ProjectExplorer::Constants::DEBUG_RUN_MODE);
auto name = QString(tr("%1 - Snapshot %2").arg(runControl()->displayName()).arg(++d->snapshotCounter));
auto debugger = new DebuggerRunTool(rc);
debugger->setStartMode(AttachCore);
debugger->setRunControlName(name);
debugger->setCoreFileName(coreFile, true);
debugger->startRunControl();
});
if (m_engine2) {
m_engine2->setRunTool(this);
m_engine2->setRunParameters(m_runParameters);
m_engine2->setCompanionEngine(m_engine);
m_engine2->setSecondaryEngine();
connect(m_engine2, &DebuggerEngine::requestRunControlFinish,
runControl(), &RunControl::initiateFinish);
connect(m_engine2, &DebuggerEngine::requestRunControlStop,
runControl(), &RunControl::initiateStop);
connect(m_engine2, &DebuggerEngine::engineStarted,
this, [this] { handleEngineStarted(m_engine2); });
connect(m_engine2, &DebuggerEngine::engineFinished,
this, [this] { handleEngineFinished(m_engine2); });
connect(m_engine2, &DebuggerEngine::appendMessageRequested,
this, &DebuggerRunTool::appendMessage);
++d->engineStartsNeeded;
++d->engineStopsNeeded;
}
if (m_runParameters.startMode == StartInternal) {
QStringList unhandledIds;
for (const Breakpoint &bp : breakHandler()->allBreakpoints()) {
if (bp.isEnabled() && !m_engine->acceptsBreakpoint(bp))
unhandledIds.append(bp.id().toString());
for (const GlobalBreakpoint bp : BreakpointManager::globalBreakpoints()) {
// if (bp->isEnabled() && !m_engine->acceptsBreakpoint(bp))
// unhandledIds.append(bp.id().toString());
}
if (!unhandledIds.isEmpty()) {
QString warningMessage =
@@ -621,7 +670,7 @@ void DebuggerRunTool::start()
"Affected are breakpoints %1")
.arg(unhandledIds.join(QLatin1String(", ")));
Internal::showMessage(warningMessage, LogWarning);
showMessage(warningMessage, LogWarning);
static bool checked = true;
if (checked)
@@ -634,20 +683,53 @@ void DebuggerRunTool::start()
}
appendMessage(tr("Debugging starts"), NormalMessageFormat);
Internal::runControlStarted(this);
QString debuggerName = m_engine->objectName();
if (m_engine2)
debuggerName += ' ' + m_engine2->objectName();
const QString message = tr("Starting debugger \"%1\" for ABI \"%2\"...")
.arg(debuggerName).arg(m_runParameters.toolChainAbi.toString());
showStatusMessage(message);
showMessage(m_engine->formatStartParameters(), LogDebug);
showMessage(DebuggerSettings::dump(), LogDebug);
if (m_engine2)
m_engine2->start();
m_engine->start();
}
void DebuggerRunTool::stop()
{
m_isDying = true;
QTC_ASSERT(m_engine, reportStopped(); return);
if (m_engine2)
m_engine2->quitDebugger();
m_engine->quitDebugger();
}
const DebuggerRunParameters &DebuggerRunTool::runParameters() const
void DebuggerRunTool::handleEngineStarted(DebuggerEngine *engine)
{
return m_runParameters;
EngineManager::activateEngine(engine);
// Correct:
// if (--d->engineStartsNeeded == 0) {
// EngineManager::activateDebugMode();
// reportStarted();
// }
// Feels better, as the QML Engine might attach late or not at all.
if (engine == m_engine) {
EngineManager::activateDebugMode();
reportStarted();
}
}
void DebuggerRunTool::handleEngineFinished(DebuggerEngine *engine)
{
engine->prepareForRestart();
if (--d->engineStopsNeeded == 0) {
appendMessage(tr("Debugging has finished"), NormalMessageFormat);
reportStopped();
}
}
bool DebuggerRunTool::isCppDebugging() const
@@ -684,36 +766,9 @@ void DebuggerRunTool::setSolibSearchPath(const QStringList &list)
m_runParameters.solibSearchPath = list;
}
void DebuggerRunTool::notifyInferiorIll()
{
m_engine->notifyInferiorIll();
}
void DebuggerRunTool::notifyInferiorExited()
{
m_engine->notifyInferiorExited();
}
void DebuggerRunTool::quitDebugger()
{
m_isDying = true;
m_engine->quitDebugger();
}
void DebuggerRunTool::abortDebugger()
{
m_engine->resetLocation();
if (!m_isDying) {
// Be friendly the first time. This will change targetState().
showMessage("ABORTING DEBUGGER. FIRST TIME.");
quitDebugger();
} else {
// We already tried. Try harder.
showMessage("ABORTING DEBUGGER. SECOND TIME.");
m_engine->abortDebuggerProcess();
if (runControl())
runControl()->initiateFinish();
}
initiateStop();
}
bool DebuggerRunTool::fixupParameters()
@@ -807,9 +862,6 @@ bool DebuggerRunTool::fixupParameters()
if (rp.isNativeMixedDebugging())
rp.inferior.environment.set("QV4_FORCE_INTERPRETER", "1");
if (rp.isCppDebugging() && !rp.skipExecutableValidation)
rp.validateExecutable();
return true;
}
@@ -912,11 +964,6 @@ DebuggerRunTool::DebuggerRunTool(RunControl *runControl, Kit *kit, bool allowTer
}
}
DebuggerEngine *DebuggerRunTool::activeEngine() const
{
return m_engine ? m_engine->activeEngine() : nullptr;
}
void DebuggerRunTool::startRunControl()
{
ProjectExplorerPlugin::startRunControl(runControl());
@@ -931,13 +978,14 @@ void DebuggerRunTool::addSolibSearchDir(const QString &str)
DebuggerRunTool::~DebuggerRunTool()
{
disconnect();
if (m_engine) {
DebuggerEngine *engine = m_engine;
if (m_runParameters.isSnapshot && !m_runParameters.coreFile.isEmpty())
QFile::remove(m_runParameters.coreFile);
delete m_engine2;
m_engine2 = nullptr;
delete m_engine;
m_engine = nullptr;
engine->disconnect();
delete engine;
}
delete d;
}
@@ -946,7 +994,9 @@ void DebuggerRunTool::showMessage(const QString &msg, int channel, int timeout)
if (channel == ConsoleOutput)
debuggerConsole()->printItem(ConsoleItem::DefaultType, msg);
Internal::showMessage(msg, channel, timeout);
m_engine->showMessage(msg, channel, timeout);
if (m_engine2)
m_engine->showMessage(msg, channel, timeout);
switch (channel) {
case AppOutput:
appendMessage(msg, StdOutFormatSameLine);

View File

@@ -28,6 +28,7 @@
#include "debugger_global.h"
#include "debuggerconstants.h"
#include "debuggerengine.h"
#include "terminal.h"
#include <projectexplorer/runconfiguration.h>
#include <projectexplorer/devicesupport/deviceusedportsgatherer.h>
@@ -51,9 +52,6 @@ public:
bool allowTerminal = true);
~DebuggerRunTool() override;
Internal::DebuggerEngine *engine() const { return m_engine; }
Internal::DebuggerEngine *activeEngine() const;
void startRunControl();
void showMessage(const QString &msg, int channel = LogDebug, int timeout = -1);
@@ -61,15 +59,8 @@ public:
void start() override;
void stop() override;
void notifyInferiorIll();
Q_SLOT void notifyInferiorExited(); // Called from Android.
void quitDebugger();
void abortDebugger();
const Internal::DebuggerRunParameters &runParameters() const;
void startDying() { m_isDying = true; }
bool isDying() const { return m_isDying; }
bool isCppDebugging() const;
bool isQmlDebugging() const;
int portsUsedByDebugger() const;
@@ -138,11 +129,13 @@ public:
private:
bool fixupParameters();
void handleEngineStarted(Internal::DebuggerEngine *engine);
void handleEngineFinished(Internal::DebuggerEngine *engine);
Internal::DebuggerRunToolPrivate *d;
QPointer<Internal::DebuggerEngine> m_engine; // Master engine
QPointer<Internal::DebuggerEngine> m_engine;
QPointer<Internal::DebuggerEngine> m_engine2;
Internal::DebuggerRunParameters m_runParameters;
bool m_isDying = false;
};
class DEBUGGER_EXPORT GdbServerPortsGatherer : public ProjectExplorer::ChannelProvider

View File

@@ -28,6 +28,7 @@
#include "debuggerengine.h"
#include "debuggerprotocol.h"
#include "debuggeractions.h"
#include "snapshothandler.h"
#include "stackhandler.h"
#include "debuggercore.h"
#include "watchhandler.h"
@@ -1138,7 +1139,7 @@ static void slotTooltipOverrideRequested
return;
const TextDocument *document = editorWidget->textDocument();
DebuggerEngine *engine = currentEngine();
DebuggerEngine *engine = EngineManager::currentEngine();
if (!engine || !engine->canDisplayTooltip())
return;

View File

@@ -68,12 +68,17 @@ public:
DisassemblerBreakpointMarker(const Breakpoint &bp, int lineNumber)
: TextMark(Utils::FileName(), lineNumber, Constants::TEXT_MARK_CATEGORY_BREAKPOINT), m_bp(bp)
{
setIcon(bp.icon());
setIcon(bp->icon());
setPriority(TextMark::NormalPriority);
}
bool isClickable() const override { return true; }
void clicked() override { m_bp.removeBreakpoint(); }
bool isClickable() const final { return true; }
void clicked() final
{
QTC_ASSERT(m_bp, return);
m_bp->deleteGlobalOrThisBreakpoint();
}
public:
Breakpoint m_bp;
@@ -331,8 +336,8 @@ void DisassemblerAgent::setContentsToDocument(const DisassemblerLines &contents)
d->document->setPreferredDisplayName(QString("Disassembler (%1)")
.arg(d->location.functionName()));
const Breakpoints bps = breakHandler()->engineBreakpoints(d->engine);
for (Breakpoint bp : bps)
const Breakpoints bps = d->engine->breakHandler()->breakpoints();
for (const Breakpoint bp : bps)
updateBreakpointMarker(bp);
updateLocationMarker();
@@ -340,7 +345,9 @@ void DisassemblerAgent::setContentsToDocument(const DisassemblerLines &contents)
void DisassemblerAgent::updateLocationMarker()
{
QTC_ASSERT(d->document, return);
if (!d->document)
return;
int lineNumber = d->lineForAddress(d->location.address());
if (d->location.needsMarker()) {
d->document->removeMark(&d->locationMark);
@@ -348,6 +355,8 @@ void DisassemblerAgent::updateLocationMarker()
d->document->addMark(&d->locationMark);
}
d->locationMark.updateIcon();
// Center cursor.
if (EditorManager::currentDocument() == d->document)
if (auto textEditor = qobject_cast<BaseTextEditor *>(EditorManager::currentEditor()))
@@ -359,9 +368,8 @@ void DisassemblerAgent::removeBreakpointMarker(const Breakpoint &bp)
if (!d->document)
return;
BreakpointModelId id = bp.id();
foreach (DisassemblerBreakpointMarker *marker, d->breakpointMarks) {
if (marker->m_bp.id() == id) {
for (DisassemblerBreakpointMarker *marker : d->breakpointMarks) {
if (marker->m_bp == bp) {
d->breakpointMarks.removeOne(marker);
d->document->removeMark(marker);
delete marker;
@@ -373,7 +381,7 @@ void DisassemblerAgent::removeBreakpointMarker(const Breakpoint &bp)
void DisassemblerAgent::updateBreakpointMarker(const Breakpoint &bp)
{
removeBreakpointMarker(bp);
const quint64 address = bp.response().address;
const quint64 address = bp->address();
if (!address)
return;
@@ -384,7 +392,7 @@ void DisassemblerAgent::updateBreakpointMarker(const Breakpoint &bp)
// HACK: If it's a FileAndLine breakpoint, and there's a source line
// above, move the marker up there. That allows setting and removing
// normal breakpoints from within the disassembler view.
if (bp.type() == BreakpointByFileAndLine) {
if (bp->type() == BreakpointByFileAndLine) {
ContextData context = getLocationContext(d->document, lineNumber - 1);
if (context.type == LocationByFile)
--lineNumber;

View File

@@ -25,12 +25,13 @@
#pragma once
#include "breakhandler.h"
#include <QObject>
namespace Debugger {
namespace Internal {
class Breakpoint;
class DebuggerEngine;
class DisassemblerAgentPrivate;
class DisassemblerLines;

File diff suppressed because it is too large Load Diff

View File

@@ -47,7 +47,6 @@ namespace Debugger {
namespace Internal {
class BreakpointParameters;
class BreakpointResponse;
class DebugInfoTask;
class DebugInfoTaskHandler;
class DebuggerResponse;
@@ -65,7 +64,7 @@ struct CoreInfo
const QString &coreFile);
};
class GdbEngine : public DebuggerEngine
class GdbEngine : public CppDebuggerEngine
{
Q_OBJECT
@@ -74,8 +73,6 @@ public:
~GdbEngine() final;
private: ////////// General Interface //////////
DebuggerEngine *cppEngine() final { return this; }
void handleGdbStartFailed();
void prepareForRestart() final;
@@ -86,7 +83,7 @@ private: ////////// General Interface //////////
void resetInferior() final;
bool acceptsDebuggerCommands() const final;
void executeDebuggerCommand(const QString &command, DebuggerLanguages languages) final;
void executeDebuggerCommand(const QString &command) final;
////////// General State //////////
@@ -189,11 +186,11 @@ private: ////////// General Interface //////////
// This should be always the last call in a function.
bool stateAcceptsBreakpointChanges() const final;
bool acceptsBreakpoint(Breakpoint bp) const final;
void insertBreakpoint(Breakpoint bp) final;
void removeBreakpoint(Breakpoint bp) final;
void changeBreakpoint(Breakpoint bp) final;
void enableSubBreakpoint(const QString &locId, bool on) final;
bool acceptsBreakpoint(const BreakpointParameters &bp) const final;
void insertBreakpoint(const Breakpoint &bp) final;
void removeBreakpoint(const Breakpoint &bp) final;
void updateBreakpoint(const Breakpoint &bp) final;
void enableSubBreakpoint(const SubBreakpoint &sbp, bool on) final;
void executeStep() final;
void executeStepOut() final;
@@ -209,6 +206,7 @@ private: ////////// General Interface //////////
void executeRunToFunction(const QString &functionName) final;
void executeJumpToLine(const ContextData &data) final;
void executeReturn() final;
void executeRecordReverse(bool reverse);
void handleExecuteContinue(const DebuggerResponse &response);
void handleExecuteStep(const DebuggerResponse &response);
@@ -223,26 +221,24 @@ private: ////////// General Interface //////////
void selectThread(ThreadId threadId) final;
void activateFrame(int index) final;
void handleAutoContinueInferior();
//
// Breakpoint specific stuff
//
void handleBreakModifications(const GdbMi &bkpts);
void handleBreakIgnore(const DebuggerResponse &response, Breakpoint bp);
void handleBreakDisable(const DebuggerResponse &response, Breakpoint bp);
void handleBreakEnable(const DebuggerResponse &response, Breakpoint bp);
void handleBreakInsert1(const DebuggerResponse &response, Breakpoint bp);
void handleBreakInsert2(const DebuggerResponse &response, Breakpoint bp);
void handleBreakCondition(const DebuggerResponse &response, Breakpoint bp);
void handleBreakThreadSpec(const DebuggerResponse &response, Breakpoint bp);
void handleBreakLineNumber(const DebuggerResponse &response, Breakpoint bp);
void handleInsertInterpreterBreakpoint(const DebuggerResponse &response, Breakpoint bp);
void handleBreakIgnore(const DebuggerResponse &response, const Breakpoint &bp);
void handleBreakDisable(const DebuggerResponse &response, const Breakpoint &bp);
void handleBreakEnable(const DebuggerResponse &response, const Breakpoint &bp);
void handleBreakInsert1(const DebuggerResponse &response, const Breakpoint &bp);
void handleBreakInsert2(const DebuggerResponse &response, const Breakpoint &bp);
void handleBreakCondition(const DebuggerResponse &response, const Breakpoint &bp);
void handleBreakThreadSpec(const DebuggerResponse &response, const Breakpoint &bp);
void handleBreakLineNumber(const DebuggerResponse &response, const Breakpoint &bp);
void handleInsertInterpreterBreakpoint(const DebuggerResponse &response, const Breakpoint &bp);
void handleInterpreterBreakpointModified(const GdbMi &data);
void handleWatchInsert(const DebuggerResponse &response, Breakpoint bp);
void handleCatchInsert(const DebuggerResponse &response, Breakpoint bp);
void handleBkpt(const GdbMi &bkpt, Breakpoint bp);
void updateResponse(BreakpointResponse &response, const GdbMi &bkpt);
void handleWatchInsert(const DebuggerResponse &response, const Breakpoint &bp);
void handleCatchInsert(const DebuggerResponse &response, const Breakpoint &bp);
void handleBkpt(const GdbMi &bkpt, const Breakpoint &bp);
QString breakpointLocation(const BreakpointParameters &data); // For gdb/MI.
QString breakpointLocation2(const BreakpointParameters &data); // For gdb/CLI fallback.
QString breakLocation(const QString &file) const;

View File

@@ -84,6 +84,7 @@ static int &currentToken()
LldbEngine::LldbEngine()
{
setObjectName("LldbEngine");
setDebuggerName("LLDB");
connect(action(AutoDerefPointers), &SavedAction::valueChanged,
this, &LldbEngine::updateLocals);
@@ -114,7 +115,7 @@ LldbEngine::~LldbEngine()
m_lldbProc.disconnect();
}
void LldbEngine::executeDebuggerCommand(const QString &command, DebuggerLanguages)
void LldbEngine::executeDebuggerCommand(const QString &command)
{
DebuggerCommand cmd("executeDebuggerCommand");
cmd.arg("command", command);
@@ -175,13 +176,18 @@ void LldbEngine::shutdownInferior()
void LldbEngine::shutdownEngine()
{
QTC_ASSERT(state() == EngineShutdownRequested, qDebug() << state());
m_lldbProc.kill();
if (m_lldbProc.state() == QProcess::Running)
m_lldbProc.terminate();
else
notifyEngineShutdownFinished();
}
void LldbEngine::abortDebuggerProcess()
{
if (m_lldbProc.state() == QProcess::Running)
m_lldbProc.kill();
else
notifyEngineShutdownFinished();
}
void LldbEngine::setupEngine()
@@ -290,17 +296,9 @@ void LldbEngine::setupEngine()
}
cmd2.callback = [this](const DebuggerResponse &response) {
bool success = response.data["success"].toInt();
const bool success = response.data["success"].toInt();
if (success) {
for (Breakpoint bp : breakHandler()->unclaimedBreakpoints()) {
if (acceptsBreakpoint(bp)) {
bp.setEngine(this);
insertBreakpoint(bp);
} else {
showMessage(QString("BREAKPOINT %1 IN STATE %2 IS NOT ACCEPTABLE")
.arg(bp.id().toString()).arg(bp.state()));
}
}
BreakpointManager::claimBreakpointsForEngine(this);
} else {
notifyEngineSetupFailed();
}
@@ -442,7 +440,7 @@ void LldbEngine::activateFrame(int frameIndex)
QTC_ASSERT(frameIndex < handler->stackSize(), return);
handler->setCurrentIndex(frameIndex);
gotoLocation(handler->currentFrame());
gotoCurrentLocation();
DebuggerCommand cmd("activateFrame");
cmd.arg("index", frameIndex);
@@ -477,104 +475,103 @@ bool LldbEngine::stateAcceptsBreakpointChanges() const
}
}
bool LldbEngine::acceptsBreakpoint(Breakpoint bp) const
bool LldbEngine::acceptsBreakpoint(const BreakpointParameters &bp) const
{
if (runParameters().startMode == AttachCore)
return false;
if (bp.parameters().isCppBreakpoint())
if (bp.isCppBreakpoint())
return true;
return isNativeMixedEnabled();
}
void LldbEngine::insertBreakpoint(Breakpoint bp)
void LldbEngine::insertBreakpoint(const Breakpoint &bp)
{
QTC_ASSERT(bp, return);
DebuggerCommand cmd("insertBreakpoint");
cmd.callback = [this, bp](const DebuggerResponse &response) {
QTC_CHECK(bp.state() == BreakpointInsertProceeding);
QTC_CHECK(bp && bp->state() == BreakpointInsertionProceeding);
updateBreakpointData(bp, response.data, true);
};
bp.addToCommand(&cmd);
bp.notifyBreakpointInsertProceeding();
bp->addToCommand(&cmd);
notifyBreakpointInsertProceeding(bp);
runCommand(cmd);
}
void LldbEngine::changeBreakpoint(Breakpoint bp)
void LldbEngine::updateBreakpoint(const Breakpoint &bp)
{
const BreakpointResponse &response = bp.response();
QTC_ASSERT(bp, return);
DebuggerCommand cmd("changeBreakpoint");
cmd.arg("lldbid", response.id.toString());
cmd.arg("lldbid", bp->responseId());
cmd.callback = [this, bp](const DebuggerResponse &response) {
QTC_CHECK(!bp.isValid() || bp.state() == BreakpointChangeProceeding);
QTC_CHECK(bp && bp->state() == BreakpointUpdateProceeding);
updateBreakpointData(bp, response.data, false);
};
bp.addToCommand(&cmd);
bp.notifyBreakpointChangeProceeding();
bp->addToCommand(&cmd);
notifyBreakpointChangeProceeding(bp);
runCommand(cmd);
}
void LldbEngine::removeBreakpoint(Breakpoint bp)
void LldbEngine::removeBreakpoint(const Breakpoint &bp)
{
const BreakpointResponse &response = bp.response();
if (response.id.isValid()) {
QTC_ASSERT(bp, return);
if (!bp->responseId().isEmpty()) {
DebuggerCommand cmd("removeBreakpoint");
cmd.arg("lldbid", response.id.toString());
cmd.callback = [bp](const DebuggerResponse &) {
QTC_CHECK(bp.state() == BreakpointRemoveProceeding);
Breakpoint bp0 = bp;
bp0.notifyBreakpointRemoveOk();
};
bp.notifyBreakpointRemoveProceeding();
cmd.arg("lldbid", bp->responseId());
notifyBreakpointRemoveProceeding(bp);
runCommand(cmd);
// Pretend it succeeds without waiting for response. Feels better.
// Otherwise, clicking in the gutter leaves the breakpoint visible
// for quite some time, so the user assumes a mis-click and clicks
// again, effectivly re-introducing the breakpoint.
notifyBreakpointRemoveOk(bp);
}
}
void LldbEngine::updateBreakpointData(Breakpoint bp, const GdbMi &bkpt, bool added)
void LldbEngine::updateBreakpointData(const Breakpoint &bp, const GdbMi &bkpt, bool added)
{
BreakHandler *handler = breakHandler();
BreakpointResponseId rid = BreakpointResponseId(bkpt["lldbid"].data());
if (!bp.isValid())
bp = handler->findBreakpointByResponseId(rid);
BreakpointResponse response = bp.response();
QTC_ASSERT(bp, return);
QString rid = bkpt["lldbid"].data();
QTC_ASSERT(bp, return);
if (added)
response.id = rid;
QTC_CHECK(response.id == rid);
response.address = 0;
response.enabled = bkpt["enabled"].toInt();
response.ignoreCount = bkpt["ignorecount"].toInt();
response.condition = fromHex(bkpt["condition"].data());
response.hitCount = bkpt["hitcount"].toInt();
response.fileName = bkpt["file"].data();
response.lineNumber = bkpt["line"].toInt();
bp->setResponseId(rid);
QTC_CHECK(bp->responseId() == rid);
bp->setAddress(0);
bp->setEnabled(bkpt["enabled"].toInt());
bp->setIgnoreCount(bkpt["ignorecount"].toInt());
bp->setCondition(fromHex(bkpt["condition"].data()));
bp->setHitCount(bkpt["hitcount"].toInt());
bp->setFileName(bkpt["file"].data());
bp->setLineNumber(bkpt["line"].toInt());
GdbMi locations = bkpt["locations"];
const int numChild = int(locations.children().size());
if (numChild > 1) {
for (const GdbMi &location : locations.children()) {
const int locid = location["locid"].toInt();
BreakpointResponse sub;
sub.id = BreakpointResponseId(rid.majorPart(), locid);
sub.type = response.type;
sub.address = location["addr"].toAddress();
sub.functionName = location["func"].data();
sub.fileName = location["file"].data();
sub.lineNumber = location["line"].toInt();
bp.insertSubBreakpoint(sub);
const QString locid = QString("%1.%2").arg(rid).arg(location["locid"].data());
SubBreakpoint loc = bp->findOrCreateSubBreakpoint(locid);
QTC_ASSERT(loc, continue);
loc->params.type = bp->type();
loc->params.address = location["addr"].toAddress();
loc->params.functionName = location["function"].data();
loc->params.fileName = location["file"].data();
loc->params.lineNumber = location["line"].toInt();
}
response.pending = false;
bp->setPending(false);
} else if (numChild == 1) {
const GdbMi location = locations.childAt(0);
response.address = location["addr"].toAddress();
response.functionName = location["func"].data();
response.pending = false;
bp->setAddress(location["addr"].toAddress());
bp->setFunctionName(location["function"].data());
bp->setPending(false);
} else {
// This can happen for pending breakpoints.
showMessage(QString("NO LOCATIONS (YET) FOR BP %1").arg(response.toString()));
showMessage(QString("NO LOCATIONS (YET) FOR BP %1").arg(bp->parameters().toString()));
}
bp.setResponse(response);
bp->adjustMarker();
if (added)
bp.notifyBreakpointInsertOk();
notifyBreakpointInsertOk(bp);
else
bp.notifyBreakpointChangeOk();
notifyBreakpointChangeOk(bp);
}
void LldbEngine::handleOutputNotification(const GdbMi &output)
@@ -896,7 +893,7 @@ void LldbEngine::handleLocationNotification(const GdbMi &reportedLocation)
void LldbEngine::reloadRegisters()
{
if (!Internal::isRegistersWindowVisible())
if (!isRegistersWindowVisible())
return;
if (state() != InferiorStopOk && state() != InferiorUnrunnable)
@@ -1012,7 +1009,8 @@ void LldbEngine::setRegisterValue(const QString &name, const QString &value)
bool LldbEngine::hasCapability(unsigned cap) const
{
if (cap & (ReverseSteppingCapability
if (cap & (0
//| ReverseSteppingCapability
| AutoDerefPointersCapability
| DisassemblerCapability
| RegisterCapability

View File

@@ -41,17 +41,14 @@
#include <QMap>
#include <QVariant>
namespace Debugger {
namespace Internal {
class GdbMi;
/* A debugger engine interfacing the LLDB debugger
* using its Python interface.
*/
class LldbEngine : public DebuggerEngine
class LldbEngine : public CppDebuggerEngine
{
Q_OBJECT
@@ -63,8 +60,6 @@ signals:
void outputReady(const QString &data);
private:
DebuggerEngine *cppEngine() override { return this; }
void executeStep() override;
void executeStepOut() override;
void executeNext() override;
@@ -92,13 +87,13 @@ private:
// This should be always the last call in a function.
bool stateAcceptsBreakpointChanges() const override;
bool acceptsBreakpoint(Breakpoint bp) const override;
void insertBreakpoint(Breakpoint bp) override;
void removeBreakpoint(Breakpoint bp) override;
void changeBreakpoint(Breakpoint bp) override;
bool acceptsBreakpoint(const BreakpointParameters &bp) const override;
void insertBreakpoint(const Breakpoint &bp) override;
void removeBreakpoint(const Breakpoint &bp) override;
void updateBreakpoint(const Breakpoint &bp) override;
void assignValueInDebugger(WatchItem *item, const QString &expr, const QVariant &value) override;
void executeDebuggerCommand(const QString &command, DebuggerLanguages languages) override;
void executeDebuggerCommand(const QString &command) override;
void loadSymbols(const QString &moduleName) override;
void loadAllSymbols() override;
@@ -130,7 +125,7 @@ private:
void handleResponse(const QString &data);
void updateAll() override;
void doUpdateLocals(const UpdateParameters &params) override;
void updateBreakpointData(Breakpoint bp, const GdbMi &bkpt, bool added);
void updateBreakpointData(const Breakpoint &bp, const GdbMi &bkpt, bool added);
void fetchStack(int limit);
void runCommand(const DebuggerCommand &cmd) override;

View File

@@ -58,6 +58,52 @@
namespace Debugger {
namespace Internal {
GlobalLogWindow *theGlobalLog = nullptr;
static LogChannel channelForChar(QChar c)
{
switch (c.unicode()) {
case 'd': return LogDebug;
case 'w': return LogWarning;
case 'e': return LogError;
case '<': return LogInput;
case '>': return LogOutput;
case 's': return LogStatus;
case 't': return LogTime;
default: return LogMisc;
}
}
QChar static charForChannel(int channel)
{
switch (channel) {
case LogDebug: return QLatin1Char('d');
case LogWarning: return QLatin1Char('w');
case LogError: return QLatin1Char('e');
case LogInput: return QLatin1Char('<');
case LogOutput: return QLatin1Char('>');
case LogStatus: return QLatin1Char('s');
case LogTime: return QLatin1Char('t');
case LogMisc:
default: return QLatin1Char(' ');
}
}
static bool writeLogContents(const QPlainTextEdit *editor, QWidget *parent)
{
bool success = false;
while (!success) {
const QString fileName = QFileDialog::getSaveFileName(parent, LogWindow::tr("Log File"));
if (fileName.isEmpty())
break;
Utils::FileSaver saver(fileName, QIODevice::Text);
saver.write(editor->toPlainText().toUtf8());
if (saver.finalize(parent))
success = true;
}
return success;
}
/////////////////////////////////////////////////////////////////////
//
// OutputHighlighter
@@ -77,7 +123,7 @@ private:
using Utils::Theme;
QTextCharFormat format;
Theme *theme = Utils::creatorTheme();
switch (LogWindow::channelForChar(text.isEmpty() ? QChar() : text.at(0))) {
switch (channelForChar(text.isEmpty() ? QChar() : text.at(0))) {
case LogInput:
format.setForeground(theme->color(Theme::Debugger_LogWindow_LogInput));
setFormat(1, text.size(), format);
@@ -149,15 +195,14 @@ class DebuggerPane : public QPlainTextEdit
Q_OBJECT
public:
DebuggerPane(LogWindow *parent)
: QPlainTextEdit(parent)
explicit DebuggerPane()
{
setFrameStyle(QFrame::NoFrame);
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
m_clearContentsAction = new QAction(this);
m_clearContentsAction->setText(tr("Clear Contents"));
m_clearContentsAction->setEnabled(true);
connect(m_clearContentsAction, &QAction::triggered,
parent, &LogWindow::clearContents);
m_saveContentsAction = new QAction(this);
m_saveContentsAction->setText(tr("Save Contents"));
@@ -168,8 +213,6 @@ public:
m_reloadDebuggingHelpersAction = new QAction(this);
m_reloadDebuggingHelpersAction->setText(tr("Reload Debugging Helpers"));
m_reloadDebuggingHelpersAction->setEnabled(true);
connect(m_reloadDebuggingHelpersAction, &QAction::triggered,
this, &DebuggerPane::reloadDebuggingHelpers);
}
void contextMenuEvent(QContextMenuEvent *ev) override
@@ -212,24 +255,18 @@ public:
setUndoRedoEnabled(true);
}
QAction *clearContentsAction() const { return m_clearContentsAction; }
QAction *reloadDebuggingHelpersAction() const { return m_reloadDebuggingHelpersAction; }
private:
void saveContents();
void reloadDebuggingHelpers();
void saveContents() { writeLogContents(this, this); }
QAction *m_clearContentsAction;
QAction *m_saveContentsAction;
QAction *m_reloadDebuggingHelpersAction;
};
void DebuggerPane::saveContents()
{
LogWindow::writeLogContents(this, this);
}
void DebuggerPane::reloadDebuggingHelpers()
{
currentEngine()->reloadDebuggingHelpers();
}
/////////////////////////////////////////////////////////////////////
//
@@ -241,9 +278,12 @@ class InputPane : public DebuggerPane
{
Q_OBJECT
public:
InputPane(LogWindow *parent)
: DebuggerPane(parent)
InputPane(LogWindow *logWindow)
{
connect(clearContentsAction(), &QAction::triggered,
logWindow, &LogWindow::clearContents);
connect(reloadDebuggingHelpersAction(), &QAction::triggered,
logWindow->engine(), &DebuggerEngine::reloadDebuggingHelpers);
(void) new InputHighlighter(this);
}
@@ -307,10 +347,13 @@ class CombinedPane : public DebuggerPane
{
Q_OBJECT
public:
CombinedPane(LogWindow *parent)
: DebuggerPane(parent)
CombinedPane(LogWindow *logWindow)
{
(void) new OutputHighlighter(this);
connect(clearContentsAction(), &QAction::triggered,
logWindow, &LogWindow::clearContents);
connect(reloadDebuggingHelpersAction(), &QAction::triggered,
logWindow->engine(), &DebuggerEngine::reloadDebuggingHelpers);
}
void gotoResult(int i)
@@ -347,8 +390,8 @@ public:
//
/////////////////////////////////////////////////////////////////////
LogWindow::LogWindow(QWidget *parent)
: QWidget(parent)
LogWindow::LogWindow(DebuggerEngine *engine)
: m_engine(engine)
{
setWindowTitle(tr("Debugger &Log"));
setObjectName(QLatin1String("Log"));
@@ -362,14 +405,10 @@ LogWindow::LogWindow(QWidget *parent)
m_combinedText = new CombinedPane(this);
m_combinedText->setReadOnly(true);
m_combinedText->setReadOnly(false);
m_combinedText->setSizePolicy(QSizePolicy::MinimumExpanding,
QSizePolicy::MinimumExpanding);
// Input only.
m_inputText = new InputPane(this);
m_inputText->setReadOnly(false);
m_inputText->setSizePolicy(QSizePolicy::MinimumExpanding,
QSizePolicy::MinimumExpanding);
m_commandEdit = new Utils::FancyLineEdit(this);
m_commandEdit->setFrame(false);
@@ -445,23 +484,33 @@ LogWindow::LogWindow(QWidget *parent)
.arg(Core::Constants::IDE_DISPLAY_NAME));
}
LogWindow::~LogWindow()
{
disconnect(&m_outputTimer, &QTimer::timeout, this, &LogWindow::doOutput);
m_outputTimer.stop();
doOutput();
}
void LogWindow::executeLine()
{
m_ignoreNextInputEcho = true;
currentEngine()->
executeDebuggerCommand(m_inputText->textCursor().block().text(), CppLanguage);
m_engine->executeDebuggerCommand(m_inputText->textCursor().block().text());
}
void LogWindow::repeatLastCommand()
{
currentEngine()->debugLastCommand();
m_engine->debugLastCommand();
}
DebuggerEngine *LogWindow::engine() const
{
return m_engine;
}
void LogWindow::sendCommand()
{
DebuggerEngine *engine = currentEngine();
if (engine->acceptsDebuggerCommands())
engine->executeDebuggerCommand(m_commandEdit->text(), CppLanguage);
if (m_engine->acceptsDebuggerCommands())
m_engine->executeDebuggerCommand(m_commandEdit->text());
else
showOutput(LogError, tr("User commands are not accepted in the current state."));
}
@@ -512,6 +561,8 @@ void LogWindow::doOutput()
if (m_queuedOutput.isEmpty())
return;
theGlobalLog->doOutput(m_queuedOutput);
QTextCursor cursor = m_combinedText->textCursor();
const bool atEnd = cursor.atEnd();
@@ -543,6 +594,8 @@ void LogWindow::showInput(int channel, const QString &input)
cursor.movePosition(QTextCursor::End);
m_inputText->setTextCursor(cursor);
m_inputText->ensureCursorVisible();
theGlobalLog->doInput(input);
}
void LogWindow::clearContents()
@@ -597,48 +650,100 @@ QString LogWindow::logTimeStamp()
return lastTimeStamp;
}
bool LogWindow::writeLogContents(const QPlainTextEdit *editor, QWidget *parent)
/////////////////////////////////////////////////////////////////////
//
// GlobalLogWindow
//
/////////////////////////////////////////////////////////////////////
GlobalLogWindow::GlobalLogWindow()
{
bool success = false;
while (!success) {
const QString fileName = QFileDialog::getSaveFileName(parent, tr("Log File"));
if (fileName.isEmpty())
break;
Utils::FileSaver saver(fileName, QIODevice::Text);
saver.write(editor->toPlainText().toUtf8());
if (saver.finalize(parent))
success = true;
}
return success;
theGlobalLog = this;
setWindowTitle(tr("Global Debugger &Log"));
setObjectName("GlobalLog");
auto m_splitter = new Core::MiniSplitter(Qt::Horizontal);
m_splitter->setParent(this);
m_rightPane = new DebuggerPane;
m_rightPane->setReadOnly(true);
m_leftPane = new DebuggerPane;
m_leftPane->setReadOnly(true);
m_splitter->addWidget(m_leftPane);
m_splitter->addWidget(m_rightPane);
m_splitter->setStretchFactor(0, 1);
m_splitter->setStretchFactor(1, 3);
auto layout = new QVBoxLayout(this);
layout->setMargin(0);
layout->setSpacing(0);
layout->addWidget(m_splitter);
layout->addWidget(new Core::FindToolBarPlaceHolder(this));
setLayout(layout);
auto aggregate = new Aggregation::Aggregate;
aggregate->add(m_rightPane);
aggregate->add(new Core::BaseTextFind(m_rightPane));
aggregate = new Aggregation::Aggregate;
aggregate->add(m_leftPane);
aggregate->add(new Core::BaseTextFind(m_leftPane));
connect(m_leftPane->clearContentsAction(), &QAction::triggered,
this, &GlobalLogWindow::clearContents);
connect(m_rightPane->clearContentsAction(), &QAction::triggered,
this, &GlobalLogWindow::clearContents);
}
QChar LogWindow::charForChannel(int channel)
GlobalLogWindow::~GlobalLogWindow()
{
switch (channel) {
case LogDebug: return QLatin1Char('d');
case LogWarning: return QLatin1Char('w');
case LogError: return QLatin1Char('e');
case LogInput: return QLatin1Char('<');
case LogOutput: return QLatin1Char('>');
case LogStatus: return QLatin1Char('s');
case LogTime: return QLatin1Char('t');
case LogMisc:
default: return QLatin1Char(' ');
}
void GlobalLogWindow::doOutput(const QString &output)
{
QTextCursor cursor = m_rightPane->textCursor();
const bool atEnd = cursor.atEnd();
m_rightPane->append(output);
if (atEnd) {
cursor.movePosition(QTextCursor::End);
m_rightPane->setTextCursor(cursor);
m_rightPane->ensureCursorVisible();
}
}
LogChannel LogWindow::channelForChar(QChar c)
void GlobalLogWindow::doInput(const QString &input)
{
switch (c.unicode()) {
case 'd': return LogDebug;
case 'w': return LogWarning;
case 'e': return LogError;
case '<': return LogInput;
case '>': return LogOutput;
case 's': return LogStatus;
case 't': return LogTime;
default: return LogMisc;
}
if (boolSetting(LogTimeStamps))
m_leftPane->append(LogWindow::logTimeStamp());
m_leftPane->append(input);
QTextCursor cursor = m_leftPane->textCursor();
cursor.movePosition(QTextCursor::End);
m_leftPane->setTextCursor(cursor);
m_leftPane->ensureCursorVisible();
}
void GlobalLogWindow::clearContents()
{
m_rightPane->clear();
m_leftPane->clear();
}
void GlobalLogWindow::setCursor(const QCursor &cursor)
{
m_rightPane->viewport()->setCursor(cursor);
m_leftPane->viewport()->setCursor(cursor);
QWidget::setCursor(cursor);
}
void GlobalLogWindow::clearUndoRedoStacks()
{
m_leftPane->clearUndoRedoStacks();
m_rightPane->clearUndoRedoStacks();
}
} // namespace Internal

View File

@@ -40,6 +40,8 @@ namespace Utils { class FancyLineEdit; }
namespace Debugger {
namespace Internal {
class DebuggerEngine;
class DebuggerPane;
class CombinedPane;
class InputPane;
@@ -48,7 +50,10 @@ class LogWindow : public QWidget
Q_OBJECT
public:
explicit LogWindow(QWidget *parent = nullptr);
explicit LogWindow(DebuggerEngine *engine);
~LogWindow() final;
DebuggerEngine *engine() const;
void setCursor(const QCursor &cursor);
@@ -59,11 +64,6 @@ public:
static QString logTimeStamp();
static bool writeLogContents(const QPlainTextEdit *editor, QWidget *parent = nullptr);
static QChar charForChannel(int channel);
static LogChannel channelForChar(QChar c);
void clearContents();
void sendCommand();
void executeLine();
@@ -73,7 +73,6 @@ public:
void repeatLastCommand();
signals:
void showPage();
void statusMessageRequested(const QString &msg, int);
private:
@@ -83,6 +82,27 @@ private:
QString m_queuedOutput;
Utils::FancyLineEdit *m_commandEdit;
bool m_ignoreNextInputEcho;
DebuggerEngine *m_engine;
};
class GlobalLogWindow : public QWidget
{
Q_OBJECT
public:
explicit GlobalLogWindow();
~GlobalLogWindow() final;
void setCursor(const QCursor &cursor);
void clearUndoRedoStacks();
void clearContents();
void doInput(const QString &input);
void doOutput(const QString &output);
private:
DebuggerPane *m_rightPane; // everything
DebuggerPane *m_leftPane; // combined input
};
} // namespace Internal

View File

@@ -65,12 +65,11 @@ namespace Internal {
PdbEngine::PdbEngine()
{
setObjectName("PdbEngine");
setDebuggerName("PDB");
}
void PdbEngine::executeDebuggerCommand(const QString &command, DebuggerLanguages languages)
void PdbEngine::executeDebuggerCommand(const QString &command)
{
if (!(languages & CppLanguage))
return;
QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
if (state() == DebuggerNotReady) {
showMessage("PDB PROCESS NOT RUNNING, PLAIN CMD IGNORED: " + command);
@@ -157,7 +156,7 @@ void PdbEngine::runEngine()
{
QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
showStatusMessage(tr("Running requested..."), 5000);
attemptBreakpointSynchronization();
BreakpointManager::claimBreakpointsForEngine(this);
notifyEngineRunAndInferiorStopOk();
updateAll();
}
@@ -246,35 +245,44 @@ void PdbEngine::selectThread(ThreadId threadId)
Q_UNUSED(threadId)
}
bool PdbEngine::acceptsBreakpoint(Breakpoint bp) const
bool PdbEngine::acceptsBreakpoint(const BreakpointParameters &bp) const
{
const QString fileName = bp.fileName();
const QString fileName = bp.fileName;
return fileName.endsWith(".py");
}
void PdbEngine::insertBreakpoint(Breakpoint bp)
void PdbEngine::insertBreakpoint(const Breakpoint &bp)
{
QTC_CHECK(bp.state() == BreakpointInsertRequested);
bp.notifyBreakpointInsertProceeding();
QTC_ASSERT(bp, return);
QTC_CHECK(bp->state() == BreakpointInsertionRequested);
notifyBreakpointInsertProceeding(bp);
QString loc;
if (bp.type() == BreakpointByFunction)
loc = bp.functionName();
const BreakpointParameters &params = bp->requestedParameters();
if (params.type == BreakpointByFunction)
loc = params.functionName;
else
loc = bp.fileName() + ':' + QString::number(bp.lineNumber());
loc = params.fileName + ':' + QString::number(params.lineNumber);
postDirectCommand("break " + loc);
}
void PdbEngine::removeBreakpoint(Breakpoint bp)
void PdbEngine::updateBreakpoint(const Breakpoint &bp)
{
QTC_CHECK(bp.state() == BreakpointRemoveRequested);
bp.notifyBreakpointRemoveProceeding();
BreakpointResponse br = bp.response();
showMessage(QString("DELETING BP %1 IN %2").arg(br.id.toString()).arg(bp.fileName()));
postDirectCommand("clear " + br.id.toString());
Q_UNUSED(bp);
QTC_CHECK(false);
}
void PdbEngine::removeBreakpoint(const Breakpoint &bp)
{
QTC_ASSERT(bp, return);
QTC_CHECK(bp->state() == BreakpointRemoveRequested);
notifyBreakpointRemoveProceeding(bp);
showMessage(QString("DELETING BP %1 IN %2")
.arg(bp->responseId()).arg(bp->fileName()));
postDirectCommand("clear " + bp->responseId());
// Pretend it succeeds without waiting for response.
bp.notifyBreakpointRemoveOk();
notifyBreakpointRemoveOk(bp);
}
void PdbEngine::loadSymbols(const QString &moduleName)
@@ -459,8 +467,8 @@ void PdbEngine::handleOutput(const QString &data)
void PdbEngine::handleOutput2(const QString &data)
{
foreach (QString line, data.split('\n')) {
const QStringList lines = data.split('\n');
for (const QString &line : lines) {
GdbMi item;
item.fromString(line);
@@ -479,21 +487,25 @@ void PdbEngine::handleOutput2(const QString &data)
} else if (line.startsWith("state=")) {
refreshState(item);
} else if (line.startsWith("Breakpoint")) {
int pos1 = line.indexOf(" at ");
const int pos1 = line.indexOf(" at ");
QTC_ASSERT(pos1 != -1, continue);
QString bpnr = line.mid(11, pos1 - 11);
int pos2 = line.lastIndexOf(':');
const QString bpnr = line.mid(11, pos1 - 11);
const int pos2 = line.lastIndexOf(':');
QTC_ASSERT(pos2 != -1, continue);
BreakpointResponse br;
br.id = BreakpointResponseId(bpnr);
br.fileName = line.mid(pos1 + 4, pos2 - pos1 - 4);
br.lineNumber = line.mid(pos2 + 1).toInt();
Breakpoint bp = breakHandler()->findBreakpointByFileAndLine(br.fileName, br.lineNumber, false);
if (bp.isValid()) {
bp.setResponse(br);
QTC_CHECK(!bp.needsChange());
bp.notifyBreakpointInsertOk();
}
const QString fileName = line.mid(pos1 + 4, pos2 - pos1 - 4);
const int lineNumber = line.mid(pos2 + 1).toInt();
const Breakpoint bp = Utils::findOrDefault(breakHandler()->breakpoints(), [&](const Breakpoint &bp) {
return bp->parameters().isLocatedAt(fileName, lineNumber, bp->markerFileName())
|| bp->requestedParameters().isLocatedAt(fileName, lineNumber, bp->markerFileName());
});
QTC_ASSERT(bp, continue);
bp->setResponseId(bpnr);
bp->setFileName(fileName);
bp->setLineNumber(lineNumber);
bp->adjustMarker();
bp->setPending(false);
QTC_CHECK(!bp->needsChange());
notifyBreakpointInsertOk(bp);
}
}
}

View File

@@ -71,13 +71,14 @@ private:
void activateFrame(int index) override;
void selectThread(ThreadId threadId) override;
bool acceptsBreakpoint(Breakpoint bp) const override;
void insertBreakpoint(Breakpoint bp) override;
void removeBreakpoint(Breakpoint bp) override;
bool acceptsBreakpoint(const BreakpointParameters &bp) const override;
void insertBreakpoint(const Breakpoint &bp) override;
void updateBreakpoint(const Breakpoint &bp) override;
void removeBreakpoint(const Breakpoint &bp) override;
void assignValueInDebugger(WatchItem *item,
const QString &expr, const QVariant &value) override;
void executeDebuggerCommand(const QString &command, DebuggerLanguages languages) override;
void executeDebuggerCommand(const QString &command) override;
void loadSymbols(const QString &moduleName) override;
void loadAllSymbols() override;

View File

@@ -1,7 +1,6 @@
HEADERS += \
$$PWD/qmlengine.h \
$$PWD/qmlengineutils.h \
$$PWD/qmlcppengine.h \
$$PWD/interactiveinterpreter.h \
$$PWD/qmlv8debuggerclientconstants.h \
$$PWD/qmlinspectoragent.h
@@ -9,6 +8,5 @@ HEADERS += \
SOURCES += \
$$PWD/qmlengine.cpp \
$$PWD/qmlengineutils.cpp \
$$PWD/qmlcppengine.cpp \
$$PWD/interactiveinterpreter.cpp \
$$PWD/qmlinspectoragent.cpp

View File

@@ -169,10 +169,10 @@ public:
void setBreakpoint(const QString type, const QString target,
bool enabled = true,int line = 0, int column = 0,
const QString condition = QString(), int ignoreCount = -1);
void clearBreakpoint(int breakpoint);
void clearBreakpoint(const Breakpoint &bp);
bool canChangeBreakpoint() const;
void changeBreakpoint(int breakpoint, bool enabled);
void changeBreakpoint(const Breakpoint &bp, bool enabled);
void setExceptionBreak(Exceptions type, bool enabled = false);
@@ -204,9 +204,8 @@ public:
QHash<int, QmlV8ObjectData> refVals; // The mapping of target object handles to retrieved values.
int sequence = -1;
QmlEngine *engine;
QHash<BreakpointModelId, int> breakpoints;
QHash<int, BreakpointModelId> breakpointsSync;
QList<int> breakpointsTemp;
QHash<int, Breakpoint> breakpointsSync;
QList<QString> breakpointsTemp;
LookupItems currentlyLookingUp; // Id -> inames
@@ -260,14 +259,14 @@ QmlEngine::QmlEngine()
: d(new QmlEnginePrivate(this, new QmlDebugConnection(this)))
{
setObjectName("QmlEngine");
setDebuggerName(tr("QML Debugger"));
QmlDebugConnection *connection = d->connection();
connect(stackHandler(), &StackHandler::stackChanged,
this, &QmlEngine::updateCurrentContext);
connect(stackHandler(), &StackHandler::currentIndexChanged,
this, &QmlEngine::updateCurrentContext);
connect(inspectorView(), &WatchTreeView::currentIndexChanged,
this, &QmlEngine::updateCurrentContext);
connect(&d->applicationLauncher, &ApplicationLauncher::processExited,
this, &QmlEngine::disconnected);
@@ -278,7 +277,7 @@ QmlEngine::QmlEngine()
debuggerConsole()->populateFileFinder();
debuggerConsole()->setScriptEvaluator([this](const QString &expr) {
executeDebuggerCommand(expr, QmlLanguage);
executeDebuggerCommand(expr);
});
d->connectionTimer.setInterval(4000);
@@ -336,7 +335,7 @@ void QmlEngine::handleLauncherStarted()
{
// FIXME: The QmlEngine never calls notifyInferiorPid() triggering the
// raising, so do it here manually for now.
runTool()->runControl()->applicationProcessHandle().activate();
ProcessHandle(inferiorPid()).activate();
tryToConnect();
}
@@ -347,7 +346,10 @@ void QmlEngine::appMessage(const QString &msg, Utils::OutputFormat /* format */)
void QmlEngine::connectionEstablished()
{
attemptBreakpointSynchronization();
connect(inspectorView(), &WatchTreeView::currentIndexChanged,
this, &QmlEngine::updateCurrentContext);
BreakpointManager::claimBreakpointsForEngine(this);
if (state() == EngineRunRequested)
notifyEngineRunAndInferiorRunOk();
@@ -435,7 +437,7 @@ void QmlEngine::appStartupFailed(const QString &errorMessage)
{
QString error = tr("Could not connect to the in-process QML debugger. %1").arg(errorMessage);
if (isMasterEngine()) {
if (companionEngine()) {
auto infoBox = new QMessageBox(ICore::mainWindow());
infoBox->setIcon(QMessageBox::Critical);
infoBox->setWindowTitle(Core::Constants::IDE_DISPLAY_NAME);
@@ -522,7 +524,8 @@ void QmlEngine::runEngine()
QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
if (!isSlaveEngine()) {
if (isPrimaryEngine()) {
// QML only.
if (runParameters().startMode == AttachToRemoteServer)
tryToConnect();
else if (runParameters().startMode == AttachToRemoteProcess)
@@ -538,8 +541,7 @@ void QmlEngine::startApplicationLauncher()
{
if (!d->applicationLauncher.isRunning()) {
const Runnable runnable = runParameters().inferior;
runTool()->appendMessage(tr("Starting %1 %2").arg(
QDir::toNativeSeparators(runnable.executable),
showMessage(tr("Starting %1 %2").arg(QDir::toNativeSeparators(runnable.executable),
runnable.commandLineArguments),
Utils::NormalMessageFormat);
d->applicationLauncher.start(runnable);
@@ -565,7 +567,6 @@ void QmlEngine::shutdownInferior()
// }
d->runCommand({DISCONNECT});
if (isSlaveEngine())
resetLocation();
stopApplicationLauncher();
closeConnection();
@@ -583,7 +584,6 @@ void QmlEngine::shutdownEngine()
stopApplicationLauncher();
notifyEngineShutdownFinished();
if (!isSlaveEngine())
showMessage(QString(), StatusBar);
}
@@ -689,34 +689,31 @@ void QmlEngine::selectThread(ThreadId threadId)
Q_UNUSED(threadId)
}
void QmlEngine::insertBreakpoint(Breakpoint bp)
void QmlEngine::insertBreakpoint(const Breakpoint &bp)
{
BreakpointState state = bp.state();
QTC_ASSERT(state == BreakpointInsertRequested, qDebug() << bp << this << state);
bp.notifyBreakpointInsertProceeding();
QTC_ASSERT(bp, return);
const BreakpointState state = bp->state();
QTC_ASSERT(state == BreakpointInsertionRequested, qDebug() << bp << this << state);
notifyBreakpointInsertProceeding(bp);
const BreakpointParameters &params = bp.parameters();
if (params.type == BreakpointAtJavaScriptThrow) {
BreakpointResponse br = bp.response();
br.pending = false;
bp.setResponse(br);
bp.notifyBreakpointInsertOk();
d->setExceptionBreak(AllExceptions, params.enabled);
const BreakpointParameters &requested = bp->requestedParameters();
if (requested.type == BreakpointAtJavaScriptThrow) {
bp->setPending(false);
notifyBreakpointInsertOk(bp);
d->setExceptionBreak(AllExceptions, requested.enabled);
} else if (params.type == BreakpointByFileAndLine) {
d->setBreakpoint(SCRIPTREGEXP, params.fileName,
params.enabled, params.lineNumber, 0,
params.condition, params.ignoreCount);
} else if (requested.type == BreakpointByFileAndLine) {
d->setBreakpoint(SCRIPTREGEXP, requested.fileName,
requested.enabled, requested.lineNumber, 0,
requested.condition, requested.ignoreCount);
} else if (params.type == BreakpointOnQmlSignalEmit) {
d->setBreakpoint(EVENT, params.functionName, params.enabled);
BreakpointResponse br = bp.response();
br.pending = false;
bp.setResponse(br);
bp.notifyBreakpointInsertOk();
} else if (requested.type == BreakpointOnQmlSignalEmit) {
d->setBreakpoint(EVENT, requested.functionName, requested.enabled);
bp->setPending(false);
notifyBreakpointInsertOk(bp);
}
d->breakpointsSync.insert(d->sequence, bp.id());
d->breakpointsSync.insert(d->sequence, bp);
}
void QmlEngine::resetLocation()
@@ -725,114 +722,64 @@ void QmlEngine::resetLocation()
d->currentlyLookingUp.clear();
}
void QmlEngine::removeBreakpoint(Breakpoint bp)
void QmlEngine::removeBreakpoint(const Breakpoint &bp)
{
const BreakpointParameters &params = bp.parameters();
QTC_ASSERT(bp, return);
const BreakpointParameters &params = bp->requestedParameters();
BreakpointState state = bp.state();
const BreakpointState state = bp->state();
QTC_ASSERT(state == BreakpointRemoveRequested, qDebug() << bp << this << state);
bp.notifyBreakpointRemoveProceeding();
int breakpoint = d->breakpoints.value(bp.id());
d->breakpoints.remove(bp.id());
notifyBreakpointRemoveProceeding(bp);
if (params.type == BreakpointAtJavaScriptThrow)
d->setExceptionBreak(AllExceptions);
else if (params.type == BreakpointOnQmlSignalEmit)
d->setBreakpoint(EVENT, params.functionName, false);
else
d->clearBreakpoint(breakpoint);
d->clearBreakpoint(bp);
if (bp.state() == BreakpointRemoveProceeding)
bp.notifyBreakpointRemoveOk();
if (bp->state() == BreakpointRemoveProceeding)
notifyBreakpointRemoveOk(bp);
}
void QmlEngine::changeBreakpoint(Breakpoint bp)
void QmlEngine::updateBreakpoint(const Breakpoint &bp)
{
BreakpointState state = bp.state();
QTC_ASSERT(state == BreakpointChangeRequested, qDebug() << bp << this << state);
bp.notifyBreakpointChangeProceeding();
QTC_ASSERT(bp, return);
const BreakpointState state = bp->state();
QTC_ASSERT(state == BreakpointUpdateRequested, qDebug() << bp << this << state);
notifyBreakpointChangeProceeding(bp);
const BreakpointParameters &params = bp.parameters();
const BreakpointParameters &requested = bp->requestedParameters();
BreakpointResponse br = bp.response();
if (params.type == BreakpointAtJavaScriptThrow) {
d->setExceptionBreak(AllExceptions, params.enabled);
} else if (params.type == BreakpointOnQmlSignalEmit) {
d->setBreakpoint(EVENT, params.functionName, params.enabled);
if (requested.type == BreakpointAtJavaScriptThrow) {
d->setExceptionBreak(AllExceptions, requested.enabled);
bp->setEnabled(requested.enabled);
} else if (requested.type == BreakpointOnQmlSignalEmit) {
d->setBreakpoint(EVENT, requested.functionName, requested.enabled);
bp->setEnabled(requested.enabled);
} else if (d->canChangeBreakpoint()) {
d->changeBreakpoint(d->breakpoints.value(bp.id()), params.enabled);
d->changeBreakpoint(bp, requested.enabled);
} else {
d->clearBreakpoint(d->breakpoints.take(bp.id()));
d->setBreakpoint(SCRIPTREGEXP, params.fileName,
params.enabled, params.lineNumber, 0,
params.condition, params.ignoreCount);
d->breakpointsSync.insert(d->sequence, bp.id());
d->clearBreakpoint(bp);
d->setBreakpoint(SCRIPTREGEXP, requested.fileName,
requested.enabled, requested.lineNumber, 0,
requested.condition, requested.ignoreCount);
d->breakpointsSync.insert(d->sequence, bp);
}
br.enabled = params.enabled;
bp.setResponse(br);
if (bp.state() == BreakpointChangeProceeding)
bp.notifyBreakpointChangeOk();
if (bp->state() == BreakpointUpdateProceeding)
notifyBreakpointChangeOk(bp);
}
void QmlEngine::attemptBreakpointSynchronization()
bool QmlEngine::acceptsBreakpoint(const BreakpointParameters &bp) const
{
if (!stateAcceptsBreakpointChanges()) {
showMessage("BREAKPOINT SYNCHRONIZATION NOT POSSIBLE IN CURRENT STATE");
return;
}
BreakHandler *handler = breakHandler();
DebuggerEngine *bpOwner = masterEngine();
for (Breakpoint bp : handler->unclaimedBreakpoints()) {
// Take ownership of the breakpoint. Requests insertion.
if (acceptsBreakpoint(bp))
bp.setEngine(bpOwner);
}
for (Breakpoint bp : handler->engineBreakpoints(bpOwner)) {
switch (bp.state()) {
case BreakpointNew:
// Should not happen once claimed.
QTC_CHECK(false);
continue;
case BreakpointInsertRequested:
insertBreakpoint(bp);
continue;
case BreakpointChangeRequested:
changeBreakpoint(bp);
continue;
case BreakpointRemoveRequested:
removeBreakpoint(bp);
continue;
case BreakpointChangeProceeding:
case BreakpointInsertProceeding:
case BreakpointRemoveProceeding:
case BreakpointInserted:
case BreakpointDead:
continue;
}
QTC_ASSERT(false, qDebug() << "UNKNOWN STATE" << bp << state());
}
DebuggerEngine::attemptBreakpointSynchronization();
}
bool QmlEngine::acceptsBreakpoint(Breakpoint bp) const
{
if (!bp.parameters().isCppBreakpoint())
return true;
//If it is a Cpp Breakpoint query if the type can be also handled by the debugger client
//TODO: enable setting of breakpoints before start of debug session
//For now, the event breakpoint can be set after the activeDebuggerClient is known
//This is because the older client does not support BreakpointOnQmlSignalHandler
BreakpointType type = bp.type();
return type == BreakpointOnQmlSignalEmit
|| type == BreakpointByFileAndLine
|| type == BreakpointAtJavaScriptThrow;
if (bp.type == BreakpointOnQmlSignalEmit || bp.type == BreakpointAtJavaScriptThrow)
return true;
return bp.isQmlFileAndLineBreakpoint();
}
void QmlEngine::loadSymbols(const QString &moduleName)
@@ -1041,6 +988,11 @@ void QmlEngine::doUpdateLocals(const UpdateParameters &params)
d->updateLocals();
}
Context QmlEngine::languageContext() const
{
return Context(Constants::C_QMLDEBUGGER);
}
void QmlEngine::disconnected()
{
showMessage(tr("QML Debugger disconnected."), StatusBar);
@@ -1084,11 +1036,8 @@ void QmlEngine::updateCurrentContext()
+ (context.isEmpty() ? tr("Global QML Context") : context));
}
void QmlEngine::executeDebuggerCommand(const QString &command, DebuggerLanguages languages)
void QmlEngine::executeDebuggerCommand(const QString &command)
{
if (!(languages & QmlLanguage))
return;
if (state() == InferiorStopOk) {
StackHandler *handler = stackHandler();
if (handler->isContentsValid() && handler->currentFrame().isUsable()) {
@@ -1116,6 +1065,16 @@ void QmlEngine::executeDebuggerCommand(const QString &command, DebuggerLanguages
}
}
bool QmlEngine::companionPreventsActions() const
{
// We need a C++ Engine in a Running state to do anything sensible
// as otherwise the debugger services in the debuggee are unresponsive.
if (DebuggerEngine *companion = companionEngine())
return companion->state() != InferiorRunOk;
return false;
}
void QmlEnginePrivate::updateScriptSource(const QString &fileName, int lineOffset, int columnOffset,
const QString &source)
{
@@ -1181,11 +1140,8 @@ void QmlEngine::connectionFailed()
// this is only an error if we are already connected and something goes wrong.
if (isConnected()) {
showMessage(tr("QML Debugger: Connection failed."), StatusBar);
if (!isSlaveEngine()) { // normal flow for slave engine when gdb exits
notifyInferiorSpontaneousStop();
notifyInferiorIll();
}
} else {
d->connectionTimer.stop();
connectionStartupFailed();
@@ -1492,7 +1448,7 @@ void QmlEnginePrivate::setBreakpoint(const QString type, const QString target,
}
}
void QmlEnginePrivate::clearBreakpoint(int breakpoint)
void QmlEnginePrivate::clearBreakpoint(const Breakpoint &bp)
{
// { "seq" : <number>,
// "type" : "request",
@@ -1502,7 +1458,7 @@ void QmlEnginePrivate::clearBreakpoint(int breakpoint)
// }
DebuggerCommand cmd(CLEARBREAKPOINT);
cmd.arg(BREAKPOINT, breakpoint);
cmd.arg(BREAKPOINT, bp->responseId().toInt());
runCommand(cmd);
}
@@ -1511,10 +1467,10 @@ bool QmlEnginePrivate::canChangeBreakpoint() const
return supportChangeBreakpoint;
}
void QmlEnginePrivate::changeBreakpoint(int breakpoint, bool enabled)
void QmlEnginePrivate::changeBreakpoint(const Breakpoint &bp, bool enabled)
{
DebuggerCommand cmd(CHANGEBREAKPOINT);
cmd.arg(BREAKPOINT, breakpoint);
cmd.arg(BREAKPOINT, bp->responseId().toInt());
cmd.arg(ENABLED, enabled);
runCommand(cmd);
}
@@ -1780,27 +1736,26 @@ void QmlEnginePrivate::messageReceived(const QByteArray &data)
int seq = resp.value("request_seq").toInt();
const QVariantMap breakpointData = resp.value(BODY).toMap();
int index = breakpointData.value("breakpoint").toInt();
const QString index = QString::number(breakpointData.value("breakpoint").toInt());
if (breakpointsSync.contains(seq)) {
BreakpointModelId id = breakpointsSync.take(seq);
breakpoints.insert(id, index);
Breakpoint bp = breakpointsSync.take(seq);
QTC_ASSERT(bp, return);
bp->setParameters(bp->requestedParameters()); // Assume it worked.
bp->setResponseId(index);
//Is actual position info present? Then breakpoint was
//accepted
const QVariantList actualLocations =
breakpointData.value("actual_locations").toList();
const int line = breakpointData.value("line").toInt() + 1;
if (actualLocations.count()) {
//The breakpoint requested line should be same as
//actual line
BreakHandler *handler = engine->breakHandler();
Breakpoint bp = handler->breakpointById(id);
if (bp.state() != BreakpointInserted) {
BreakpointResponse br = bp.response();
br.lineNumber = breakpointData.value("line").toInt() + 1;
br.pending = false;
bp.setResponse(br);
bp.notifyBreakpointInsertOk();
if (bp && bp->state() != BreakpointInserted) {
bp->setLineNumber(line);
bp->setPending(false);
engine->notifyBreakpointInsertOk(bp);
}
}
@@ -1900,14 +1855,18 @@ void QmlEnginePrivate::messageReceived(const QByteArray &data)
bool inferiorStop = true;
QList<int> v8BreakpointIds;
{
QList<Breakpoint> v8Breakpoints;
const QVariantList v8BreakpointIdList = breakData.value("breakpoints").toList();
for (const QVariant &breakpointId : v8BreakpointIdList)
v8BreakpointIds << breakpointId.toInt();
for (const QVariant &breakpointId : v8BreakpointIdList) {
const QString x = breakpointId.toString();
const QString responseId = QString::number(breakpointId.toInt());
Breakpoint bp = engine->breakHandler()->findBreakpointByResponseId(responseId);
QTC_ASSERT(bp, continue);
v8Breakpoints << bp;
}
if (!v8BreakpointIds.isEmpty() && invocationText.startsWith("[anonymous]()")
if (!v8Breakpoints.isEmpty() && invocationText.startsWith("[anonymous]()")
&& scriptUrl.endsWith(".qml")
&& sourceLineText.trimmed().startsWith('(')) {
@@ -1915,15 +1874,12 @@ void QmlEnginePrivate::messageReceived(const QByteArray &data)
// -> relocate the breakpoint to column: 1 and continue
int newColumn = sourceLineText.indexOf('(') + 1;
BreakHandler *handler = engine->breakHandler();
for (int v8Id : v8BreakpointIds) {
const BreakpointModelId id = breakpoints.key(v8Id);
Breakpoint bp = handler->breakpointById(id);
if (bp.isValid()) {
const BreakpointParameters &params = bp.parameters();
for (const Breakpoint &bp : v8Breakpoints) {
QTC_ASSERT(bp, continue);
const BreakpointParameters &params = bp->requestedParameters();
clearBreakpoint(v8Id);
clearBreakpoint(bp);
setBreakpoint(SCRIPTREGEXP,
params.fileName,
params.enabled,
@@ -1931,8 +1887,7 @@ void QmlEnginePrivate::messageReceived(const QByteArray &data)
newColumn,
params.condition,
params.ignoreCount);
breakpointsSync.insert(sequence, id);
}
breakpointsSync.insert(sequence, bp);
}
continueDebugging(Continue);
inferiorStop = false;
@@ -1946,29 +1901,23 @@ void QmlEnginePrivate::messageReceived(const QByteArray &data)
if (inferiorStop) {
//Update breakpoint data
BreakHandler *handler = engine->breakHandler();
for (int v8Id : v8BreakpointIds) {
const BreakpointModelId id = breakpoints.key(v8Id);
Breakpoint bp = handler->breakpointById(id);
if (bp) {
BreakpointResponse br = bp.response();
if (br.functionName.isEmpty()) {
br.functionName = invocationText;
bp.setResponse(br);
}
if (bp.state() != BreakpointInserted) {
br.lineNumber = breakData.value("sourceLine").toInt() + 1;
br.pending = false;
bp.setResponse(br);
bp.notifyBreakpointInsertOk();
for (const Breakpoint &bp : v8Breakpoints) {
QTC_ASSERT(bp, continue);
if (bp->functionName().isEmpty()) {
bp->setFunctionName(invocationText);
}
if (bp->state() != BreakpointInserted) {
bp->setLineNumber(breakData.value("sourceLine").toInt() + 1);
bp->setPending(false);
engine->notifyBreakpointInsertOk(bp);
}
}
if (engine->state() == InferiorRunOk) {
for (const QVariant &breakpointId : v8BreakpointIds) {
if (breakpointsTemp.contains(breakpointId.toInt()))
clearBreakpoint(breakpointId.toInt());
for (const Breakpoint &bp : v8Breakpoints) {
QTC_ASSERT(bp, continue);
if (breakpointsTemp.contains(bp->responseId()))
clearBreakpoint(bp);
}
engine->notifyInferiorSpontaneousStop();
backtrace();

View File

@@ -34,7 +34,6 @@
namespace Debugger {
namespace Internal {
class WatchItem;
class QmlEnginePrivate;
class QmlInspectorAgent;
@@ -68,7 +67,6 @@ private:
void setState(DebuggerState state, bool forced) override;
void gotoLocation(const Internal::Location &location) override;
void insertBreakpoint(Breakpoint bp) override;
bool canDisplayTooltip() const override { return false; }
@@ -97,10 +95,10 @@ private:
void activateFrame(int index) override;
void selectThread(ThreadId threadId) override;
void attemptBreakpointSynchronization() override;
void removeBreakpoint(Breakpoint bp) override;
void changeBreakpoint(Breakpoint bp) override;
bool acceptsBreakpoint(Breakpoint bp) const override;
bool acceptsBreakpoint(const BreakpointParameters &bp) const final;
void insertBreakpoint(const Breakpoint &bp) final;
void removeBreakpoint(const Breakpoint &bp) final;
void updateBreakpoint(const Breakpoint &bp) final;
void assignValueInDebugger(WatchItem *item,
const QString &expr, const QVariant &value) override;
@@ -117,12 +115,14 @@ private:
void updateItem(const QString &iname) override;
void expandItem(const QString &iname) override;
void selectWatchData(const QString &iname) override;
void executeDebuggerCommand(const QString &command, DebuggerLanguages languages) override;
void executeDebuggerCommand(const QString &command) override;
bool companionPreventsActions() const override;
bool hasCapability(unsigned) const override;
void quitDebugger() override;
void doUpdateLocals(const UpdateParameters &params) override;
Core::Context languageContext() const override;
void closeConnection();
void startApplicationLauncher();
@@ -135,7 +135,6 @@ private:
bool isConnected() const;
private:
friend class QmlCppEngine;
friend class QmlEnginePrivate;
friend class QmlInspectorAgent;
QmlEnginePrivate *d;

View File

@@ -67,7 +67,8 @@ Q_LOGGING_CATEGORY(qmlInspectorLog, "qtc.dbg.qmlinspector")
QmlInspectorAgent::QmlInspectorAgent(QmlEngine *engine, QmlDebugConnection *connection)
: m_qmlEngine(engine)
, m_objectToSelect(WatchItem::InvalidId)
, m_masterEngine(engine->masterEngine())
, m_toolsClient(nullptr)
, m_targetToSync(NoTarget)
, m_debugIdToSelect(WatchItem::InvalidId)
, m_currentSelectedDebugId(WatchItem::InvalidId)
, m_inspectorToolsContext("Debugger.QmlInspector")
@@ -786,7 +787,7 @@ void QmlInspectorAgent::toolsClientStateChanged(QmlDebugClient::State state)
Core::ICore::addAdditionalContext(m_inspectorToolsContext);
m_toolsClientConnected = true;
enableTools(m_masterEngine->state() == InferiorRunOk);
enableTools(m_qmlEngine->state() == InferiorRunOk);
if (m_showAppOnTopAction->isChecked())
m_toolsClient->showAppOnTop(true);
@@ -901,7 +902,7 @@ void QmlInspectorAgent::setActiveEngineClient(BaseEngineDebugClient *client)
void QmlInspectorAgent::jumpToObjectDefinitionInEditor(
const FileReference &objSource, int debugId)
{
const QString fileName = m_masterEngine->toFileInProject(objSource.url());
const QString fileName = m_qmlEngine->toFileInProject(objSource.url());
Core::EditorManager::openEditorAt(fileName, objSource.lineNumber());
if (debugId != WatchItem::InvalidId && debugId != m_currentSelectedDebugId) {

View File

@@ -134,7 +134,6 @@ private:
QList<int> m_fetchDataIds;
QTimer m_delayQueryTimer;
DebuggerEngine *m_masterEngine;
QHash<QString, QmlDebug::BaseEngineDebugClient*> m_engineClients;
QmlDebug::BaseToolsClient *m_toolsClient = nullptr;

View File

@@ -676,8 +676,8 @@ bool RegisterHandler::setData(const QModelIndex &idx, const QVariant &data, int
bool RegisterHandler::contextMenuEvent(const ItemViewEvent &ev)
{
const bool actionsEnabled = m_engine->debuggerActionsEnabled();
const DebuggerState state = m_engine->state();
const bool actionsEnabled = m_engine->debuggerActionsEnabled();
RegisterItem *registerItem = itemForIndexAtLevel<1>(ev.index());
RegisterSubItem *registerSubItem = itemForIndexAtLevel<2>(ev.index());

View File

@@ -25,15 +25,28 @@
#include "snapshothandler.h"
#include "analyzer/analyzermanager.h"
#include "debuggeractions.h"
#include "debuggerinternalconstants.h"
#include "debuggericons.h"
#include "debuggercore.h"
#include "debuggerruncontrol.h"
#include "stackhandler.h"
#include <coreplugin/icontext.h>
#include <coreplugin/icore.h>
#include <coreplugin/modemanager.h>
#include <utils/basetreeview.h>
#include <utils/treemodel.h>
#include <utils/qtcassert.h>
#include <QDebug>
#include <QFile>
#include <QMenu>
#include <QTimer>
using namespace Core;
using namespace Utils;
namespace Debugger {
namespace Internal {
@@ -102,64 +115,108 @@ QDebug operator<<(QDebug d, const SnapshotData &f)
}
#endif
class EngineItem : public QObject, public TreeItem
{
public:
QVariant data(int column, int role) const final;
bool setData(int row, const QVariant &data, int role) final;
const bool m_isPreset = false;
QPointer<DebuggerEngine> m_engine;
};
class EngineManagerPrivate : public QObject
{
public:
EngineManagerPrivate()
{
m_engineModel.setHeader({EngineManager::tr("Name"), EngineManager::tr("File")});
m_engineModel.rootItem()->appendChild(new EngineItem); // The preset case.
m_engineChooser = new QComboBox;
m_engineChooser->setVisible(false);
m_engineChooser->setModel(&m_engineModel);
connect(m_engineChooser, static_cast<void(QComboBox::*)(int)>(&QComboBox::activated),
this, &EngineManagerPrivate::activateEngineByIndex);
}
~EngineManagerPrivate()
{
delete m_engineChooser;
}
EngineItem *findEngineItem(DebuggerEngine *engine);
void activateEngine(DebuggerEngine *engine);
void activateEngineItem(EngineItem *engineItem);
void activateEngineByIndex(int index);
void selectUiForCurrentEngine();
void updateEngineChooserVisibility();
TreeModel<TypedTreeItem<EngineItem>, EngineItem> m_engineModel;
QPointer<EngineItem> m_currentItem;
Core::Id m_previousMode;
QPointer<QComboBox> m_engineChooser;
};
////////////////////////////////////////////////////////////////////////
//
// SnapshotHandler
// EngineManager
//
////////////////////////////////////////////////////////////////////////
/*!
\class Debugger::Internal::SnapshotHandler
\brief The SnapshotHandler class provides a model to represent the
snapshots in a QTreeView.
A snapshot represents a debugging session.
\class Debugger::Internal::EngineManager
\brief The EngineManager manages running debugger engines.
*/
SnapshotHandler::SnapshotHandler() = default;
static EngineManager *theEngineManager = nullptr;
static EngineManagerPrivate *d = nullptr;
SnapshotHandler::~SnapshotHandler()
EngineManager::EngineManager()
{
for (int i = m_snapshots.size(); --i >= 0; ) {
if (DebuggerRunTool *runTool = at(i)) {
const DebuggerRunParameters &rp = runTool->runParameters();
if (rp.isSnapshot && !rp.coreFile.isEmpty())
QFile::remove(rp.coreFile);
}
}
theEngineManager = this;
d = new EngineManagerPrivate;
}
int SnapshotHandler::rowCount(const QModelIndex &parent) const
QWidget *EngineManager::engineChooser()
{
// Since the stack is not a tree, row count is 0 for any valid parent
return parent.isValid() ? 0 : m_snapshots.size();
return d->m_engineChooser;
}
int SnapshotHandler::columnCount(const QModelIndex &parent) const
EngineManager::~EngineManager()
{
return parent.isValid() ? 0 : 2;
theEngineManager = nullptr;
delete d;
}
QVariant SnapshotHandler::data(const QModelIndex &index, int role) const
EngineManager *EngineManager::instance()
{
if (!index.isValid() || index.row() >= m_snapshots.size())
return QVariant();
return theEngineManager;
}
const DebuggerRunTool *runTool = at(index.row());
QAbstractItemModel *EngineManager::model()
{
return &d->m_engineModel;
}
void EngineManager::activateEngine(DebuggerEngine *engine)
{
d->activateEngine(engine);
}
QVariant EngineItem::data(int column, int role) const
{
if (m_engine) {
if (role == SnapshotCapabilityRole)
return runTool && runTool->activeEngine()->hasCapability(SnapshotCapability);
return m_engine->hasCapability(SnapshotCapability);
if (!runTool)
return QLatin1String("<finished>");
const DebuggerRunParameters &rp = runTool->runParameters();
const DebuggerRunParameters &rp = m_engine->runParameters();
switch (role) {
case Qt::DisplayRole:
switch (index.column()) {
switch (column) {
case 0:
return rp.displayName;
return m_engine->displayName();
case 1:
return rp.coreFile.isEmpty() ? rp.inferior.executable : rp.coreFile;
}
@@ -169,110 +226,189 @@ QVariant SnapshotHandler::data(const QModelIndex &index, int role) const
return QVariant();
case Qt::DecorationRole:
// Return icon that indicates whether this is the active stack frame.
if (index.column() == 0)
return (index.row() == m_currentIndex) ? Icons::LOCATION.icon() : Icons::EMPTY.icon();
// Return icon that indicates whether this is the active engine
if (column == 0)
return d->m_currentItem == this ? Icons::LOCATION.icon() : Icons::EMPTY.icon();
default:
break;
}
return QVariant();
}
QVariant SnapshotHandler::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch (section) {
case 0: return tr("Name");
case 1: return tr("File");
};
} else {
switch (role) {
case Qt::DisplayRole:
return EngineManager::tr("Debugger Preset");
default:
break;
}
}
return QVariant();
}
Qt::ItemFlags SnapshotHandler::flags(const QModelIndex &index) const
bool EngineItem::setData(int row, const QVariant &value, int role)
{
if (index.row() >= m_snapshots.size())
return nullptr;
if (index.row() == m_snapshots.size())
return QAbstractTableModel::flags(index);
return true ? QAbstractTableModel::flags(index) : Qt::ItemFlags({});
Q_UNUSED(row);
if (!m_engine)
return false;
if (role == BaseTreeView::ItemActivatedRole) {
EngineItem *engineItem = d->findEngineItem(m_engine);
d->activateEngineItem(engineItem);
return true;
}
if (role == BaseTreeView::ItemViewEventRole) {
ItemViewEvent ev = value.value<ItemViewEvent>();
if (auto cmev = ev.as<QContextMenuEvent>()) {
auto menu = new QMenu(ev.view());
QAction *actCreate = menu->addAction(tr("Create Snapshot"));
actCreate->setEnabled(m_engine->hasCapability(SnapshotCapabilityRole));
menu->addSeparator();
QAction *actRemove = menu->addAction(tr("Abort Debugger"));
actRemove->setEnabled(true);
QAction *act = menu->exec(cmev->globalPos());
if (act == actCreate && m_engine)
m_engine->createSnapshot();
else if (act == actRemove && m_engine)
m_engine->quitDebugger();
return true;
}
if (auto kev = ev.as<QKeyEvent>(QEvent::KeyPress)) {
if (kev->key() == Qt::Key_Delete && m_engine) {
m_engine->quitDebugger();
} else if (kev->key() == Qt::Key_Return || kev->key() == Qt::Key_Enter) {
d->activateEngineByIndex(row);
}
return true;
}
}
return false;
}
void SnapshotHandler::activateSnapshot(int index)
void EngineManagerPrivate::activateEngineByIndex(int index)
{
beginResetModel();
m_currentIndex = index;
//qDebug() << "ACTIVATING INDEX: " << m_currentIndex << " OF " << size();
Internal::displayDebugger(at(index));
endResetModel();
activateEngineItem(m_engineModel.rootItem()->childAt(index));
}
void SnapshotHandler::createSnapshot(int index)
void EngineManagerPrivate::activateEngineItem(EngineItem *engineItem)
{
DebuggerRunTool *runTool = at(index);
QTC_ASSERT(runTool, return);
runTool->engine()->createSnapshot();
if (m_currentItem) {
if (DebuggerEngine *engine = m_currentItem->m_engine) {
const Context context = engine->languageContext();
ICore::removeAdditionalContext(context);
}
}
m_currentItem = engineItem;
if (m_currentItem) {
if (DebuggerEngine *engine = m_currentItem->m_engine) {
const Context context = engine->languageContext();
ICore::addAdditionalContext(context);
engine->gotoCurrentLocation();
}
}
selectUiForCurrentEngine();
}
void SnapshotHandler::removeSnapshot(int index)
void EngineManagerPrivate::selectUiForCurrentEngine()
{
DebuggerRunTool *runTool = at(index);
//qDebug() << "REMOVING " << runTool;
QTC_ASSERT(runTool, return);
#if 0
// See http://sourceware.org/bugzilla/show_bug.cgi?id=11241.
setState(EngineSetupRequested);
postCommand("set stack-cache off");
#endif
//QString fileName = runTool->startParameters().coreFile;
//if (!fileName.isEmpty())
// QFile::remove(fileName);
beginResetModel();
m_snapshots.removeAt(index);
if (index == m_currentIndex)
m_currentIndex = -1;
else if (index < m_currentIndex)
--m_currentIndex;
//runTool->quitDebugger();
endResetModel();
Perspective *perspective = nullptr;
int row = 0;
if (m_currentItem && m_currentItem->m_engine) {
perspective = m_currentItem->m_engine->perspective();
row = m_engineModel.rootItem()->indexOf(m_currentItem);
}
m_engineChooser->setCurrentIndex(row);
if (perspective)
perspective->select();
else
selectPerspective(Debugger::Constants::PRESET_PERSPRECTIVE_ID);
m_engineModel.rootItem()->forFirstLevelChildren([this](EngineItem *engineItem) {
if (engineItem && engineItem->m_engine)
engineItem->m_engine->updateMarkers();
});
emit theEngineManager->currentEngineChanged();
}
void SnapshotHandler::removeAll()
void EngineManager::selectUiForCurrentEngine()
{
beginResetModel();
m_snapshots.clear();
m_currentIndex = -1;
endResetModel();
d->selectUiForCurrentEngine();
}
void SnapshotHandler::appendSnapshot(DebuggerRunTool *runTool)
EngineItem *EngineManagerPrivate::findEngineItem(DebuggerEngine *engine)
{
beginResetModel();
m_snapshots.append(runTool);
m_currentIndex = size() - 1;
endResetModel();
return m_engineModel.rootItem()->findFirstLevelChild([engine](EngineItem *engineItem) {
return engineItem->m_engine == engine;
});
}
void SnapshotHandler::removeSnapshot(DebuggerRunTool *runTool)
void EngineManagerPrivate::activateEngine(DebuggerEngine *engine)
{
EngineItem *engineItem = findEngineItem(engine);
activateEngineItem(engineItem);
}
void EngineManagerPrivate::updateEngineChooserVisibility()
{
// Show it if there's more than one option (i.e. not the the preset engine only)
const int count = m_engineModel.rootItem()->childCount();
m_engineChooser->setVisible(count >= 2);
}
void EngineManager::registerEngine(DebuggerEngine *engine)
{
auto engineItem = new EngineItem;
engineItem->m_engine = engine;
d->m_engineModel.rootItem()->appendChild(engineItem);
d->updateEngineChooserVisibility();
}
void EngineManager::activateDebugMode()
{
if (ModeManager::currentModeId() != Constants::MODE_DEBUG) {
d->m_previousMode = ModeManager::currentModeId();
ModeManager::activateMode(Constants::MODE_DEBUG);
}
}
void EngineManager::unregisterEngine(DebuggerEngine *engine)
{
// Could be that the run controls died before it was appended.
int index = m_snapshots.indexOf(runTool);
if (index != -1)
removeSnapshot(index);
if (auto engineItem = d->findEngineItem(engine))
d->m_engineModel.destroyItem(engineItem);
d->updateEngineChooserVisibility();
emit theEngineManager->currentEngineChanged();
}
void SnapshotHandler::setCurrentIndex(int index)
QList<QPointer<DebuggerEngine>> EngineManager::engines()
{
beginResetModel();
m_currentIndex = index;
endResetModel();
QList<QPointer<DebuggerEngine>> result;
d->m_engineModel.forItemsAtLevel<1>([&result](EngineItem *engineItem) {
if (DebuggerEngine *engine = engineItem->m_engine)
result.append(engine);
});
return result;
}
DebuggerRunTool *SnapshotHandler::at(int i) const
QPointer<DebuggerEngine> EngineManager::currentEngine()
{
return m_snapshots.at(i).data();
return d->m_currentItem ? d->m_currentItem->m_engine : nullptr;
}
} // namespace Internal

View File

@@ -25,47 +25,43 @@
#pragma once
#include <utils/treemodel.h>
#include <QAbstractTableModel>
#include <QComboBox>
#include <QPointer>
namespace Debugger {
class DebuggerRunTool;
namespace Internal {
class SnapshotHandler : public QAbstractTableModel
class DebuggerEngine;
class EngineManager : public QObject
{
Q_OBJECT
public:
explicit SnapshotHandler();
~SnapshotHandler() override;
explicit EngineManager();
~EngineManager() final;
// Called from SnapshotHandler after a new snapshot has been added
void removeAll();
QAbstractItemModel *model() { return this; }
int currentIndex() const { return m_currentIndex; }
void appendSnapshot(DebuggerRunTool *runTool);
void removeSnapshot(DebuggerRunTool *runTool);
void setCurrentIndex(int index);
int size() const { return m_snapshots.size(); }
DebuggerRunTool *at(int index) const;
static EngineManager *instance();
static QAbstractItemModel *model();
void createSnapshot(int index);
void activateSnapshot(int index);
void removeSnapshot(int index);
static void registerEngine(DebuggerEngine *engine);
static void unregisterEngine(DebuggerEngine *engine);
static void activateEngine(DebuggerEngine *engine);
static void activateDebugMode();
private:
// QAbstractTableModel
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
static QList<QPointer<DebuggerEngine> > engines();
static QPointer<DebuggerEngine> currentEngine();
int m_currentIndex = -1;
QList< QPointer<DebuggerRunTool> > m_snapshots;
static void selectUiForCurrentEngine();
static QWidget *engineChooser();
signals:
void engineStateChanged(DebuggerEngine *engine);
void currentEngineChanged();
};
} // namespace Internal

View File

@@ -1,108 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "snapshotwindow.h"
#include "snapshothandler.h"
#include "debuggeractions.h"
#include "debuggerinternalconstants.h"
#include "debuggercore.h"
#include "debuggerruncontrol.h"
#include <utils/qtcassert.h>
#include <utils/savedaction.h>
#include <QDebug>
#include <QMenu>
#include <QKeyEvent>
namespace Debugger {
namespace Internal {
///////////////////////////////////////////////////////////////////////
//
// SnapshotWindow
//
///////////////////////////////////////////////////////////////////////
SnapshotTreeView::SnapshotTreeView(SnapshotHandler *handler)
{
m_snapshotHandler = handler;
setWindowTitle(tr("Snapshots"));
}
void SnapshotTreeView::rowActivated(const QModelIndex &index)
{
m_snapshotHandler->activateSnapshot(index.row());
}
void SnapshotTreeView::keyPressEvent(QKeyEvent *ev)
{
if (ev->key() == Qt::Key_Delete) {
QItemSelectionModel *sm = selectionModel();
QTC_ASSERT(sm, return);
QModelIndexList si = sm->selectedIndexes();
if (si.isEmpty())
si.append(currentIndex().sibling(currentIndex().row(), 0));
foreach (const QModelIndex &idx, si)
if (idx.column() == 0)
removeSnapshot(idx.row());
}
BaseTreeView::keyPressEvent(ev);
}
void SnapshotTreeView::contextMenuEvent(QContextMenuEvent *ev)
{
QModelIndex idx = indexAt(ev->pos());
QMenu menu;
QAction *actCreate = menu.addAction(tr("Create Snapshot"));
actCreate->setEnabled(idx.data(SnapshotCapabilityRole).toBool());
menu.addSeparator();
QAction *actRemove = menu.addAction(tr("Remove Snapshot"));
actRemove->setEnabled(idx.isValid());
menu.addSeparator();
menu.addAction(action(SettingsDialog));
QAction *act = menu.exec(ev->globalPos());
if (act == actCreate)
m_snapshotHandler->createSnapshot(idx.row());
else if (act == actRemove)
removeSnapshot(idx.row());
}
void SnapshotTreeView::removeSnapshot(int i)
{
m_snapshotHandler->at(i)->quitDebugger();
}
} // namespace Internal
} // namespace Debugger

View File

@@ -1,52 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <utils/basetreeview.h>
namespace Debugger {
namespace Internal {
class SnapshotHandler;
class SnapshotTreeView : public Utils::BaseTreeView
{
Q_OBJECT
public:
explicit SnapshotTreeView(SnapshotHandler *handler);
private:
void rowActivated(const QModelIndex &index);
void removeSnapshot(int i);
void keyPressEvent(QKeyEvent *ev) override;
void contextMenuEvent(QContextMenuEvent *ev) override;
SnapshotHandler *m_snapshotHandler;
};
} // namespace Internal
} // namespace Debugger

View File

@@ -121,7 +121,7 @@ bool SourceFilesHandler::setData(const QModelIndex &idx, const QVariant &data, i
QModelIndex index = idx.sibling(idx.row(), 0);
QString name = index.data().toString();
auto addAction = [this, menu](const QString &display, bool on, const std::function<void()> &onTriggered) {
auto addAction = [menu](const QString &display, bool on, const std::function<void()> &onTriggered) {
QAction *act = menu->addAction(display);
act->setEnabled(on);
QObject::connect(act, &QAction::triggered, onTriggered);

View File

@@ -25,17 +25,17 @@
#include "terminal.h"
#include "debuggerruncontrol.h"
#include <QDebug>
#include <QIODevice>
#include <QSocketNotifier>
#include <projectexplorer/runconfiguration.h>
#include <coreplugin/icore.h>
#include <utils/qtcassert.h>
#include <utils/hostosinfo.h>
#include <QDebug>
#include <QIODevice>
#include <QSocketNotifier>
#ifdef Q_OS_UNIX
# define DEBUGGER_USE_TERMINAL
#endif
@@ -168,13 +168,12 @@ void Terminal::onSlaveReaderActivated(int fd)
#endif
}
TerminalRunner::TerminalRunner(DebuggerRunTool *debugger)
: RunWorker(debugger->runControl())
TerminalRunner::TerminalRunner(RunControl *runControl, const Runnable &stubRunnable)
: RunWorker(runControl)
{
setDisplayName("TerminalRunner");
const DebuggerRunParameters &rp = debugger->runParameters();
m_stubRunnable = rp.inferior;
m_stubRunnable = stubRunnable;
connect(&m_stubProc, &ConsoleProcess::processError,
this, &TerminalRunner::stubError);

View File

@@ -71,7 +71,8 @@ private:
class TerminalRunner : public ProjectExplorer::RunWorker
{
public:
explicit TerminalRunner(DebuggerRunTool *runControl);
TerminalRunner(ProjectExplorer::RunControl *runControl,
const ProjectExplorer::Runnable &stubRunnable);
qint64 applicationPid() const { return m_applicationPid; }
qint64 applicationMainThreadId() const { return m_applicationMainThreadId; }

View File

@@ -367,7 +367,7 @@ void ThreadsHandler::updateThreadBox()
forItemsAtLevel<1>([&list](ThreadItem *item) {
list.append(QString::fromLatin1("#%1 %2").arg(item->threadData.id.raw()).arg(item->threadData.name));
});
Internal::setThreadBoxContents(list, indexForThreadId(this, m_currentId));
m_engine->setThreadBoxContents(list, indexForThreadId(this, m_currentId));
}
ThreadData ThreadsHandler::thread(ThreadId id) const

View File

@@ -1298,7 +1298,7 @@ void WatchModel::timerEvent(QTimerEvent *event)
}
ungrabWidget();
}
showMessage(msg, StatusBar);
m_engine->showMessage(msg, StatusBar);
} else {
WatchModelBase::timerEvent(event);
}
@@ -1951,8 +1951,10 @@ QString WatchModel::nameForFormat(int format)
///////////////////////////////////////////////////////////////////////
WatchHandler::WatchHandler(DebuggerEngine *engine)
: m_engine(engine)
{
m_model = new WatchModel(this, engine);
loadSessionDataForEngine();
}
WatchHandler::~WatchHandler()
@@ -2072,7 +2074,13 @@ void WatchHandler::resetValueCache()
void WatchHandler::resetWatchers()
{
loadSessionData();
loadFormats();
theWatcherNames.clear();
theWatcherCount = 0;
const QStringList watchers = sessionValue("Watchers").toStringList();
m_model->m_watchRoot->removeChildren();
for (const QString &exp : watchers)
watchExpression(exp.trimmed());
}
void WatchHandler::notifyUpdateStarted(const UpdateParameters &updateParameters)
@@ -2173,7 +2181,7 @@ void WatchHandler::watchExpression(const QString &exp, const QString &name, bool
m_model->m_engine->updateWatchData(item->iname);
}
updateLocalsWindow();
Internal::raiseWatchersWindow();
m_engine->raiseWatchersWindow();
}
void WatchHandler::updateWatchExpression(WatchItem *item, const QString &newExp)
@@ -2358,7 +2366,7 @@ void WatchHandler::updateLocalsWindow()
{
// Force show/hide of return view.
bool showReturn = m_model->m_returnRoot->childCount() != 0;
Internal::updateLocalsWindow(showReturn);
m_engine->updateLocalsWindow(showReturn);
}
QStringList WatchHandler::watchedExpressions()
@@ -2382,6 +2390,11 @@ void WatchHandler::saveSessionData()
}
void WatchHandler::loadSessionData()
{
// Handled by loadSesseionDataForEngine.
}
void WatchHandler::loadSessionDataForEngine()
{
loadFormats();
theWatcherNames.clear();

View File

@@ -76,8 +76,10 @@ public:
WatchItem *findItem(const QString &iname) const;
const WatchItem *findCppLocalVariable(const QString &name) const;
void loadSessionData();
void saveSessionData();
void loadSessionDataForEngine();
static void loadSessionData();
static void saveSessionData();
bool isExpandedIName(const QString &iname) const;
QSet<QString> expandedINames() const;
@@ -120,6 +122,7 @@ public:
void recordTypeInfo(const GdbMi &typeInfo);
private:
DebuggerEngine * const m_engine; // Not owned
WatchModel *m_model; // Owned.
};

View File

@@ -230,13 +230,13 @@ QmlProfilerTool::QmlProfilerTool()
QObject::connect(d->m_startAction, &QAction::triggered, this, &QmlProfilerTool::profileStartupProject);
Utils::Perspective *perspective = d->m_viewContainer->perspective();
perspective->addToolbarAction(d->m_startAction);
perspective->addToolbarAction(d->m_stopAction);
perspective->addToolbarWidget(d->m_recordButton);
perspective->addToolbarWidget(d->m_clearButton);
perspective->addToolbarWidget(d->m_searchButton);
perspective->addToolbarWidget(d->m_displayFeaturesButton);
perspective->addToolbarWidget(d->m_timeLabel);
perspective->addToolBarAction(d->m_startAction);
perspective->addToolBarAction(d->m_stopAction);
perspective->addToolBarWidget(d->m_recordButton);
perspective->addToolBarWidget(d->m_clearButton);
perspective->addToolBarWidget(d->m_searchButton);
perspective->addToolBarWidget(d->m_displayFeaturesButton);
perspective->addToolBarWidget(d->m_timeLabel);
connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::updateRunActions,
this, &QmlProfilerTool::updateRunActions);

View File

@@ -82,17 +82,17 @@ QmlProfilerViewManager::QmlProfilerViewManager(QObject *parent,
m_flameGraphView = new FlameGraphView(m_profilerModelManager);
prepareEventsView(m_flameGraphView);
QWidget *anchor = nullptr;
QWidget *anchorDock = nullptr;
if (m_traceView->isUsable()) {
anchor = m_traceView;
anchorDock = m_traceView;
m_perspective->addWindow(m_traceView, Perspective::SplitVertical, nullptr);
m_perspective->addWindow(m_flameGraphView, Perspective::AddToTab, anchor);
m_perspective->addWindow(m_flameGraphView, Perspective::AddToTab, m_traceView);
} else {
anchor = m_flameGraphView;
anchorDock = m_flameGraphView;
m_perspective->addWindow(m_flameGraphView, Perspective::SplitVertical, nullptr);
}
m_perspective->addWindow(m_statisticsView, Perspective::AddToTab, anchor);
m_perspective->addWindow(anchor, Perspective::Raise, nullptr);
m_perspective->addWindow(m_statisticsView, Perspective::AddToTab, anchorDock);
m_perspective->addWindow(anchorDock, Perspective::Raise, nullptr);
Debugger::registerPerspective(m_perspective);
}
@@ -102,6 +102,7 @@ QmlProfilerViewManager::~QmlProfilerViewManager()
delete m_traceView;
delete m_flameGraphView;
delete m_statisticsView;
delete m_perspective;
}
void QmlProfilerViewManager::clear()

View File

@@ -420,17 +420,17 @@ CallgrindTool::CallgrindTool()
updateEventCombo();
auto perspective = new Perspective(CallgrindPerspectiveId, tr("Callgrind"));
perspective->addToolbarAction(m_startAction);
perspective->addToolbarAction(m_stopAction);
perspective->addToolbarAction(m_loadExternalLogFile);
perspective->addToolbarAction(m_dumpAction);
perspective->addToolbarAction(m_resetAction);
perspective->addToolbarAction(m_pauseAction);
perspective->addToolbarAction(m_discardAction);
perspective->addToolbarAction(m_goBack);
perspective->addToolbarAction(m_goNext);
perspective->addToolBarAction(m_startAction);
perspective->addToolBarAction(m_stopAction);
perspective->addToolBarAction(m_loadExternalLogFile);
perspective->addToolBarAction(m_dumpAction);
perspective->addToolBarAction(m_resetAction);
perspective->addToolBarAction(m_pauseAction);
perspective->addToolBarAction(m_discardAction);
perspective->addToolBarAction(m_goBack);
perspective->addToolBarAction(m_goNext);
perspective->addToolbarSeparator();
perspective->addToolbarWidget(m_eventCombo);
perspective->addToolBarWidget(m_eventCombo);
// Cost formatting
{
@@ -463,7 +463,7 @@ CallgrindTool::CallgrindTool()
button->setPopupMode(QToolButton::InstantPopup);
button->setText(QLatin1String("$"));
button->setToolTip(tr("Cost Format"));
perspective->addToolbarWidget(button);
perspective->addToolBarWidget(button);
}
ValgrindGlobalSettings *settings = ValgrindPlugin::globalSettings();
@@ -500,10 +500,10 @@ CallgrindTool::CallgrindTool()
setCostFormat(settings->costFormat());
enableCycleDetection(settings->detectCycles());
perspective->addToolbarAction(m_cycleDetection);
perspective->addToolbarAction(m_shortenTemplates);
perspective->addToolbarAction(m_filterProjectCosts);
perspective->addToolbarWidget(m_searchFilter);
perspective->addToolBarAction(m_cycleDetection);
perspective->addToolBarAction(m_shortenTemplates);
perspective->addToolBarAction(m_filterProjectCosts);
perspective->addToolBarWidget(m_searchFilter);
perspective->addWindow(m_flatView, Perspective::SplitVertical, nullptr);
perspective->addWindow(m_calleesView, Perspective::SplitVertical, nullptr);

View File

@@ -683,13 +683,13 @@ MemcheckTool::MemcheckTool()
ProjectExplorerPlugin::startRunControl(rc);
});
perspective->addToolbarAction(m_startAction);
perspective->addToolBarAction(m_startAction);
//toolbar.addAction(m_startWithGdbAction);
perspective->addToolbarAction(m_stopAction);
perspective->addToolbarAction(m_loadExternalLogFile);
perspective->addToolbarAction(m_goBack);
perspective->addToolbarAction(m_goNext);
perspective->addToolbarWidget(filterButton);
perspective->addToolBarAction(m_stopAction);
perspective->addToolBarAction(m_loadExternalLogFile);
perspective->addToolBarAction(m_goBack);
perspective->addToolBarAction(m_goNext);
perspective->addToolBarWidget(filterButton);
Debugger::registerPerspective(perspective);
updateFromSettings();