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 | |
36 | enum class DebugCommandSource { |
37 | Console, |
38 | HeartBeat, |
39 | Other |
40 | }; |
41 | |
42 | struct DebugCommand{ |
43 | QString command; |
44 | QString params; |
45 | DebugCommandSource source; |
46 | }; |
47 | |
48 | using PDebugCommand = std::shared_ptr<DebugCommand>; |
49 | struct WatchVar; |
50 | using PWatchVar = std::shared_ptr<WatchVar>; |
51 | struct 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 | |
62 | enum class BreakpointType { |
63 | Breakpoint, |
64 | Watchpoint, |
65 | ReadWatchpoint, |
66 | WriteWatchpoint |
67 | }; |
68 | |
69 | struct 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 | |
80 | using PBreakpoint = std::shared_ptr<Breakpoint>; |
81 | |
82 | struct Trace { |
83 | QString funcname; |
84 | QString filename; |
85 | QString address; |
86 | int line; |
87 | int level; |
88 | }; |
89 | |
90 | using PTrace = std::shared_ptr<Trace>; |
91 | |
92 | class RegisterModel: public QAbstractTableModel { |
93 | Q_OBJECT |
94 | public: |
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 (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(); |
103 | private: |
104 | QStringList mRegisterNames; |
105 | QHash<int,QString> mRegisterValues; |
106 | }; |
107 | |
108 | class BreakpointModel: public QAbstractTableModel { |
109 | Q_OBJECT |
110 | // QAbstractItemModel interface |
111 | public: |
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 (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); |
125 | public 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); |
130 | private: |
131 | QList<PBreakpoint> mList; |
132 | }; |
133 | |
134 | class BacktraceModel : public QAbstractTableModel { |
135 | Q_OBJECT |
136 | // QAbstractItemModel interface |
137 | public: |
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 (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; |
148 | private: |
149 | QList<PTrace> mList; |
150 | }; |
151 | |
152 | class WatchModel: public QAbstractItemModel { |
153 | Q_OBJECT |
154 | public: |
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 (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); |
181 | signals: |
182 | void setWatchVarValue(const QString& name, const QString& value); |
183 | public 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(); |
200 | signals: |
201 | void fetchChildren(const QString& name); |
202 | private: |
203 | QModelIndex index(PWatchVar var) const; |
204 | QModelIndex index(WatchVar* pVar) const; |
205 | private: |
206 | QList<PWatchVar> mWatchVars; |
207 | QHash<QString,PWatchVar> mVarIndex; |
208 | int mUpdateCount; |
209 | |
210 | // QAbstractItemModel interface |
211 | public: |
212 | bool setData(const QModelIndex &index, const QVariant &value, int role) override; |
213 | Qt::ItemFlags flags(const QModelIndex &index) const override; |
214 | }; |
215 | |
216 | struct MemoryLine { |
217 | uintptr_t startAddress; |
218 | QList<unsigned char> datas; |
219 | QSet<int> changedDatas; |
220 | }; |
221 | |
222 | using PMemoryLine = std::shared_ptr<MemoryLine>; |
223 | |
224 | class MemoryModel: public QAbstractTableModel{ |
225 | Q_OBJECT |
226 | public: |
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 |
233 | signals: |
234 | void setMemoryData(qlonglong address, unsigned char data); |
235 | public: |
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 (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 | |
243 | private: |
244 | int mDataPerLine; |
245 | QList<PMemoryLine> mLines; |
246 | qulonglong mStartAddress; |
247 | }; |
248 | |
249 | |
250 | class DebugReader; |
251 | class DebugTarget; |
252 | class Editor; |
253 | |
254 | using PDebugReader = std::shared_ptr<DebugReader>; |
255 | |
256 | class Debugger : public QObject |
257 | { |
258 | Q_OBJECT |
259 | public: |
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 | |
310 | signals: |
311 | void evalValueReady(const QString& s); |
312 | void memoryExamineReady(const QStringList& s); |
313 | void localsReady(const QStringList& s); |
314 | public slots: |
315 | void stop(); |
316 | void refreshAll(); |
317 | |
318 | private: |
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 | |
325 | private 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); |
338 | private: |
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 | |
352 | class DebugTarget: public QThread { |
353 | Q_OBJECT |
354 | public: |
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); |
365 | signals: |
366 | void processError(QProcess::ProcessError error); |
367 | private: |
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 |
379 | protected: |
380 | void run() override; |
381 | }; |
382 | |
383 | class DebugReader : public QThread |
384 | { |
385 | Q_OBJECT |
386 | public: |
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 ¤tCmd() 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 | |
439 | signals: |
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 | |
475 | private: |
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); |
500 | private slots: |
501 | void asyncUpdate(); |
502 | private: |
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 |
538 | protected: |
539 | void run() override; |
540 | }; |
541 | |
542 | #endif // DEBUGGER_H |
543 | |