1/*
2 * Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com)
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17#ifndef DEBUGGER_H
18#define DEBUGGER_H
19
20#include <QAbstractTableModel>
21#include <QList>
22#include <QList>
23#include <QMap>
24#include <QMutex>
25#include <QObject>
26#include <QProcess>
27#include <QQueue>
28#include <QQueue>
29#include <QSemaphore>
30#include <QSet>
31#include <QThread>
32#include <QTimer>
33#include <memory>
34#include "gdbmiresultparser.h"
35
36enum class DebugCommandSource {
37 Console,
38 HeartBeat,
39 Other
40};
41
42struct DebugCommand{
43 QString command;
44 QString params;
45 DebugCommandSource source;
46};
47
48using PDebugCommand = std::shared_ptr<DebugCommand>;
49struct WatchVar;
50using PWatchVar = std::shared_ptr<WatchVar>;
51struct WatchVar {
52 QString name;
53 QString expression;
54 bool hasMore;
55 QString value;
56 QString type;
57 int numChild;
58 QList<PWatchVar> children;
59 WatchVar * parent; //use raw point to prevent circular-reference
60};
61
62enum class BreakpointType {
63 Breakpoint,
64 Watchpoint,
65 ReadWatchpoint,
66 WriteWatchpoint
67};
68
69struct Breakpoint {
70 int number; // breakpoint number
71 QString type; // type of the breakpoint
72 QString catch_type;
73 int line;
74 QString filename;
75 QString condition;
76 bool enabled;
77 BreakpointType breakpointType;
78};
79
80using PBreakpoint = std::shared_ptr<Breakpoint>;
81
82struct Trace {
83 QString funcname;
84 QString filename;
85 QString address;
86 int line;
87 int level;
88};
89
90using PTrace = std::shared_ptr<Trace>;
91
92class RegisterModel: public QAbstractTableModel {
93 Q_OBJECT
94public:
95 explicit RegisterModel(QObject* parent = nullptr);
96 int rowCount(const QModelIndex &parent) const override;
97 int columnCount(const QModelIndex &parent) const override;
98 QVariant data(const QModelIndex &index, int role) const override;
99 QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
100 void updateNames(const QStringList& regNames);
101 void updateValues(const QHash<int,QString> registerValues);
102 void clear();
103private:
104 QStringList mRegisterNames;
105 QHash<int,QString> mRegisterValues;
106};
107
108class BreakpointModel: public QAbstractTableModel {
109 Q_OBJECT
110 // QAbstractItemModel interface
111public:
112 explicit BreakpointModel(QObject *parent = nullptr);
113 int rowCount(const QModelIndex &parent) const override;
114 int columnCount(const QModelIndex &parent) const override;
115 QVariant data(const QModelIndex &index, int role) const override;
116 QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
117 void addBreakpoint(PBreakpoint p);
118 void clear();
119 void removeBreakpoint(int index);
120 PBreakpoint setBreakPointCondition(int index, const QString& condition);
121 const QList<PBreakpoint>& breakpoints() const;
122 PBreakpoint breakpoint(int index) const;
123 void save(const QString& filename);
124 void load(const QString& filename);
125public slots:
126 void updateBreakpointNumber(const QString& filename, int line, int number);
127 void invalidateAllBreakpointNumbers(); // call this when gdb is stopped
128 void onFileDeleteLines(const QString& filename, int startLine, int count);
129 void onFileInsertLines(const QString& filename, int startLine, int count);
130private:
131 QList<PBreakpoint> mList;
132};
133
134class BacktraceModel : public QAbstractTableModel {
135 Q_OBJECT
136 // QAbstractItemModel interface
137public:
138 explicit BacktraceModel(QObject *parent = nullptr);
139 int rowCount(const QModelIndex &parent) const override;
140 int columnCount(const QModelIndex &parent) const override;
141 QVariant data(const QModelIndex &index, int role) const override;
142 QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
143 void addTrace(PTrace p);
144 void clear();
145 void removeTrace(int index);
146 const QList<PTrace>& backtraces() const;
147 PTrace backtrace(int index) const;
148private:
149 QList<PTrace> mList;
150};
151
152class WatchModel: public QAbstractItemModel {
153 Q_OBJECT
154public:
155 explicit WatchModel(QObject *parent = nullptr);
156 QVariant data(const QModelIndex &index, int role) const override;
157
158 QModelIndex index(int row, int column,
159 const QModelIndex &parent = QModelIndex()) const override;
160 QModelIndex parent(const QModelIndex &index) const override;
161 QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
162 void fetchMore(const QModelIndex &parent) override;
163 bool canFetchMore(const QModelIndex &parent) const override;
164 int rowCount(const QModelIndex &parent = QModelIndex()) const override;
165 int columnCount(const QModelIndex &parent = QModelIndex()) const override;
166 bool hasChildren(const QModelIndex &parent) const override;
167 void addWatchVar(PWatchVar watchVar);
168 void removeWatchVar(const QString& expression);
169 void removeWatchVar(const QModelIndex& index);
170 void clear();
171 const QList<PWatchVar>& watchVars();
172 PWatchVar findWatchVar(const QModelIndex& index);
173 PWatchVar findWatchVar(const QString& expr);
174 void resetAllVarInfos();
175 void clearAllVarInfos();
176 void beginUpdate();
177 void endUpdate();
178 void notifyUpdated(PWatchVar var);
179 void save(const QString& filename);
180 void load(const QString& filename);
181signals:
182 void setWatchVarValue(const QString& name, const QString& value);
183public slots:
184 void updateVarInfo(const QString& expression,
185 const QString& name,
186 int numChild,
187 const QString& value,
188 const QString& type,
189 bool hasMore);
190 void prepareVarChildren(const QString& parentName, int numChild, bool hasMore);
191 void addVarChild(const QString& parentName, const QString& name,
192 const QString& exp, int numChild,
193 const QString& value, const QString& type,
194 bool hasMore);
195 void updateVarValue(const QString& name, const QString& val,
196 const QString& inScope, bool typeChanged,
197 const QString& newType, int newNumChildren,
198 bool hasMore);
199 void updateAllHasMoreVars();
200signals:
201 void fetchChildren(const QString& name);
202private:
203 QModelIndex index(PWatchVar var) const;
204 QModelIndex index(WatchVar* pVar) const;
205private:
206 QList<PWatchVar> mWatchVars;
207 QHash<QString,PWatchVar> mVarIndex;
208 int mUpdateCount;
209
210 // QAbstractItemModel interface
211public:
212 bool setData(const QModelIndex &index, const QVariant &value, int role) override;
213 Qt::ItemFlags flags(const QModelIndex &index) const override;
214};
215
216struct MemoryLine {
217 uintptr_t startAddress;
218 QList<unsigned char> datas;
219 QSet<int> changedDatas;
220};
221
222using PMemoryLine = std::shared_ptr<MemoryLine>;
223
224class MemoryModel: public QAbstractTableModel{
225 Q_OBJECT
226public:
227 explicit MemoryModel(int dataPerLine,QObject* parent=nullptr);
228
229 void updateMemory(const QStringList& value);
230 qulonglong startAddress() const;
231 void reset();
232 // QAbstractItemModel interface
233signals:
234 void setMemoryData(qlonglong address, unsigned char data);
235public:
236 int rowCount(const QModelIndex &parent) const override;
237 int columnCount(const QModelIndex &parent) const override;
238 QVariant data(const QModelIndex &index, int role) const override;
239 QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
240 bool setData(const QModelIndex &index, const QVariant &value, int role) override;
241 Qt::ItemFlags flags(const QModelIndex &index) const override;
242
243private:
244 int mDataPerLine;
245 QList<PMemoryLine> mLines;
246 qulonglong mStartAddress;
247};
248
249
250class DebugReader;
251class DebugTarget;
252class Editor;
253
254using PDebugReader = std::shared_ptr<DebugReader>;
255
256class Debugger : public QObject
257{
258 Q_OBJECT
259public:
260 explicit Debugger(QObject *parent = nullptr);
261 // Play/pause
262 bool start(int compilerSetIndex, const QString& inferior, const QStringList& binDirs);
263 void sendCommand(const QString& command, const QString& params,
264 DebugCommandSource source = DebugCommandSource::Other);
265 bool commandRunning();
266 bool inferiorRunning();
267 void interrupt();
268
269 //breakpoints
270 void addBreakpoint(int line, const Editor* editor);
271 void addBreakpoint(int line, const QString& filename);
272 void deleteBreakpoints(const QString& filename);
273 void deleteBreakpoints(const Editor* editor);
274 void deleteBreakpoints();
275 void removeBreakpoint(int line, const Editor* editor);
276 void removeBreakpoint(int line, const QString& filename);
277 void removeBreakpoint(int index);
278 PBreakpoint breakpointAt(int line, const QString& filename, int &index);
279 PBreakpoint breakpointAt(int line, const Editor* editor, int &index);
280 void setBreakPointCondition(int index, const QString& condition);
281 void sendAllBreakpointsToDebugger();
282
283 //watch vars
284 void addWatchVar(const QString& expression);
285 void modifyWatchVarExpression(const QString& oldExpr, const QString& newExpr);
286
287 void removeWatchVars(bool deleteparent);
288 void removeWatchVar(const QModelIndex& index);
289 void sendAllWatchVarsToDebugger();
290 PWatchVar findWatchVar(const QString& expression);
291 PWatchVar watchVarAt(const QModelIndex& index);
292// void notifyWatchVarUpdated(PWatchVar var);
293
294 BacktraceModel* backtraceModel();
295 BreakpointModel* breakpointModel();
296 bool executing() const;
297
298 int leftPageIndexBackup() const;
299 void setLeftPageIndexBackup(int leftPageIndexBackup);
300
301 WatchModel *watchModel() const;
302
303 RegisterModel *registerModel() const;
304
305 MemoryModel *memoryModel() const;
306
307 bool forceUTF8() const;
308 void setForceUTF8(bool newForceUTF8);
309
310signals:
311 void evalValueReady(const QString& s);
312 void memoryExamineReady(const QStringList& s);
313 void localsReady(const QStringList& s);
314public slots:
315 void stop();
316 void refreshAll();
317
318private:
319 void sendWatchCommand(PWatchVar var);
320 void sendRemoveWatchCommand(PWatchVar var);
321 void sendBreakpointCommand(PBreakpoint breakpoint);
322 void sendClearBreakpointCommand(int index);
323 void sendClearBreakpointCommand(PBreakpoint breakpoint);
324
325private slots:
326 void syncFinishedParsing();
327 void setMemoryData(qulonglong address, unsigned char data);
328 void setWatchVarValue(const QString& name, const QString& value);
329 void updateMemory(const QStringList& value);
330 void updateEval(const QString& value);
331 void updateDisassembly(const QString& file, const QString& func,const QStringList& value);
332 void onChangeDebugConsoleLastline(const QString& text);
333 void cleanUpReader();
334 void updateRegisterNames(const QStringList& registerNames);
335 void updateRegisterValues(const QHash<int,QString>& values);
336 void refreshWatchVars();
337 void fetchVarChildren(const QString& varName);
338private:
339 bool mExecuting;
340 bool mCommandChanged;
341 BreakpointModel *mBreakpointModel;
342 BacktraceModel *mBacktraceModel;
343 WatchModel *mWatchModel;
344 RegisterModel *mRegisterModel;
345 MemoryModel *mMemoryModel;
346 DebugReader *mReader;
347 DebugTarget *mTarget;
348 bool mForceUTF8;
349 int mLeftPageIndexBackup;
350};
351
352class DebugTarget: public QThread {
353 Q_OBJECT
354public:
355 explicit DebugTarget(const QString& inferior,
356 const QString& GDBServer,
357 int port,
358 QObject *parent = nullptr);
359 void setInputFile(const QString& inputFile);
360 void stopDebug();
361 void waitStart();
362 const QStringList &binDirs() const;
363 void addBinDirs(const QStringList &binDirs);
364 void addBinDir(const QString &binDir);
365signals:
366 void processError(QProcess::ProcessError error);
367private:
368 QString mInferior;
369 QString mGDBServer;
370 int mPort;
371 bool mStop;
372 std::shared_ptr<QProcess> mProcess;
373 QSemaphore mStartSemaphore;
374 bool mErrorOccured;
375 QString mInputFile;
376 QStringList mBinDirs;
377
378 // QThread interface
379protected:
380 void run() override;
381};
382
383class DebugReader : public QThread
384{
385 Q_OBJECT
386public:
387 explicit DebugReader(Debugger* debugger, QObject *parent = nullptr);
388 void postCommand(const QString &Command, const QString &Params, DebugCommandSource Source);
389 void registerInferiorStoppedCommand(const QString &Command, const QString &Params);
390 QString debuggerPath() const;
391 void setDebuggerPath(const QString &debuggerPath);
392 void stopDebug();
393
394 bool commandRunning();
395 void waitStart();
396
397 bool inferiorPaused() const;
398
399 bool processExited() const;
400
401 bool signalReceived() const;
402
403 const QStringList &consoleOutput() const;
404
405 int breakPointLine() const;
406
407 const QString &breakPointFile() const;
408
409 const PDebugCommand &currentCmd() const;
410
411 bool updateCPUInfo() const;
412
413 bool updateLocals() const;
414
415 const QStringList &localsValue() const;
416
417 bool evalReady() const;
418
419 const QString &evalValue() const;
420
421 bool updateMemory() const;
422
423 const QStringList &memoryValue() const;
424
425 bool receivedSFWarning() const;
426
427 const QStringList &fullOutput() const;
428
429 bool inferiorRunning() const;
430
431 const QString &signalName() const;
432
433 const QString &signalMeaning() const;
434
435 const QStringList &binDirs() const;
436 void addBinDirs(const QStringList &binDirs);
437 void addBinDir(const QString &binDir);
438
439signals:
440 void parseStarted();
441 void invalidateAllVars();
442 void parseFinished();
443 void writeToDebugFailed();
444 void processError(QProcess::ProcessError error);
445 void changeDebugConsoleLastLine(const QString& text);
446 void cmdStarted();
447 void cmdFinished();
448
449 void breakpointInfoGetted(const QString& filename, int line, int number);
450 void inferiorContinued();
451 void inferiorStopped(const QString& filename, int line, bool setFocus);
452 void localsUpdated(const QStringList& localsValue);
453 void evalUpdated(const QString& value);
454 void memoryUpdated(const QStringList& memoryValues);
455 void disassemblyUpdate(const QString& filename, const QString& funcName, const QStringList& result);
456 void registerNamesUpdated(const QStringList& registerNames);
457 void registerValuesUpdated(const QHash<int,QString>& values);
458 void varCreated(const QString& expression,
459 const QString& name,
460 int numChild,
461 const QString& value,
462 const QString& type,
463 bool hasMore);
464 void prepareVarChildren(const QString& parentName,int numChild, bool hasMore);
465 void addVarChild(const QString& parentName, const QString& name,
466 const QString& exp, int numChild,
467 const QString& value, const QString& type,
468 bool hasMore);
469 void varValueUpdated(const QString& name, const QString& val,
470 const QString& inScope, bool typeChanged,
471 const QString& newType, int newNumChildren,
472 bool hasMore);
473 void varsValueUpdated();
474
475private:
476 void clearCmdQueue();
477
478 void runNextCmd();
479 QStringList tokenize(const QString& s);
480
481 bool outputTerminated(const QByteArray& text);
482 void handleBreakpoint(const GDBMIResultParser::ParseObject& breakpoint);
483 void handleStack(const QList<GDBMIResultParser::ParseValue> & stack);
484 void handleLocalVariables(const QList<GDBMIResultParser::ParseValue> & variables);
485 void handleEvaluation(const QString& value);
486 void handleMemory(const QList<GDBMIResultParser::ParseValue> & rows);
487 void handleRegisterNames(const QList<GDBMIResultParser::ParseValue> & names);
488 void handleRegisterValue(const QList<GDBMIResultParser::ParseValue> & values);
489 void handleCreateVar(const GDBMIResultParser::ParseObject& multiVars);
490 void handleListVarChildren(const GDBMIResultParser::ParseObject& multiVars);
491 void handleUpdateVarValue(const QList<GDBMIResultParser::ParseValue> &changes);
492 void processConsoleOutput(const QByteArray& line);
493 void processResult(const QByteArray& result);
494 void processExecAsyncRecord(const QByteArray& line);
495 void processError(const QByteArray& errorLine);
496 void processResultRecord(const QByteArray& line);
497 void processDebugOutput(const QByteArray& debugOutput);
498 void runInferiorStoppedHook();
499 QByteArray removeToken(const QByteArray& line);
500private slots:
501 void asyncUpdate();
502private:
503 Debugger *mDebugger;
504 QString mDebuggerPath;
505 QMutex mCmdQueueMutex;
506 QSemaphore mStartSemaphore;
507 QQueue<PDebugCommand> mCmdQueue;
508 bool mErrorOccured;
509 bool mAsyncUpdated;
510 //fOnInvalidateAllVars: TInvalidateAllVarsEvent;
511 bool mCmdRunning;
512 PDebugCommand mCurrentCmd;
513 std::shared_ptr<QProcess> mProcess;
514 QStringList mBinDirs;
515
516 //fWatchView: TTreeView;
517
518 QString mSignalName;
519 QString mSignalMeaning;
520
521 //
522 QList<PDebugCommand> mInferiorStoppedHookCommands;
523 bool mInferiorRunning;
524 bool mProcessExited;
525
526 bool mSignalReceived;
527 bool mUpdateCPUInfo;
528 bool mReceivedSFWarning;
529
530 int mCurrentLine;
531 int mCurrentAddress;
532 QString mCurrentFunc;
533 QString mCurrentFile;
534 QStringList mConsoleOutput;
535 QStringList mFullOutput;
536 bool mStop;
537 // QThread interface
538protected:
539 void run() override;
540};
541
542#endif // DEBUGGER_H
543