544 lines
18 KiB
C++
544 lines
18 KiB
C++
/*
|
|
* Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com)
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
#ifndef DEBUGGER_H
|
|
#define DEBUGGER_H
|
|
|
|
#include <QAbstractTableModel>
|
|
#include <QList>
|
|
#include <QList>
|
|
#include <QMap>
|
|
#include <QMutex>
|
|
#include <QObject>
|
|
#include <QProcess>
|
|
#include <QQueue>
|
|
#include <QQueue>
|
|
#include <QSemaphore>
|
|
#include <QSet>
|
|
#include <QThread>
|
|
#include <QTimer>
|
|
#include <memory>
|
|
#include "gdbmiresultparser.h"
|
|
|
|
enum class DebugCommandSource {
|
|
Console,
|
|
HeartBeat,
|
|
Other
|
|
};
|
|
|
|
struct DebugCommand{
|
|
QString command;
|
|
QString params;
|
|
DebugCommandSource source;
|
|
};
|
|
|
|
using PDebugCommand = std::shared_ptr<DebugCommand>;
|
|
struct WatchVar;
|
|
using PWatchVar = std::shared_ptr<WatchVar>;
|
|
struct WatchVar {
|
|
QString name;
|
|
QString expression;
|
|
bool hasMore;
|
|
QString value;
|
|
QString type;
|
|
int numChild;
|
|
QList<PWatchVar> children;
|
|
WatchVar * parent; //use raw point to prevent circular-reference
|
|
};
|
|
|
|
enum class BreakpointType {
|
|
Breakpoint,
|
|
Watchpoint,
|
|
ReadWatchpoint,
|
|
WriteWatchpoint
|
|
};
|
|
|
|
struct Breakpoint {
|
|
int number; // breakpoint number
|
|
QString type; // type of the breakpoint
|
|
QString catch_type;
|
|
int line;
|
|
QString filename;
|
|
QString condition;
|
|
bool enabled;
|
|
BreakpointType breakpointType;
|
|
};
|
|
|
|
using PBreakpoint = std::shared_ptr<Breakpoint>;
|
|
|
|
struct Trace {
|
|
QString funcname;
|
|
QString filename;
|
|
QString address;
|
|
int line;
|
|
int level;
|
|
};
|
|
|
|
using PTrace = std::shared_ptr<Trace>;
|
|
|
|
class RegisterModel: public QAbstractTableModel {
|
|
Q_OBJECT
|
|
public:
|
|
explicit RegisterModel(QObject* parent = nullptr);
|
|
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;
|
|
void updateNames(const QStringList& regNames);
|
|
void updateValues(const QHash<int,QString> registerValues);
|
|
void clear();
|
|
private:
|
|
QStringList mRegisterNames;
|
|
QHash<int,QString> mRegisterValues;
|
|
};
|
|
|
|
class BreakpointModel: public QAbstractTableModel {
|
|
Q_OBJECT
|
|
// QAbstractItemModel interface
|
|
public:
|
|
explicit BreakpointModel(QObject *parent = nullptr);
|
|
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;
|
|
void addBreakpoint(PBreakpoint p);
|
|
void clear();
|
|
void removeBreakpoint(int index);
|
|
PBreakpoint setBreakPointCondition(int index, const QString& condition);
|
|
const QList<PBreakpoint>& breakpoints() const;
|
|
PBreakpoint breakpoint(int index) const;
|
|
void save(const QString& filename);
|
|
void load(const QString& filename);
|
|
public slots:
|
|
void updateBreakpointNumber(const QString& filename, int line, int number);
|
|
void invalidateAllBreakpointNumbers(); // call this when gdb is stopped
|
|
void onFileDeleteLines(const QString& filename, int startLine, int count);
|
|
void onFileInsertLines(const QString& filename, int startLine, int count);
|
|
private:
|
|
QList<PBreakpoint> mList;
|
|
};
|
|
|
|
class BacktraceModel : public QAbstractTableModel {
|
|
Q_OBJECT
|
|
// QAbstractItemModel interface
|
|
public:
|
|
explicit BacktraceModel(QObject *parent = nullptr);
|
|
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;
|
|
void addTrace(PTrace p);
|
|
void clear();
|
|
void removeTrace(int index);
|
|
const QList<PTrace>& backtraces() const;
|
|
PTrace backtrace(int index) const;
|
|
private:
|
|
QList<PTrace> mList;
|
|
};
|
|
|
|
class WatchModel: public QAbstractItemModel {
|
|
Q_OBJECT
|
|
public:
|
|
explicit WatchModel(QObject *parent = nullptr);
|
|
QVariant data(const QModelIndex &index, int role) const override;
|
|
|
|
QModelIndex index(int row, int column,
|
|
const QModelIndex &parent = QModelIndex()) const override;
|
|
QModelIndex parent(const QModelIndex &index) const override;
|
|
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
|
void fetchMore(const QModelIndex &parent) override;
|
|
bool canFetchMore(const QModelIndex &parent) const override;
|
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
|
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
|
bool hasChildren(const QModelIndex &parent) const override;
|
|
void addWatchVar(PWatchVar watchVar);
|
|
void removeWatchVar(const QString& expression);
|
|
void removeWatchVar(const QModelIndex& index);
|
|
void clear();
|
|
const QList<PWatchVar>& watchVars();
|
|
PWatchVar findWatchVar(const QModelIndex& index);
|
|
PWatchVar findWatchVar(const QString& expr);
|
|
void resetAllVarInfos();
|
|
void clearAllVarInfos();
|
|
void beginUpdate();
|
|
void endUpdate();
|
|
void notifyUpdated(PWatchVar var);
|
|
void save(const QString& filename);
|
|
void load(const QString& filename);
|
|
signals:
|
|
void setWatchVarValue(const QString& name, const QString& value);
|
|
public slots:
|
|
void updateVarInfo(const QString& expression,
|
|
const QString& name,
|
|
int numChild,
|
|
const QString& value,
|
|
const QString& type,
|
|
bool hasMore);
|
|
void prepareVarChildren(const QString& parentName, int numChild, bool hasMore);
|
|
void addVarChild(const QString& parentName, const QString& name,
|
|
const QString& exp, int numChild,
|
|
const QString& value, const QString& type,
|
|
bool hasMore);
|
|
void updateVarValue(const QString& name, const QString& val,
|
|
const QString& inScope, bool typeChanged,
|
|
const QString& newType, int newNumChildren,
|
|
bool hasMore);
|
|
void updateAllHasMoreVars();
|
|
signals:
|
|
void fetchChildren(const QString& name);
|
|
private:
|
|
QModelIndex index(PWatchVar var) const;
|
|
QModelIndex index(WatchVar* pVar) const;
|
|
private:
|
|
QList<PWatchVar> mWatchVars;
|
|
QHash<QString,PWatchVar> mVarIndex;
|
|
int mUpdateCount;
|
|
|
|
// QAbstractItemModel interface
|
|
public:
|
|
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
|
|
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
|
};
|
|
|
|
struct MemoryLine {
|
|
uintptr_t startAddress;
|
|
QList<unsigned char> datas;
|
|
QSet<int> changedDatas;
|
|
};
|
|
|
|
using PMemoryLine = std::shared_ptr<MemoryLine>;
|
|
|
|
class MemoryModel: public QAbstractTableModel{
|
|
Q_OBJECT
|
|
public:
|
|
explicit MemoryModel(int dataPerLine,QObject* parent=nullptr);
|
|
|
|
void updateMemory(const QStringList& value);
|
|
qulonglong startAddress() const;
|
|
void reset();
|
|
// QAbstractItemModel interface
|
|
signals:
|
|
void setMemoryData(qlonglong address, unsigned char data);
|
|
public:
|
|
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;
|
|
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
|
|
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
|
|
|
private:
|
|
int mDataPerLine;
|
|
QList<PMemoryLine> mLines;
|
|
qulonglong mStartAddress;
|
|
};
|
|
|
|
|
|
class DebugReader;
|
|
class DebugTarget;
|
|
class Editor;
|
|
|
|
using PDebugReader = std::shared_ptr<DebugReader>;
|
|
|
|
class Debugger : public QObject
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
explicit Debugger(QObject *parent = nullptr);
|
|
~Debugger();
|
|
// Play/pause
|
|
bool start(int compilerSetIndex, const QString& inferior, const QStringList& binDirs);
|
|
void sendCommand(const QString& command, const QString& params,
|
|
DebugCommandSource source = DebugCommandSource::Other);
|
|
bool commandRunning();
|
|
bool inferiorRunning();
|
|
void interrupt();
|
|
|
|
//breakpoints
|
|
void addBreakpoint(int line, const Editor* editor);
|
|
void addBreakpoint(int line, const QString& filename);
|
|
void deleteBreakpoints(const QString& filename);
|
|
void deleteBreakpoints(const Editor* editor);
|
|
void deleteBreakpoints();
|
|
void removeBreakpoint(int line, const Editor* editor);
|
|
void removeBreakpoint(int line, const QString& filename);
|
|
void removeBreakpoint(int index);
|
|
PBreakpoint breakpointAt(int line, const QString& filename, int &index);
|
|
PBreakpoint breakpointAt(int line, const Editor* editor, int &index);
|
|
void setBreakPointCondition(int index, const QString& condition);
|
|
void sendAllBreakpointsToDebugger();
|
|
|
|
//watch vars
|
|
void addWatchVar(const QString& expression);
|
|
void modifyWatchVarExpression(const QString& oldExpr, const QString& newExpr);
|
|
|
|
void removeWatchVars(bool deleteparent);
|
|
void removeWatchVar(const QModelIndex& index);
|
|
void sendAllWatchVarsToDebugger();
|
|
PWatchVar findWatchVar(const QString& expression);
|
|
PWatchVar watchVarAt(const QModelIndex& index);
|
|
// void notifyWatchVarUpdated(PWatchVar var);
|
|
|
|
BacktraceModel* backtraceModel();
|
|
BreakpointModel* breakpointModel();
|
|
bool executing() const;
|
|
|
|
int leftPageIndexBackup() const;
|
|
void setLeftPageIndexBackup(int leftPageIndexBackup);
|
|
|
|
WatchModel *watchModel() const;
|
|
|
|
RegisterModel *registerModel() const;
|
|
|
|
MemoryModel *memoryModel() const;
|
|
|
|
bool forceUTF8() const;
|
|
void setForceUTF8(bool newForceUTF8);
|
|
|
|
signals:
|
|
void evalValueReady(const QString& s);
|
|
void memoryExamineReady(const QStringList& s);
|
|
void localsReady(const QStringList& s);
|
|
public slots:
|
|
void stop();
|
|
void refreshAll();
|
|
|
|
private:
|
|
void sendWatchCommand(PWatchVar var);
|
|
void sendRemoveWatchCommand(PWatchVar var);
|
|
void sendBreakpointCommand(PBreakpoint breakpoint);
|
|
void sendClearBreakpointCommand(int index);
|
|
void sendClearBreakpointCommand(PBreakpoint breakpoint);
|
|
|
|
private slots:
|
|
void syncFinishedParsing();
|
|
void setMemoryData(qulonglong address, unsigned char data);
|
|
void setWatchVarValue(const QString& name, const QString& value);
|
|
void updateMemory(const QStringList& value);
|
|
void updateEval(const QString& value);
|
|
void updateDisassembly(const QString& file, const QString& func,const QStringList& value);
|
|
void onChangeDebugConsoleLastline(const QString& text);
|
|
void cleanUpReader();
|
|
void updateRegisterNames(const QStringList& registerNames);
|
|
void updateRegisterValues(const QHash<int,QString>& values);
|
|
void refreshWatchVars();
|
|
void fetchVarChildren(const QString& varName);
|
|
private:
|
|
bool mExecuting;
|
|
bool mCommandChanged;
|
|
BreakpointModel *mBreakpointModel;
|
|
BacktraceModel *mBacktraceModel;
|
|
WatchModel *mWatchModel;
|
|
RegisterModel *mRegisterModel;
|
|
MemoryModel *mMemoryModel;
|
|
DebugReader *mReader;
|
|
DebugTarget *mTarget;
|
|
bool mForceUTF8;
|
|
int mLeftPageIndexBackup;
|
|
};
|
|
|
|
class DebugTarget: public QThread {
|
|
Q_OBJECT
|
|
public:
|
|
explicit DebugTarget(const QString& inferior,
|
|
const QString& GDBServer,
|
|
int port,
|
|
QObject *parent = nullptr);
|
|
void setInputFile(const QString& inputFile);
|
|
void stopDebug();
|
|
void waitStart();
|
|
const QStringList &binDirs() const;
|
|
void addBinDirs(const QStringList &binDirs);
|
|
void addBinDir(const QString &binDir);
|
|
signals:
|
|
void processError(QProcess::ProcessError error);
|
|
private:
|
|
QString mInferior;
|
|
QString mGDBServer;
|
|
int mPort;
|
|
bool mStop;
|
|
std::shared_ptr<QProcess> mProcess;
|
|
QSemaphore mStartSemaphore;
|
|
bool mErrorOccured;
|
|
QString mInputFile;
|
|
QStringList mBinDirs;
|
|
|
|
// QThread interface
|
|
protected:
|
|
void run() override;
|
|
};
|
|
|
|
class DebugReader : public QThread
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
explicit DebugReader(Debugger* debugger, QObject *parent = nullptr);
|
|
void postCommand(const QString &Command, const QString &Params, DebugCommandSource Source);
|
|
void registerInferiorStoppedCommand(const QString &Command, const QString &Params);
|
|
QString debuggerPath() const;
|
|
void setDebuggerPath(const QString &debuggerPath);
|
|
void stopDebug();
|
|
|
|
bool commandRunning();
|
|
void waitStart();
|
|
|
|
bool inferiorPaused() const;
|
|
|
|
bool processExited() const;
|
|
|
|
bool signalReceived() const;
|
|
|
|
const QStringList &consoleOutput() const;
|
|
|
|
int breakPointLine() const;
|
|
|
|
const QString &breakPointFile() const;
|
|
|
|
const PDebugCommand ¤tCmd() const;
|
|
|
|
bool updateCPUInfo() const;
|
|
|
|
bool updateLocals() const;
|
|
|
|
const QStringList &localsValue() const;
|
|
|
|
bool evalReady() const;
|
|
|
|
const QString &evalValue() const;
|
|
|
|
bool updateMemory() const;
|
|
|
|
const QStringList &memoryValue() const;
|
|
|
|
bool receivedSFWarning() const;
|
|
|
|
const QStringList &fullOutput() const;
|
|
|
|
bool inferiorRunning() const;
|
|
|
|
const QString &signalName() const;
|
|
|
|
const QString &signalMeaning() const;
|
|
|
|
const QStringList &binDirs() const;
|
|
void addBinDirs(const QStringList &binDirs);
|
|
void addBinDir(const QString &binDir);
|
|
|
|
signals:
|
|
void parseStarted();
|
|
void invalidateAllVars();
|
|
void parseFinished();
|
|
void writeToDebugFailed();
|
|
void processError(QProcess::ProcessError error);
|
|
void changeDebugConsoleLastLine(const QString& text);
|
|
void cmdStarted();
|
|
void cmdFinished();
|
|
|
|
void breakpointInfoGetted(const QString& filename, int line, int number);
|
|
void inferiorContinued();
|
|
void inferiorStopped(const QString& filename, int line, bool setFocus);
|
|
void localsUpdated(const QStringList& localsValue);
|
|
void evalUpdated(const QString& value);
|
|
void memoryUpdated(const QStringList& memoryValues);
|
|
void disassemblyUpdate(const QString& filename, const QString& funcName, const QStringList& result);
|
|
void registerNamesUpdated(const QStringList& registerNames);
|
|
void registerValuesUpdated(const QHash<int,QString>& values);
|
|
void varCreated(const QString& expression,
|
|
const QString& name,
|
|
int numChild,
|
|
const QString& value,
|
|
const QString& type,
|
|
bool hasMore);
|
|
void prepareVarChildren(const QString& parentName,int numChild, bool hasMore);
|
|
void addVarChild(const QString& parentName, const QString& name,
|
|
const QString& exp, int numChild,
|
|
const QString& value, const QString& type,
|
|
bool hasMore);
|
|
void varValueUpdated(const QString& name, const QString& val,
|
|
const QString& inScope, bool typeChanged,
|
|
const QString& newType, int newNumChildren,
|
|
bool hasMore);
|
|
void varsValueUpdated();
|
|
|
|
private:
|
|
void clearCmdQueue();
|
|
|
|
void runNextCmd();
|
|
QStringList tokenize(const QString& s);
|
|
|
|
bool outputTerminated(const QByteArray& text);
|
|
void handleBreakpoint(const GDBMIResultParser::ParseObject& breakpoint);
|
|
void handleStack(const QList<GDBMIResultParser::ParseValue> & stack);
|
|
void handleLocalVariables(const QList<GDBMIResultParser::ParseValue> & variables);
|
|
void handleEvaluation(const QString& value);
|
|
void handleMemory(const QList<GDBMIResultParser::ParseValue> & rows);
|
|
void handleRegisterNames(const QList<GDBMIResultParser::ParseValue> & names);
|
|
void handleRegisterValue(const QList<GDBMIResultParser::ParseValue> & values);
|
|
void handleCreateVar(const GDBMIResultParser::ParseObject& multiVars);
|
|
void handleListVarChildren(const GDBMIResultParser::ParseObject& multiVars);
|
|
void handleUpdateVarValue(const QList<GDBMIResultParser::ParseValue> &changes);
|
|
void processConsoleOutput(const QByteArray& line);
|
|
void processResult(const QByteArray& result);
|
|
void processExecAsyncRecord(const QByteArray& line);
|
|
void processError(const QByteArray& errorLine);
|
|
void processResultRecord(const QByteArray& line);
|
|
void processDebugOutput(const QByteArray& debugOutput);
|
|
void runInferiorStoppedHook();
|
|
QByteArray removeToken(const QByteArray& line);
|
|
private slots:
|
|
void asyncUpdate();
|
|
private:
|
|
Debugger *mDebugger;
|
|
QString mDebuggerPath;
|
|
QMutex mCmdQueueMutex;
|
|
QSemaphore mStartSemaphore;
|
|
QQueue<PDebugCommand> mCmdQueue;
|
|
bool mErrorOccured;
|
|
bool mAsyncUpdated;
|
|
//fOnInvalidateAllVars: TInvalidateAllVarsEvent;
|
|
bool mCmdRunning;
|
|
PDebugCommand mCurrentCmd;
|
|
std::shared_ptr<QProcess> mProcess;
|
|
QStringList mBinDirs;
|
|
|
|
//fWatchView: TTreeView;
|
|
|
|
QString mSignalName;
|
|
QString mSignalMeaning;
|
|
|
|
//
|
|
QList<PDebugCommand> mInferiorStoppedHookCommands;
|
|
bool mInferiorRunning;
|
|
bool mProcessExited;
|
|
|
|
bool mSignalReceived;
|
|
bool mUpdateCPUInfo;
|
|
bool mReceivedSFWarning;
|
|
|
|
int mCurrentLine;
|
|
int mCurrentAddress;
|
|
QString mCurrentFunc;
|
|
QString mCurrentFile;
|
|
QStringList mConsoleOutput;
|
|
QStringList mFullOutput;
|
|
bool mStop;
|
|
// QThread interface
|
|
protected:
|
|
void run() override;
|
|
};
|
|
|
|
#endif // DEBUGGER_H
|