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#include "debugger.h"
18#include "utils.h"
19#include "mainwindow.h"
20#include "editor.h"
21#include "settings.h"
22#include "widgets/cpudialog.h"
23#include "systemconsts.h"
24
25#include <QFile>
26#include <QFileInfo>
27#include <QMessageBox>
28#include <QPlainTextEdit>
29#include <QDebug>
30#include <QDir>
31#include <QJsonDocument>
32#include <QJsonArray>
33#include <QJsonObject>
34#include "widgets/signalmessagedialog.h"
35
36Debugger::Debugger(QObject *parent) : QObject(parent),
37 mForceUTF8(false)
38{
39 mBreakpointModel=new BreakpointModel(this);
40 mBacktraceModel=new BacktraceModel(this);
41 mWatchModel = new WatchModel(this);
42 mRegisterModel = new RegisterModel(this);
43 mMemoryModel = new MemoryModel(8,this);
44 connect(mMemoryModel,&MemoryModel::setMemoryData,
45 this, &Debugger::setMemoryData);
46 connect(mWatchModel, &WatchModel::setWatchVarValue,
47 this, &Debugger::setWatchVarValue);
48 mExecuting = false;
49 mReader = nullptr;
50 mTarget = nullptr;
51 mCommandChanged = false;
52 mLeftPageIndexBackup = -1;
53
54 connect(mWatchModel, &WatchModel::fetchChildren,
55 this, &Debugger::fetchVarChildren);
56}
57
58bool Debugger::start(int compilerSetIndex, const QString& inferior, const QStringList& binDirs)
59{
60
61 Settings::PCompilerSet compilerSet = pSettings->compilerSets().getSet(compilerSetIndex);
62 if (!compilerSet) {
63 compilerSet = pSettings->compilerSets().defaultSet();
64 }
65 if (!compilerSet) {
66 QMessageBox::critical(pMainWindow,
67 tr("No compiler set"),
68 tr("No compiler set is configured.")+tr("Can't start debugging."));
69 return false;
70 }
71 setForceUTF8(CompilerInfoManager::forceUTF8InDebugger(compilerSet->compilerType()));
72 mExecuting = true;
73 QString debuggerPath = compilerSet->debugger();
74 //QFile debuggerProgram(debuggerPath);
75// if (!isTextAllAscii(debuggerPath)) {
76// mExecuting = false;
77// QMessageBox::critical(pMainWindow,
78// tr("Debugger path error"),
79// tr("Debugger's path \"%1\" contains non-ascii characters.")
80// .arg(debuggerPath)
81// + "<br />"
82// + tr("This prevents it from executing."));
83// return false;
84// }
85 if (!fileExists(debuggerPath)) {
86 mExecuting = false;
87 QMessageBox::critical(pMainWindow,
88 tr("Debugger not exists"),
89 tr("Can''t find debugger in : \"%1\"").arg(debuggerPath));
90 return false;
91 }
92 if (pSettings->debugger().useGDBServer()) {
93 if (!isTextAllAscii(compilerSet->debugServer())) {
94 mExecuting = false;
95 QMessageBox::critical(pMainWindow,
96 tr("GDB Server path error"),
97 tr("GDB Server's path \"%1\" contains non-ascii characters.")
98 .arg(compilerSet->debugServer())
99 + "<br />"
100 + tr("This prevents it from executing."));
101 return false;
102 }
103 if (!fileExists(compilerSet->debugServer())) {
104 mExecuting = false;
105 QMessageBox::critical(pMainWindow,
106 tr("GDB Server not exists"),
107 tr("Can''t find gdb server in : \"%1\"").arg(compilerSet->debugServer()));
108 return false;
109 }
110
111 }
112 mMemoryModel->reset();
113 mWatchModel->resetAllVarInfos();
114 if (pSettings->debugger().useGDBServer()) {
115 mTarget = new DebugTarget(inferior,compilerSet->debugServer(),pSettings->debugger().GDBServerPort());
116 if (pSettings->executor().redirectInput())
117 mTarget->setInputFile(pSettings->executor().inputFilename());
118 connect(mTarget, &QThread::finished,[this](){
119 if (mExecuting) {
120 stop();
121 }
122 mTarget->deleteLater();
123 mTarget = nullptr;
124 });
125 mTarget->addBinDirs(binDirs);
126 mTarget->addBinDir(pSettings->dirs().appDir());
127 mTarget->start();
128 mTarget->waitStart();
129 }
130 mReader = new DebugReader(this);
131 mReader->addBinDirs(binDirs);
132 mReader->addBinDir(pSettings->dirs().appDir());
133 mReader->setDebuggerPath(debuggerPath);
134 connect(mReader, &QThread::finished,this,&Debugger::cleanUpReader);
135 connect(mReader, &QThread::finished,mMemoryModel,&MemoryModel::reset);
136 connect(mReader, &DebugReader::parseFinished,this,&Debugger::syncFinishedParsing,Qt::BlockingQueuedConnection);
137 connect(mReader, &DebugReader::changeDebugConsoleLastLine,this,&Debugger::onChangeDebugConsoleLastline);
138 connect(mReader, &DebugReader::cmdStarted,pMainWindow, &MainWindow::disableDebugActions);
139 connect(mReader, &DebugReader::cmdFinished,pMainWindow, &MainWindow::enableDebugActions);
140 connect(mReader, &DebugReader::inferiorStopped, pMainWindow, &MainWindow::enableDebugActions);
141
142 connect(mReader, &DebugReader::breakpointInfoGetted, mBreakpointModel,
143 &BreakpointModel::updateBreakpointNumber);
144 connect(mReader, &DebugReader::localsUpdated, pMainWindow,
145 &MainWindow::onLocalsReady);
146 connect(mReader, &DebugReader::memoryUpdated,this,
147 &Debugger::updateMemory);
148 connect(mReader, &DebugReader::evalUpdated,this,
149 &Debugger::updateEval);
150 connect(mReader, &DebugReader::disassemblyUpdate,this,
151 &Debugger::updateDisassembly);
152 connect(mReader, &DebugReader::registerNamesUpdated, this,
153 &Debugger::updateRegisterNames);
154 connect(mReader, &DebugReader::registerValuesUpdated, this,
155 &Debugger::updateRegisterValues);
156 connect(mReader, &DebugReader::varCreated,mWatchModel,
157 &WatchModel::updateVarInfo);
158 connect(mReader, &DebugReader::prepareVarChildren,mWatchModel,
159 &WatchModel::prepareVarChildren);
160 connect(mReader, &DebugReader::addVarChild,mWatchModel,
161 &WatchModel::addVarChild);
162 connect(mReader, &DebugReader::varValueUpdated,mWatchModel,
163 &WatchModel::updateVarValue);
164 connect(mReader, &DebugReader::varsValueUpdated,mWatchModel,
165 &WatchModel::updateAllHasMoreVars);
166 connect(mReader, &DebugReader::inferiorContinued,pMainWindow,
167 &MainWindow::removeActiveBreakpoints);
168 connect(mReader, &DebugReader::inferiorStopped,pMainWindow,
169 &MainWindow::setActiveBreakpoint);
170 connect(mReader, &DebugReader::inferiorStopped,this,
171 &Debugger::refreshAll);
172
173 mReader->registerInferiorStoppedCommand("-stack-list-frames","");
174 mReader->start();
175 mReader->waitStart();
176
177 pMainWindow->updateAppTitle();
178
179 //Application.HintHidePause := 5000;
180 return true;
181}
182void Debugger::stop() {
183 if (mExecuting) {
184 if (mTarget) {
185 mTarget->stopDebug();
186 mTarget = nullptr;
187 }
188 mReader->stopDebug();
189 }
190}
191void Debugger::cleanUpReader()
192{
193 if (mExecuting) {
194 mExecuting = false;
195
196 //stop debugger
197 mReader->deleteLater();
198 mReader=nullptr;
199
200 if (pMainWindow->cpuDialog()!=nullptr) {
201 pMainWindow->cpuDialog()->close();
202 }
203
204 // Free resources
205 pMainWindow->removeActiveBreakpoints();
206
207 pMainWindow->txtLocals()->clear();
208
209 pMainWindow->updateAppTitle();
210
211 pMainWindow->updateDebugEval("");
212
213 mBacktraceModel->clear();
214
215 mWatchModel->clearAllVarInfos();
216
217 mBreakpointModel->invalidateAllBreakpointNumbers();
218
219 pMainWindow->updateEditorActions();
220 }
221}
222
223void Debugger::updateRegisterNames(const QStringList &registerNames)
224{
225 mRegisterModel->updateNames(registerNames);
226}
227
228void Debugger::updateRegisterValues(const QHash<int, QString> &values)
229{
230 mRegisterModel->updateValues(values);
231}
232
233void Debugger::refreshAll()
234{
235 refreshWatchVars();
236 sendCommand("-stack-list-variables", "--all-values");
237 if (memoryModel()->startAddress()>0)
238 sendCommand("-data-read-memory",QString("%1 x 1 %2 %3 ")
239 .arg(memoryModel()->startAddress())
240 .arg(pSettings->debugger().memoryViewRows())
241 .arg(pSettings->debugger().memoryViewColumns())
242 );
243}
244
245RegisterModel *Debugger::registerModel() const
246{
247 return mRegisterModel;
248}
249
250WatchModel *Debugger::watchModel() const
251{
252 return mWatchModel;
253}
254
255void Debugger::sendCommand(const QString &command, const QString &params, DebugCommandSource source)
256{
257 if (mExecuting && mReader) {
258 mReader->postCommand(command,params,source);
259 }
260}
261
262bool Debugger::commandRunning()
263{
264 if (mExecuting && mReader) {
265 return mReader->commandRunning();
266 }
267 return false;
268}
269
270bool Debugger::inferiorRunning()
271{
272 if (mExecuting && mReader) {
273 return mReader->inferiorRunning();
274 }
275 return false;
276}
277
278void Debugger::interrupt()
279{
280 sendCommand("-exec-interrupt", "");
281}
282
283void Debugger::addBreakpoint(int line, const Editor* editor)
284{
285 addBreakpoint(line,editor->filename());
286}
287
288void Debugger::addBreakpoint(int line, const QString &filename)
289{
290 PBreakpoint bp=std::make_shared<Breakpoint>();
291 bp->number = -1;
292 bp->line = line;
293 bp->filename = filename;
294 bp->condition = "";
295 bp->enabled = true;
296 bp->breakpointType = BreakpointType::Breakpoint;
297 mBreakpointModel->addBreakpoint(bp);
298 if (mExecuting) {
299 sendBreakpointCommand(bp);
300 }
301}
302
303void Debugger::deleteBreakpoints(const QString &filename)
304{
305 for (int i=mBreakpointModel->breakpoints().size()-1;i>=0;i--) {
306 PBreakpoint bp = mBreakpointModel->breakpoints()[i];
307 if (bp->filename == filename) {
308 mBreakpointModel->removeBreakpoint(i);
309 }
310 }
311}
312
313void Debugger::deleteBreakpoints(const Editor *editor)
314{
315 deleteBreakpoints(editor->filename());
316}
317
318void Debugger::deleteBreakpoints()
319{
320 for (int i=mBreakpointModel->breakpoints().size()-1;i>=0;i--) {
321 removeBreakpoint(i);
322 }
323}
324
325void Debugger::removeBreakpoint(int line, const Editor *editor)
326{
327 removeBreakpoint(line,editor->filename());
328}
329
330void Debugger::removeBreakpoint(int line, const QString &filename)
331{
332 for (int i=mBreakpointModel->breakpoints().size()-1;i>=0;i--) {
333 PBreakpoint bp = mBreakpointModel->breakpoints()[i];
334 if (bp->filename == filename && bp->line == line) {
335 removeBreakpoint(i);
336 }
337 }
338}
339
340void Debugger::removeBreakpoint(int index)
341{
342 sendClearBreakpointCommand(index);
343 mBreakpointModel->removeBreakpoint(index);
344}
345
346PBreakpoint Debugger::breakpointAt(int line, const QString& filename, int &index)
347{
348 const QList<PBreakpoint>& breakpoints=mBreakpointModel->breakpoints();
349 for (index=0;index<breakpoints.count();index++){
350 PBreakpoint breakpoint = breakpoints[index];
351 if (breakpoint->line == line
352 && breakpoint->filename == filename)
353 return breakpoint;
354 }
355 index=-1;
356 return PBreakpoint();
357}
358
359PBreakpoint Debugger::breakpointAt(int line, const Editor *editor, int &index)
360{
361 return breakpointAt(line,editor->filename(),index);
362}
363
364void Debugger::setBreakPointCondition(int index, const QString &condition)
365{
366 PBreakpoint breakpoint=mBreakpointModel->setBreakPointCondition(index,condition);
367 if (condition.isEmpty()) {
368 sendCommand("-break-condition",
369 QString("%1").arg(breakpoint->number));
370 } else {
371 sendCommand("-break-condition",
372 QString("%1 %2").arg(breakpoint->number).arg(condition));
373 }
374}
375
376void Debugger::sendAllBreakpointsToDebugger()
377{
378 for (PBreakpoint breakpoint:mBreakpointModel->breakpoints()) {
379 sendBreakpointCommand(breakpoint);
380 }
381}
382
383void Debugger::addWatchVar(const QString &expression)
384{
385 // Don't allow duplicates...
386 PWatchVar oldVar = mWatchModel->findWatchVar(expression);
387 if (oldVar)
388 return;
389
390 PWatchVar var = std::make_shared<WatchVar>();
391 var->parent= nullptr;
392 var->expression = expression;
393 var->value = tr("Execute to evaluate");
394 var->numChild = 0;
395 var->hasMore = false;
396 var->parent = nullptr;
397
398 mWatchModel->addWatchVar(var);
399 sendWatchCommand(var);
400}
401
402void Debugger::modifyWatchVarExpression(const QString &oldExpr, const QString &newExpr)
403{
404 // check if name already exists;
405 PWatchVar var = mWatchModel->findWatchVar(newExpr);
406 if (var)
407 return;
408
409 var = mWatchModel->findWatchVar(oldExpr);
410 if (var) {
411 if (mExecuting && !var->expression.isEmpty())
412 sendRemoveWatchCommand(var);
413 var->expression = newExpr;
414 var->type.clear();
415 var->value.clear();
416 var->hasMore = false;
417 var->numChild=0;
418 var->name.clear();
419 var->children.clear();
420
421 if (mExecuting) {
422 sendWatchCommand(var);
423 }
424 }
425}
426
427void Debugger::refreshWatchVars()
428{
429 if (mExecuting) {
430 sendAllWatchVarsToDebugger();
431 sendCommand("-var-update"," --all-values *");
432 }
433}
434
435void Debugger::fetchVarChildren(const QString &varName)
436{
437 if (mExecuting) {
438 sendCommand("-var-list-children",varName);
439 }
440}
441
442bool Debugger::forceUTF8() const
443{
444 return mForceUTF8;
445}
446
447void Debugger::setForceUTF8(bool newForceUTF8)
448{
449 mForceUTF8 = newForceUTF8;
450}
451
452MemoryModel *Debugger::memoryModel() const
453{
454 return mMemoryModel;
455}
456
457void Debugger::removeWatchVars(bool deleteparent)
458{
459 if (deleteparent) {
460 mWatchModel->clear();
461 } else {
462 for(const PWatchVar& var:mWatchModel->watchVars()) {
463 sendRemoveWatchCommand(var);
464 }
465 mWatchModel->clearAllVarInfos();
466 }
467}
468
469void Debugger::removeWatchVar(const QModelIndex &index)
470{
471 PWatchVar var = mWatchModel->findWatchVar(index);
472 if (!var)
473 return;
474 sendRemoveWatchCommand(var);
475 mWatchModel->removeWatchVar(index);
476}
477
478void Debugger::sendAllWatchVarsToDebugger()
479{
480 for (PWatchVar var:mWatchModel->watchVars()) {
481 if (var->name.isEmpty())
482 sendWatchCommand(var);
483 }
484}
485
486PWatchVar Debugger::findWatchVar(const QString &expression)
487{
488 return mWatchModel->findWatchVar(expression);
489}
490
491PWatchVar Debugger::watchVarAt(const QModelIndex &index)
492{
493 return mWatchModel->findWatchVar(index);
494}
495
496//void Debugger::notifyWatchVarUpdated(PWatchVar var)
497//{
498// mWatchModel->notifyUpdated(var);
499//}
500BacktraceModel* Debugger::backtraceModel()
501{
502 return mBacktraceModel;
503}
504
505BreakpointModel *Debugger::breakpointModel()
506{
507 return mBreakpointModel;
508}
509
510void Debugger::sendWatchCommand(PWatchVar var)
511{
512 sendCommand("-var-create", var->expression);
513}
514
515void Debugger::sendRemoveWatchCommand(PWatchVar var)
516{
517 sendCommand("-var-delete",QString("%1").arg(var->name));
518}
519
520void Debugger::sendBreakpointCommand(PBreakpoint breakpoint)
521{
522 if (breakpoint && mExecuting) {
523 // break "filename":linenum
524 QString condition;
525 if (!breakpoint->condition.isEmpty()) {
526 condition = " -c " + breakpoint->condition;
527 }
528 QString filename = breakpoint->filename;
529 filename.replace('\\','/');
530 sendCommand("-break-insert",
531 QString("%1 --source \"%2\" --line %3")
532 .arg(condition,filename)
533 .arg(breakpoint->line));
534 }
535}
536
537void Debugger::sendClearBreakpointCommand(int index)
538{
539 sendClearBreakpointCommand(mBreakpointModel->breakpoints()[index]);
540}
541
542void Debugger::sendClearBreakpointCommand(PBreakpoint breakpoint)
543{
544 // Debugger already running? Remove it from GDB
545 if (breakpoint && breakpoint->number>=0 && mExecuting) {
546 //clear "filename":linenum
547 QString filename = breakpoint->filename;
548 filename.replace('\\','/');
549 sendCommand("-break-delete",
550 QString("%1").arg(breakpoint->number));
551 }
552}
553
554void Debugger::syncFinishedParsing()
555{
556 bool spawnedcpuform = false;
557
558 // GDB determined that the source code is more recent than the executable. Ask the user if he wants to rebuild.
559 if (mReader->receivedSFWarning()) {
560 if (QMessageBox::question(pMainWindow,
561 tr("Compile"),
562 tr("Source file is more recent than executable.")+"<BR /><BR />" + tr("Recompile?"),
563 QMessageBox::Yes | QMessageBox::No,
564 QMessageBox::Yes
565 ) == QMessageBox::Yes) {
566 stop();
567 pMainWindow->compile();
568 return;
569 }
570 }
571
572 // show command output
573 if (pSettings->debugger().enableDebugConsole() ) {
574 if (pSettings->debugger().showDetailLog()) {
575 for (const QString& line:mReader->fullOutput()) {
576 pMainWindow->addDebugOutput(line);
577 }
578 } else {
579 if (mReader->currentCmd() && mReader->currentCmd()->command == "disas") {
580
581 } else {
582 for (const QString& line:mReader->consoleOutput()) {
583 pMainWindow->addDebugOutput(line);
584 }
585 if (
586 (mReader->currentCmd()
587 && mReader->currentCmd()->source== DebugCommandSource::Console)
588 || !mReader->consoleOutput().isEmpty() ) {
589 pMainWindow->addDebugOutput("(gdb)");
590 }
591 }
592 }
593 }
594
595 // The program to debug has stopped. Stop the debugger
596 if (mReader->processExited()) {
597 stop();
598 return;
599 }
600
601 if (mReader->signalReceived()
602 && mReader->signalName()!="SIGINT"
603 && mReader->signalName()!="SIGTRAP") {
604 SignalMessageDialog dialog(pMainWindow);
605 dialog.setOpenCPUInfo(pSettings->debugger().openCPUInfoWhenSignaled());
606 dialog.setMessage(
607 tr("Signal \"%1\" Received: ").arg(mReader->signalName())
608 + "<br />"
609 + mReader->signalMeaning());
610 int result = dialog.exec();
611 if (result == QDialog::Accepted && dialog.openCPUInfo()) {
612 pMainWindow->showCPUInfoDialog();
613 }
614 }
615
616 // CPU form updates itself when spawned, don't update twice!
617 if ((mReader->updateCPUInfo() && !spawnedcpuform) && (pMainWindow->cpuDialog()!=nullptr)) {
618 pMainWindow->cpuDialog()->updateInfo();
619 }
620}
621
622void Debugger::setMemoryData(qulonglong address, unsigned char data)
623{
624 sendCommand("-data-write-memory-bytes", QString("%1 \"%2\"").arg(address).arg(data,2,16,QChar('0')));
625 refreshAll();
626}
627
628void Debugger::setWatchVarValue(const QString &name, const QString &value)
629{
630 sendCommand("-var-assign",QString("%1 %2").arg(name,value));
631 refreshAll();
632}
633
634void Debugger::updateMemory(const QStringList &value)
635{
636 mMemoryModel->updateMemory(value);
637 emit memoryExamineReady(value);
638}
639
640void Debugger::updateEval(const QString &value)
641{
642 emit evalValueReady(value);
643}
644
645void Debugger::updateDisassembly(const QString& file, const QString& func, const QStringList &value)
646{
647 if (pMainWindow->cpuDialog()) {
648 pMainWindow->cpuDialog()->setDisassembly(file,func,value);
649 }
650}
651
652void Debugger::onChangeDebugConsoleLastline(const QString& text)
653{
654 //pMainWindow->changeDebugOutputLastline(text);
655 pMainWindow->addDebugOutput(text);
656}
657
658int Debugger::leftPageIndexBackup() const
659{
660 return mLeftPageIndexBackup;
661}
662
663void Debugger::setLeftPageIndexBackup(int leftPageIndexBackup)
664{
665 mLeftPageIndexBackup = leftPageIndexBackup;
666}
667
668bool Debugger::executing() const
669{
670 return mExecuting;
671}
672
673DebugReader::DebugReader(Debugger* debugger, QObject *parent) : QThread(parent),
674 mCmdQueueMutex(QMutex::Recursive),
675 mStartSemaphore(0)
676{
677 mDebugger = debugger;
678 mProcess = std::make_shared<QProcess>();
679 mCmdRunning = false;
680 mAsyncUpdated = false;
681}
682
683void DebugReader::postCommand(const QString &Command, const QString &Params,
684 DebugCommandSource Source)
685{
686 QMutexLocker locker(&mCmdQueueMutex);
687 PDebugCommand pCmd = std::make_shared<DebugCommand>();
688 pCmd->command = Command;
689 pCmd->params = Params;
690 pCmd->source = Source;
691 mCmdQueue.enqueue(pCmd);
692// if (!mCmdRunning)
693 // runNextCmd();
694}
695
696void DebugReader::registerInferiorStoppedCommand(const QString &Command, const QString &Params)
697{
698 QMutexLocker locker(&mCmdQueueMutex);
699 PDebugCommand pCmd = std::make_shared<DebugCommand>();
700 pCmd->command = Command;
701 pCmd->params = Params;
702 pCmd->source = DebugCommandSource::Other;
703 mInferiorStoppedHookCommands.append(pCmd);
704}
705
706void DebugReader::clearCmdQueue()
707{
708 QMutexLocker locker(&mCmdQueueMutex);
709 mCmdQueue.clear();
710}
711
712void DebugReader::processConsoleOutput(const QByteArray& line)
713{
714 if (line.length()>3 && line.startsWith("~\"") && line.endsWith("\"")) {
715 QByteArray s=line.mid(2,line.length()-3);
716 QByteArray stringValue;
717 const char *p=s.data();
718 while (*p!=0) {
719 if (*p=='\\' && *(p+1)!=0) {
720 p++;
721 switch (*p) {
722 case '\'':
723 stringValue+=0x27;
724 p++;
725 break;
726 case '"':
727 stringValue+=0x22;
728 p++;
729 break;
730 case '?':
731 stringValue+=0x3f;
732 p++;
733 break;
734 case '\\':
735 stringValue+=0x5c;
736 p++;
737 break;
738 case 'a':
739 stringValue+=0x07;
740 p++;
741 break;
742 case 'b':
743 stringValue+=0x08;
744 p++;
745 break;
746 case 'f':
747 stringValue+=0x0c;
748 p++;
749 break;
750 case 'n':
751 stringValue+=0x0a;
752 p++;
753 break;
754 case 'r':
755 stringValue+=0x0d;
756 p++;
757 break;
758 case 't':
759 stringValue+=0x09;
760 p++;
761 break;
762 case 'v':
763 stringValue+=0x0b;
764 p++;
765 break;
766 case '0':
767 case '1':
768 case '2':
769 case '3':
770 case '4':
771 case '5':
772 case '6':
773 case '7':
774 {
775 int i=0;
776 for (i=0;i<3;i++) {
777 if (*(p+i)<'0' || *(p+i)>'7')
778 break;
779 }
780 QByteArray numStr(p,i);
781 bool ok;
782 unsigned char ch = numStr.toInt(&ok,8);
783 stringValue+=ch;
784 p+=i;
785 break;
786 }
787 }
788 } else {
789 stringValue+=*p;
790 p++;
791 }
792 }
793 mConsoleOutput.append(QString::fromLocal8Bit(stringValue));
794 }
795}
796
797void DebugReader::processResult(const QByteArray &result)
798{
799 GDBMIResultParser parser;
800 GDBMIResultType resultType;
801 GDBMIResultParser::ParseObject multiValues;
802 if (!mCurrentCmd)
803 return;
804 bool parseOk = parser.parse(result, mCurrentCmd->command, resultType,multiValues);
805 if (!parseOk)
806 return;
807 switch(resultType) {
808 case GDBMIResultType::BreakpointTable:
809 case GDBMIResultType::Frame:
810 case GDBMIResultType::Locals:
811 break;
812 case GDBMIResultType::Breakpoint:
813 handleBreakpoint(multiValues["bkpt"].object());
814 return;
815 case GDBMIResultType::FrameStack:
816 handleStack(multiValues["stack"].array());
817 return;
818 case GDBMIResultType::LocalVariables:
819 handleLocalVariables(multiValues["variables"].array());
820 return;
821 case GDBMIResultType::Evaluation:
822 handleEvaluation(multiValues["value"].value());
823 return;
824 case GDBMIResultType::Memory:
825 handleMemory(multiValues["memory"].array());
826 return;
827 case GDBMIResultType::RegisterNames:
828 handleRegisterNames(multiValues["register-names"].array());
829 return;
830 case GDBMIResultType::RegisterValues:
831 handleRegisterValue(multiValues["register-values"].array());
832 return;
833 case GDBMIResultType::CreateVar:
834 handleCreateVar(multiValues);
835 return;
836 case GDBMIResultType::ListVarChildren:
837 handleListVarChildren(multiValues);
838 return;
839 case GDBMIResultType::UpdateVarValue:
840 handleUpdateVarValue(multiValues["changelist"].array());
841 return;
842 default:
843 return;
844 }
845
846}
847
848void DebugReader::processExecAsyncRecord(const QByteArray &line)
849{
850 QByteArray result;
851 GDBMIResultParser::ParseObject multiValues;
852 GDBMIResultParser parser;
853 if (!parser.parseAsyncResult(line,result,multiValues))
854 return;
855 if (result == "running") {
856 mInferiorRunning = true;
857 mCurrentAddress=0;
858 mCurrentFile.clear();
859 mCurrentLine=-1;
860 mCurrentFunc.clear();
861 emit inferiorContinued();
862 return;
863 }
864 if (result == "stopped") {
865 mInferiorRunning = false;
866 QByteArray reason = multiValues["reason"].value();
867 if (reason == "exited") {
868 //inferior exited, gdb should terminate too
869 mProcessExited = true;
870 return;
871 }
872 if (reason == "exited-normally") {
873 //inferior exited, gdb should terminate too
874 mProcessExited = true;
875 return;
876 }
877 if (reason == "exited-signalled") {
878 //inferior exited, gdb should terminate too
879 mProcessExited = true;
880 mSignalReceived = true;
881 return;
882 }
883 mUpdateCPUInfo = true;
884 GDBMIResultParser::ParseValue frame(multiValues["frame"]);
885 if (frame.isValid()) {
886 GDBMIResultParser::ParseObject frameObj = frame.object();
887 mCurrentAddress = frameObj["addr"].hexValue();
888 mCurrentLine = frameObj["line"].intValue();
889 if (mDebugger->forceUTF8())
890 mCurrentFile = frameObj["fullname"].utf8PathValue();
891 else
892 mCurrentFile = frameObj["fullname"].pathValue();
893 mCurrentFunc = frameObj["func"].value();
894 }
895 if (reason == "signal-received") {
896 mSignalReceived = true;
897 mSignalName = multiValues["signal-name"].value();
898 mSignalMeaning = multiValues["signal-meaning"].value();
899 }
900 runInferiorStoppedHook();
901 if (mCurrentCmd && mCurrentCmd->source == DebugCommandSource::Console)
902 emit inferiorStopped(mCurrentFile, mCurrentLine,false);
903 else
904 emit inferiorStopped(mCurrentFile, mCurrentLine,true);
905 }
906}
907
908void DebugReader::processError(const QByteArray &errorLine)
909{
910 mConsoleOutput.append(QString::fromLocal8Bit(errorLine));
911}
912
913void DebugReader::processResultRecord(const QByteArray &line)
914{
915 if (line.startsWith("^exit")) {
916 mProcessExited = true;
917 return;
918 }
919 if (line.startsWith("^error")) {
920 processError(line);
921 return;
922 }
923 if (line.startsWith("^done")
924 || line.startsWith("^running")) {
925 int pos = line.indexOf(',');
926 if (pos>=0) {
927 QByteArray result = line.mid(pos+1);
928 processResult(result);
929 } else if (mCurrentCmd && !(mCurrentCmd->command.startsWith('-'))) {
930 if (mCurrentCmd->command == "disas") {
931 QStringList disOutput = mConsoleOutput;
932 if (disOutput.length()>=3) {
933 disOutput.pop_back();
934 disOutput.pop_front();
935 disOutput.pop_front();
936 }
937 emit disassemblyUpdate(mCurrentFile,mCurrentFunc, disOutput);
938 }
939 }
940 return ;
941 }
942 if (line.startsWith("^connected")) {
943 //TODO: connected to remote target
944 return;
945 }
946}
947
948void DebugReader::processDebugOutput(const QByteArray& debugOutput)
949{
950 // Only update once per update at most
951 //WatchView.Items.BeginUpdate;
952
953 emit parseStarted();
954
955 mConsoleOutput.clear();
956 mFullOutput.clear();
957
958 mSignalReceived = false;
959 mUpdateCPUInfo = false;
960 mReceivedSFWarning = false;
961 QList<QByteArray> lines = splitByteArrayToLines(debugOutput);
962
963 for (int i=0;i<lines.count();i++) {
964 QByteArray line = lines[i];
965 if (pSettings->debugger().showDetailLog())
966 mFullOutput.append(line);
967 line = removeToken(line);
968 if (line.isEmpty()) {
969 continue;
970 }
971 switch (line[0]) {
972 case '~': // console stream output
973 processConsoleOutput(line);
974 break;
975 case '@': // target stream output
976 case '&': // log stream output
977 break;
978 case '^': // result record
979 processResultRecord(line);
980 break;
981 case '*': // exec async output
982 processExecAsyncRecord(line);
983 break;
984 case '+': // status async output
985 case '=': // notify async output
986 break;
987 }
988 }
989 emit parseFinished();
990 mConsoleOutput.clear();
991 mFullOutput.clear();
992}
993
994void DebugReader::runInferiorStoppedHook()
995{
996 foreach (const PDebugCommand& cmd, mInferiorStoppedHookCommands) {
997 mCmdQueue.push_front(cmd);
998 }
999}
1000
1001void DebugReader::runNextCmd()
1002{
1003 QMutexLocker locker(&mCmdQueueMutex);
1004
1005 if (mCurrentCmd) {
1006 DebugCommandSource commandSource = mCurrentCmd->source;
1007 mCurrentCmd=nullptr;
1008 if (commandSource!=DebugCommandSource::HeartBeat)
1009 emit cmdFinished();
1010 }
1011 if (mCmdQueue.isEmpty()) {
1012 if (pSettings->debugger().useGDBServer() && mInferiorRunning && !mAsyncUpdated) {
1013 mAsyncUpdated = true;
1014 QTimer::singleShot(50,this,&DebugReader::asyncUpdate);
1015 }
1016 return;
1017 }
1018
1019 PDebugCommand pCmd = mCmdQueue.dequeue();
1020 mCmdRunning = true;
1021 mCurrentCmd = pCmd;
1022 if (pCmd->source!=DebugCommandSource::HeartBeat)
1023 emit cmdStarted();
1024
1025 QByteArray s;
1026 QByteArray params;
1027 s=pCmd->command.toLocal8Bit();
1028 if (!pCmd->params.isEmpty()) {
1029 params = pCmd->params.toLocal8Bit();
1030 }
1031
1032 //clang compatibility
1033 if (pCmd->command == "-break-insert" && mDebugger->forceUTF8()) {
1034 params = pCmd->params.toUtf8();
1035 }
1036 if (pCmd->command == "-var-create") {
1037 //hack for variable creation,to easy remember var expression
1038 params = " - @ "+params;
1039 } else if (pCmd->command == "-var-list-children") {
1040 //hack for list variable children,to easy remember var expression
1041 params = " --all-values \"" + params+'\"';
1042 }
1043 s+=" "+params;
1044 s+= "\n";
1045 if (mProcess->write(s)<0) {
1046 emit writeToDebugFailed();
1047 }
1048
1049// if devDebugger.ShowCommandLog or pCmd^.ShowInConsole then begin
1050 if (pSettings->debugger().enableDebugConsole() ) {
1051 //update debug console
1052 if (pSettings->debugger().showDetailLog()
1053 && pCmd->source != DebugCommandSource::Console) {
1054 emit changeDebugConsoleLastLine(pCmd->command + ' ' + params);
1055 }
1056 }
1057}
1058
1059QStringList DebugReader::tokenize(const QString &s)
1060{
1061 QStringList result;
1062 int tStart,tEnd;
1063 int i=0;
1064 while (i<s.length()) {
1065 QChar ch = s[i];
1066 if (ch == ' ' || ch == '\t'
1067 || ch == '\r'
1068 || ch == '\n') {
1069// if (!current.isEmpty()) {
1070// result.append(current);
1071// current = "";
1072// }
1073 i++;
1074 continue;
1075 } else if (ch == '\'') {
1076 tStart = i;
1077 i++; //skip \'
1078 while (i<s.length()) {
1079 if (s[i]=='\'') {
1080 i++;
1081 break;
1082 } else if (s[i] == '\\') {
1083 i+=2;
1084 continue;
1085 }
1086 i++;
1087 }
1088 tEnd = std::min(i,s.length());
1089 result.append(s.mid(tStart,tEnd-tStart));
1090 } else if (ch == '\"') {
1091 tStart = i;
1092 i++; //skip \'
1093 while (i<s.length()) {
1094 if (s[i]=='\"') {
1095 i++;
1096 break;
1097 } else if (s[i] == '\\') {
1098 i+=2;
1099 continue;
1100 }
1101 i++;
1102 }
1103 tEnd = std::min(i,s.length());
1104 result.append(s.mid(tStart,tEnd-tStart));
1105 } else if (ch == '<') {
1106 tStart = i;
1107 i++;
1108 while (i<s.length()) {
1109 if (s[i]=='>') {
1110 i++;
1111 break;
1112 }
1113 i++;
1114 }
1115 tEnd = std::min(i,s.length());
1116 result.append(s.mid(tStart,tEnd-tStart));
1117 } else if (ch == '(') {
1118 tStart = i;
1119 i++;
1120 while (i<s.length()) {
1121 if (s[i]==')') {
1122 i++;
1123 break;
1124 }
1125 i++;
1126 }
1127 tEnd = std::min(i,s.length());
1128 result.append(s.mid(tStart,tEnd-tStart));
1129 } else if (ch == '_' ||
1130 ch == '.' ||
1131 ch == '+' ||
1132 ch == '-' ||
1133 ch.isLetterOrNumber() ) {
1134 tStart = i;
1135 while (i<s.length()) {
1136 ch = s[i];
1137 if (!(ch == '_' ||
1138 ch == '.' ||
1139 ch == '+' ||
1140 ch == '-' ||
1141 ch.isLetterOrNumber() ))
1142 break;
1143 i++;
1144 }
1145 tEnd = std::min(i,s.length());
1146 result.append(s.mid(tStart,tEnd-tStart));
1147 } else {
1148 result.append(s[i]);
1149 i++;
1150 }
1151 }
1152 return result;
1153}
1154
1155bool DebugReader::outputTerminated(const QByteArray &text)
1156{
1157 QStringList lines = textToLines(QString::fromUtf8(text));
1158 foreach (const QString& line,lines) {
1159 if (line.trimmed() == "(gdb)")
1160 return true;
1161 }
1162 return false;
1163}
1164
1165void DebugReader::handleBreakpoint(const GDBMIResultParser::ParseObject& breakpoint)
1166{
1167 QString filename;
1168 // gdb use system encoding for file path
1169 if (mDebugger->forceUTF8())
1170 filename = breakpoint["fullname"].utf8PathValue();
1171 else
1172 filename = breakpoint["fullname"].pathValue();
1173 int line = breakpoint["line"].intValue();
1174 int number = breakpoint["number"].intValue();
1175 emit breakpointInfoGetted(filename, line , number);
1176}
1177
1178void DebugReader::handleStack(const QList<GDBMIResultParser::ParseValue> & stack)
1179{
1180 mDebugger->backtraceModel()->clear();
1181 foreach (const GDBMIResultParser::ParseValue& frameValue, stack) {
1182 GDBMIResultParser::ParseObject frameObject = frameValue.object();
1183 PTrace trace = std::make_shared<Trace>();
1184 trace->funcname = frameObject["func"].value();
1185 if (mDebugger->forceUTF8())
1186 trace->filename = frameObject["fullname"].utf8PathValue();
1187 else
1188 trace->filename = frameObject["fullname"].pathValue();
1189 trace->line = frameObject["line"].intValue();
1190 trace->level = frameObject["level"].intValue(0);
1191 trace->address = frameObject["addr"].value();
1192 mDebugger->backtraceModel()->addTrace(trace);
1193 }
1194}
1195
1196void DebugReader::handleLocalVariables(const QList<GDBMIResultParser::ParseValue> &variables)
1197{
1198 QStringList locals;
1199 foreach (const GDBMIResultParser::ParseValue& varValue, variables) {
1200 GDBMIResultParser::ParseObject varObject = varValue.object();
1201 locals.append(
1202 QString("%1 = %2")
1203 .arg(
1204 QString(varObject["name"].value()),
1205 QString(varObject["value"].value())
1206 ));
1207 }
1208 emit localsUpdated(locals);
1209}
1210
1211void DebugReader::handleEvaluation(const QString &value)
1212{
1213 emit evalUpdated(value);
1214}
1215
1216void DebugReader::handleMemory(const QList<GDBMIResultParser::ParseValue> &rows)
1217{
1218 QStringList memory;
1219 foreach (const GDBMIResultParser::ParseValue& row, rows) {
1220 GDBMIResultParser::ParseObject rowObject = row.object();
1221 QList<GDBMIResultParser::ParseValue> data = rowObject["data"].array();
1222 QStringList values;
1223 foreach (const GDBMIResultParser::ParseValue& val, data) {
1224 values.append(val.value());
1225 }
1226 memory.append(QString("%1 %2")
1227 .arg(rowObject["addr"].value(),values.join(" ")));
1228 }
1229 emit memoryUpdated(memory);
1230}
1231
1232void DebugReader::handleRegisterNames(const QList<GDBMIResultParser::ParseValue> &names)
1233{
1234 QStringList nameList;
1235 foreach (const GDBMIResultParser::ParseValue& nameValue, names) {
1236 nameList.append(nameValue.value());
1237 }
1238 emit registerNamesUpdated(nameList);
1239}
1240
1241void DebugReader::handleRegisterValue(const QList<GDBMIResultParser::ParseValue> &values)
1242{
1243 QHash<int,QString> result;
1244 foreach (const GDBMIResultParser::ParseValue& val, values) {
1245 GDBMIResultParser::ParseObject obj = val.object();
1246 int number = obj["number"].intValue();
1247 QString value = obj["value"].value();
1248 bool ok;
1249 long long intVal;
1250 intVal = value.toLongLong(&ok,10);
1251 if (ok) {
1252 value = QString("0x%1").arg(intVal,0,16);
1253 }
1254 result.insert(number,value);
1255 }
1256 emit registerValuesUpdated(result);
1257}
1258
1259void DebugReader::handleCreateVar(const GDBMIResultParser::ParseObject &multiVars)
1260{
1261 if (!mCurrentCmd)
1262 return;
1263 QString expression = mCurrentCmd->params;
1264 QString name = multiVars["name"].value();
1265 int numChild = multiVars["numchild"].intValue(0);
1266 QString value = multiVars["value"].value();
1267 QString type = multiVars["type"].value();
1268 bool hasMore = multiVars["has_more"].value() != "0";
1269 emit varCreated(expression,name,numChild,value,type,hasMore);
1270}
1271
1272void DebugReader::handleListVarChildren(const GDBMIResultParser::ParseObject &multiVars)
1273{
1274 if (!mCurrentCmd)
1275 return;
1276 QString parentName = mCurrentCmd->params;
1277 int parentNumChild = multiVars["numchild"].intValue(0);
1278 QList<GDBMIResultParser::ParseValue> children = multiVars["children"].array();
1279 bool hasMore = multiVars["has_more"].value()!="0";
1280 emit prepareVarChildren(parentName,parentNumChild,hasMore);
1281 foreach(const GDBMIResultParser::ParseValue& child, children) {
1282 GDBMIResultParser::ParseObject childObj = child.object();
1283 QString name = childObj["name"].value();
1284 QString exp = childObj["exp"].value();
1285 int numChild = childObj["numchild"].intValue(0);
1286 QString value = childObj["value"].value();
1287 QString type = childObj["type"].value();
1288 bool hasMore = childObj["has_more"].value() != "0";
1289 emit addVarChild(parentName,
1290 name,
1291 exp,
1292 numChild,
1293 value,
1294 type,
1295 hasMore);
1296 }
1297}
1298
1299void DebugReader::handleUpdateVarValue(const QList<GDBMIResultParser::ParseValue> &changes)
1300{
1301 foreach (const GDBMIResultParser::ParseValue& value, changes) {
1302 GDBMIResultParser::ParseObject obj = value.object();
1303 QString name = obj["name"].value();
1304 QString val = obj["value"].value();
1305 QString inScope = obj["in_scope"].value();
1306 bool typeChanged = (obj["type_changed"].value()=="true");
1307 QString newType = obj["new_type"].value();
1308 int newNumChildren = obj["new_num_children"].intValue(-1);
1309 bool hasMore = (obj["has_more"].value() == "1");
1310 emit varValueUpdated(name,val,inScope,typeChanged,newType,newNumChildren,
1311 hasMore);
1312 }
1313 //todo: -var-list-children will freeze if the var is not correctly initialized
1314 //emit varsValueUpdated();
1315}
1316
1317QByteArray DebugReader::removeToken(const QByteArray &line)
1318{
1319 int p=0;
1320 while (p<line.length()) {
1321 QChar ch=line[p];
1322 if (ch<'0' || ch>'9') {
1323 break;
1324 }
1325 p++;
1326 }
1327 if (p<line.length())
1328 return line.mid(p);
1329 return line;
1330}
1331
1332void DebugReader::asyncUpdate()
1333{
1334 QMutexLocker locker(&mCmdQueueMutex);
1335 if (mCmdQueue.isEmpty())
1336 postCommand("-var-update"," --all-values *",DebugCommandSource::HeartBeat);
1337 mAsyncUpdated = false;
1338}
1339
1340const QStringList &DebugReader::binDirs() const
1341{
1342 return mBinDirs;
1343}
1344
1345void DebugReader::addBinDirs(const QStringList &binDirs)
1346{
1347 mBinDirs.append(binDirs);
1348}
1349
1350void DebugReader::addBinDir(const QString &binDir)
1351{
1352 mBinDirs.append(binDir);
1353}
1354
1355const QString &DebugReader::signalMeaning() const
1356{
1357 return mSignalMeaning;
1358}
1359
1360const QString &DebugReader::signalName() const
1361{
1362 return mSignalName;
1363}
1364
1365bool DebugReader::inferiorRunning() const
1366{
1367 return mInferiorRunning;
1368}
1369
1370const QStringList &DebugReader::fullOutput() const
1371{
1372 return mFullOutput;
1373}
1374
1375bool DebugReader::receivedSFWarning() const
1376{
1377 return mReceivedSFWarning;
1378}
1379
1380bool DebugReader::updateCPUInfo() const
1381{
1382 return mUpdateCPUInfo;
1383}
1384
1385const PDebugCommand &DebugReader::currentCmd() const
1386{
1387 return mCurrentCmd;
1388}
1389
1390const QStringList &DebugReader::consoleOutput() const
1391{
1392 return mConsoleOutput;
1393}
1394
1395bool DebugReader::signalReceived() const
1396{
1397 return mSignalReceived;
1398}
1399
1400bool DebugReader::processExited() const
1401{
1402 return mProcessExited;
1403}
1404
1405QString DebugReader::debuggerPath() const
1406{
1407 return mDebuggerPath;
1408}
1409
1410void DebugReader::setDebuggerPath(const QString &debuggerPath)
1411{
1412 mDebuggerPath = debuggerPath;
1413}
1414
1415void DebugReader::stopDebug()
1416{
1417 mStop = true;
1418}
1419
1420bool DebugReader::commandRunning()
1421{
1422 return !mCmdQueue.isEmpty();
1423}
1424
1425void DebugReader::waitStart()
1426{
1427 mStartSemaphore.acquire(1);
1428}
1429
1430void DebugReader::run()
1431{
1432 mStop = false;
1433 mInferiorRunning = false;
1434 mProcessExited = false;
1435 mErrorOccured = false;
1436 QString cmd = mDebuggerPath;
1437// QString arguments = "--annotate=2";
1438 QString arguments = "--interpret=mi --silent";
1439 QString workingDir = QFileInfo(mDebuggerPath).path();
1440
1441 mProcess = std::make_shared<QProcess>();
1442 auto action = finally([&]{
1443 mProcess.reset();
1444 });
1445 mProcess->setProgram(cmd);
1446 mProcess->setArguments(splitProcessCommand(arguments));
1447 mProcess->setProcessChannelMode(QProcess::MergedChannels);
1448
1449 QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
1450 QString path = env.value("PATH");
1451 QStringList pathAdded = mBinDirs;
1452 if (!path.isEmpty()) {
1453 path = pathAdded.join(PATH_SEPARATOR) + PATH_SEPARATOR + path;
1454 } else {
1455 path = pathAdded.join(PATH_SEPARATOR);
1456 }
1457 QString cmdDir = extractFileDir(cmd);
1458 if (!cmdDir.isEmpty()) {
1459 path = cmdDir + PATH_SEPARATOR + path;
1460 }
1461 env.insert("PATH",path);
1462 mProcess->setProcessEnvironment(env);
1463
1464 mProcess->setWorkingDirectory(workingDir);
1465
1466 connect(mProcess.get(), &QProcess::errorOccurred,
1467 [&](){
1468 mErrorOccured= true;
1469 });
1470 QByteArray buffer;
1471 QByteArray readed;
1472
1473 mProcess->start();
1474 mProcess->waitForStarted(5000);
1475 mStartSemaphore.release(1);
1476 while (true) {
1477 mProcess->waitForFinished(1);
1478 if (mProcess->state()!=QProcess::Running) {
1479 break;
1480 }
1481 if (mStop) {
1482 mProcess->terminate();
1483 mProcess->kill();
1484 break;
1485 }
1486 if (mErrorOccured)
1487 break;
1488 readed = mProcess->readAll();
1489 buffer += readed;
1490
1491 if (readed.endsWith("\n")&& outputTerminated(buffer)) {
1492 processDebugOutput(buffer);
1493 buffer.clear();
1494 mCmdRunning = false;
1495 runNextCmd();
1496 } else if (!mCmdRunning && readed.isEmpty()){
1497 runNextCmd();
1498 } else if (readed.isEmpty()){
1499 msleep(1);
1500 }
1501 }
1502 if (mErrorOccured) {
1503 emit processError(mProcess->error());
1504 }
1505}
1506
1507BreakpointModel::BreakpointModel(QObject *parent):QAbstractTableModel(parent)
1508{
1509
1510}
1511
1512int BreakpointModel::rowCount(const QModelIndex &) const
1513{
1514 return mList.size();
1515}
1516
1517int BreakpointModel::columnCount(const QModelIndex &) const
1518{
1519 return 3;
1520}
1521
1522QVariant BreakpointModel::data(const QModelIndex &index, int role) const
1523{
1524 if (!index.isValid())
1525 return QVariant();
1526 if (index.row()<0 || index.row() >= static_cast<int>(mList.size()))
1527 return QVariant();
1528 PBreakpoint breakpoint = mList[index.row()];
1529 if (!breakpoint)
1530 return QVariant();
1531 switch (role) {
1532 case Qt::DisplayRole:
1533 switch (index.column()) {
1534 case 0: {
1535 return extractFileName(breakpoint->filename);
1536 }
1537 case 1:
1538 if (breakpoint->line>0)
1539 return breakpoint->line;
1540 else
1541 return "";
1542 case 2:
1543 return breakpoint->condition;
1544 default:
1545 return QVariant();
1546 }
1547 case Qt::ToolTipRole:
1548 switch (index.column()) {
1549 case 0:
1550 return breakpoint->filename;
1551 case 1:
1552 if (breakpoint->line>0)
1553 return breakpoint->line;
1554 else
1555 return "";
1556 case 2:
1557 return breakpoint->condition;
1558 default:
1559 return QVariant();
1560 }
1561 default:
1562 return QVariant();
1563 }
1564}
1565
1566QVariant BreakpointModel::headerData(int section, Qt::Orientation orientation, int role) const
1567{
1568 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
1569 switch(section) {
1570 case 0:
1571 return tr("Filename");
1572 case 1:
1573 return tr("Line");
1574 case 2:
1575 return tr("Condition");
1576 }
1577 }
1578 return QVariant();
1579}
1580
1581void BreakpointModel::addBreakpoint(PBreakpoint p)
1582{
1583 beginInsertRows(QModelIndex(),mList.size(),mList.size());
1584 mList.push_back(p);
1585 endInsertRows();
1586}
1587
1588void BreakpointModel::clear()
1589{
1590 beginResetModel();
1591 mList.clear();
1592 endResetModel();
1593}
1594
1595void BreakpointModel::removeBreakpoint(int row)
1596{
1597 beginRemoveRows(QModelIndex(),row,row);
1598 mList.removeAt(row);
1599 endRemoveRows();
1600}
1601
1602void BreakpointModel::invalidateAllBreakpointNumbers()
1603{
1604 foreach (PBreakpoint bp,mList) {
1605 bp->number = -1;
1606 }
1607 //emit dateChanged(createIndex(0,0),)
1608}
1609
1610PBreakpoint BreakpointModel::setBreakPointCondition(int index, const QString &condition)
1611{
1612 PBreakpoint breakpoint = mList[index];
1613 breakpoint->condition = condition;
1614 emit dataChanged(createIndex(index,0),createIndex(index,2));
1615 return breakpoint;
1616}
1617
1618const QList<PBreakpoint> &BreakpointModel::breakpoints() const
1619{
1620 return mList;
1621}
1622
1623PBreakpoint BreakpointModel::breakpoint(int index) const
1624{
1625 if (index<0 && index>=mList.count())
1626 return PBreakpoint();
1627 return mList[index];
1628}
1629
1630void BreakpointModel::save(const QString &filename)
1631{
1632 QFile file(filename);
1633 if (file.open(QFile::WriteOnly | QFile::Truncate)) {
1634 QJsonArray array;
1635 foreach (const PBreakpoint& breakpoint, mList) {
1636 QJsonObject obj;
1637 obj["filename"]=breakpoint->filename;
1638 obj["line"]=breakpoint->line;
1639 obj["condition"]=breakpoint->condition;
1640 obj["enabled"]=breakpoint->enabled;
1641 obj["breakpoint_type"] = static_cast<int>(breakpoint->breakpointType);
1642 array.append(obj);
1643 }
1644 QJsonDocument doc;
1645 doc.setArray(array);
1646 if (file.write(doc.toJson())<0) {
1647 throw FileError(tr("Save file '%1' failed.")
1648 .arg(filename));
1649 }
1650 } else {
1651 throw FileError(tr("Can't open file '%1' for write.")
1652 .arg(filename));
1653 }
1654}
1655
1656void BreakpointModel::load(const QString &filename)
1657{
1658 clear();
1659 QFile file(filename);
1660 if (!file.exists())
1661 return;
1662 if (file.open(QFile::ReadOnly)) {
1663 QByteArray content = file.readAll();
1664 QJsonParseError error;
1665 QJsonDocument doc(QJsonDocument::fromJson(content,&error));
1666 if (error.error != QJsonParseError::NoError) {
1667 throw FileError(tr("Error in json file '%1':%2 : %3")
1668 .arg(filename)
1669 .arg(error.offset)
1670 .arg(error.errorString()));
1671 }
1672 QJsonArray array = doc.array();
1673 for (int i=0;i<array.count();i++) {
1674 QJsonValue value = array[i];
1675 QJsonObject obj=value.toObject();
1676 PBreakpoint breakpoint = std::make_shared<Breakpoint>();
1677 breakpoint->filename = QFileInfo(obj["filename"].toString()).absoluteFilePath();
1678 breakpoint->line = obj["line"].toInt();
1679 breakpoint->condition = obj["condition"].toString();
1680 breakpoint->enabled = obj["enabled"].toBool();
1681 breakpoint->breakpointType = static_cast<BreakpointType>(obj["breakpoint_type"].toInt());
1682
1683 addBreakpoint(breakpoint);
1684 }
1685 } else {
1686 throw FileError(tr("Can't open file '%1' for read.")
1687 .arg(filename));
1688 }
1689}
1690
1691void BreakpointModel::updateBreakpointNumber(const QString& filename, int line, int number)
1692{
1693 foreach (PBreakpoint bp, mList) {
1694 if (bp->filename == filename && bp->line == line) {
1695 bp->number = number;
1696 return;
1697 }
1698 }
1699}
1700
1701void BreakpointModel::onFileDeleteLines(const QString& filename, int startLine, int count)
1702{
1703 for (int i = mList.count()-1;i>=0;i--){
1704 PBreakpoint breakpoint = mList[i];
1705 if (breakpoint->filename == filename
1706 && breakpoint->line>=startLine) {
1707 if (breakpoint->line >= startLine+count) {
1708 breakpoint->line -= count;
1709 emit dataChanged(createIndex(i,0),createIndex(i,2));
1710 } else {
1711 removeBreakpoint(i);
1712 }
1713 }
1714 }
1715}
1716
1717void BreakpointModel::onFileInsertLines(const QString& filename, int startLine, int count)
1718{
1719 for (int i = mList.count()-1;i>=0;i--){
1720 PBreakpoint breakpoint = mList[i];
1721 if (breakpoint->filename == filename
1722 && breakpoint->line>=startLine) {
1723 breakpoint->line+=count;
1724 emit dataChanged(createIndex(i,0),createIndex(i,2));
1725 }
1726 }
1727}
1728
1729
1730BacktraceModel::BacktraceModel(QObject *parent):QAbstractTableModel(parent)
1731{
1732
1733}
1734
1735int BacktraceModel::rowCount(const QModelIndex &) const
1736{
1737 return mList.size();
1738}
1739
1740int BacktraceModel::columnCount(const QModelIndex &) const
1741{
1742 return 3;
1743}
1744
1745QVariant BacktraceModel::data(const QModelIndex &index, int role) const
1746{
1747 if (!index.isValid())
1748 return QVariant();
1749 if (index.row()<0 || index.row() >= static_cast<int>(mList.size()))
1750 return QVariant();
1751 PTrace trace = mList[index.row()];
1752 if (!trace)
1753 return QVariant();
1754 switch (role) {
1755 case Qt::DisplayRole:
1756 switch (index.column()) {
1757 case 0:
1758 return trace->funcname;
1759 case 1:
1760 return trace->filename;
1761 case 2:
1762 if (trace->line>0)
1763 return trace->line;
1764 else
1765 return "";
1766 default:
1767 return QVariant();
1768 }
1769 default:
1770 return QVariant();
1771 }
1772}
1773
1774QVariant BacktraceModel::headerData(int section, Qt::Orientation orientation, int role) const
1775{
1776 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
1777 switch(section) {
1778 case 0:
1779 return tr("Function");
1780 case 1:
1781 return tr("Filename");
1782 case 2:
1783 return tr("Line");
1784 }
1785 }
1786 return QVariant();
1787}
1788
1789void BacktraceModel::addTrace(PTrace p)
1790{
1791 beginInsertRows(QModelIndex(),mList.size(),mList.size());
1792 mList.push_back(p);
1793 endInsertRows();
1794}
1795
1796void BacktraceModel::clear()
1797{
1798 beginResetModel();
1799 mList.clear();
1800 endResetModel();
1801}
1802
1803void BacktraceModel::removeTrace(int row)
1804{
1805 beginRemoveRows(QModelIndex(),row,row);
1806 mList.removeAt(row);
1807 endRemoveRows();
1808}
1809
1810const QList<PTrace> &BacktraceModel::backtraces() const
1811{
1812 return mList;
1813}
1814
1815PTrace BacktraceModel::backtrace(int index) const
1816{
1817 if (index>=0 && index < mList.count()){
1818 return mList[index];
1819 }
1820 return PTrace();
1821}
1822
1823WatchModel::WatchModel(QObject *parent):QAbstractItemModel(parent)
1824{
1825 mUpdateCount = 0;
1826}
1827
1828QVariant WatchModel::data(const QModelIndex &index, int role) const
1829{
1830 if (!index.isValid()) {
1831 return QVariant();
1832 }
1833 WatchVar* item = static_cast<WatchVar*>(index.internalPointer());
1834 switch (role) {
1835 case Qt::DisplayRole:
1836 switch(index.column()) {
1837 case 0:
1838 return item->expression;
1839 case 1:
1840 return item->type;
1841 case 2:
1842 return item->value;
1843 }
1844 }
1845 return QVariant();
1846}
1847
1848QModelIndex WatchModel::index(int row, int column, const QModelIndex &parent) const
1849{
1850 if (!hasIndex(row,column,parent))
1851 return QModelIndex();
1852 WatchVar* parentItem;
1853 PWatchVar pChild;
1854 if (!parent.isValid()) {
1855 parentItem = nullptr;
1856 pChild = mWatchVars[row];
1857 } else {
1858 parentItem = static_cast<WatchVar*>(parent.internalPointer());
1859 pChild = parentItem->children[row];
1860 }
1861 if (pChild) {
1862 return createIndex(row,column,pChild.get());
1863 }
1864 return QModelIndex();
1865}
1866
1867static int getWatchIndex(WatchVar* var, const QList<PWatchVar> list) {
1868 for (int i=0;i<list.size();i++) {
1869 PWatchVar v = list[i];
1870 if (v.get() == var) {
1871 return i;
1872 }
1873 }
1874 return -1;
1875}
1876
1877QModelIndex WatchModel::parent(const QModelIndex &index) const
1878{
1879 if (!index.isValid()) {
1880 return QModelIndex();
1881 }
1882 WatchVar* childItem = static_cast<WatchVar*>(index.internalPointer());
1883 WatchVar* parentItem = childItem->parent;
1884
1885 //parent is root
1886 if (parentItem == nullptr) {
1887 return QModelIndex();
1888 }
1889 int row;
1890 WatchVar* grandItem = parentItem->parent;
1891 if (grandItem == nullptr) {
1892 row = getWatchIndex(parentItem,mWatchVars);
1893 } else {
1894 row = getWatchIndex(parentItem,grandItem->children);
1895 }
1896 return createIndex(row,0,parentItem);
1897}
1898
1899int WatchModel::rowCount(const QModelIndex &parent) const
1900{
1901 if (!parent.isValid()) {
1902 return mWatchVars.count();
1903 } else {
1904 WatchVar* parentItem = static_cast<WatchVar*>(parent.internalPointer());
1905 return parentItem->children.count();
1906 }
1907}
1908
1909int WatchModel::columnCount(const QModelIndex&) const
1910{
1911 return 3;
1912}
1913
1914void WatchModel::addWatchVar(PWatchVar watchVar)
1915{
1916 for (PWatchVar var:mWatchVars) {
1917 if (watchVar->expression == var->expression) {
1918 return;
1919 }
1920 }
1921 beginInsertRows(QModelIndex(),mWatchVars.count(),mWatchVars.count());
1922 mWatchVars.append(watchVar);
1923 endInsertRows();
1924}
1925
1926void WatchModel::removeWatchVar(const QString &express)
1927{
1928 for (int i=mWatchVars.size()-1;i>=0;i--) {
1929 PWatchVar var = mWatchVars[i];
1930 if (express == var->expression) {
1931 this->beginResetModel();
1932 //this->beginRemoveRows(QModelIndex(),i,i);
1933 if (mVarIndex.contains(var->name))
1934 mVarIndex.remove(var->name);
1935 mWatchVars.removeAt(i);
1936 //this->endRemoveRows();
1937 this->endResetModel();
1938 }
1939 }
1940}
1941
1942void WatchModel::removeWatchVar(const QModelIndex &index)
1943{
1944 int r=index.row();
1945 this->beginRemoveRows(QModelIndex(),r,r);
1946 PWatchVar var = mWatchVars[r];
1947 if (mVarIndex.contains(var->name))
1948 mVarIndex.remove(var->name);
1949 mWatchVars.removeAt(r);
1950 this->endRemoveRows();
1951}
1952
1953void WatchModel::clear()
1954{
1955 this->beginResetModel();
1956 mWatchVars.clear();
1957 this->endResetModel();
1958}
1959
1960const QList<PWatchVar> &WatchModel::watchVars()
1961{
1962 return mWatchVars;
1963}
1964
1965PWatchVar WatchModel::findWatchVar(const QModelIndex &index)
1966{
1967 if (!index.isValid())
1968 return PWatchVar();
1969 int r=index.row();
1970 return mWatchVars[r];
1971}
1972
1973PWatchVar WatchModel::findWatchVar(const QString &expr)
1974{
1975 for (PWatchVar var:mWatchVars) {
1976 if (expr == var->expression) {
1977 return var;
1978 }
1979 }
1980 return PWatchVar();
1981}
1982
1983void WatchModel::resetAllVarInfos()
1984{
1985 beginResetModel();
1986 foreach (PWatchVar var, mWatchVars) {
1987 var->name.clear();
1988 var->value = tr("Not Valid");
1989 var->numChild = 0;
1990 var->hasMore = false;
1991 var->type.clear();
1992 var->children.clear();
1993 }
1994 mVarIndex.clear();
1995 endResetModel();
1996}
1997
1998void WatchModel::updateVarInfo(const QString &expression, const QString &name, int numChild, const QString &value, const QString &type, bool hasMore)
1999{
2000 PWatchVar var = findWatchVar(expression);
2001 if (!var)
2002 return;
2003 var->name = name;
2004 var->value = value;
2005 var->numChild = numChild;
2006 var->hasMore = hasMore;
2007 var->type = type;
2008 mVarIndex.insert(name,var);
2009 QModelIndex idx = index(var);
2010 if (!idx.isValid())
2011 return;
2012 emit dataChanged(idx,createIndex(idx.row(),2,var.get()));
2013}
2014
2015void WatchModel::prepareVarChildren(const QString &parentName, int numChild, bool hasMore)
2016{
2017 PWatchVar var = mVarIndex.value(parentName,PWatchVar());
2018 if (var) {
2019 var->numChild = numChild;
2020 var->hasMore = hasMore;
2021 if (var->children.count()>0) {
2022 beginRemoveRows(index(var),0,var->children.count()-1);
2023 var->children.clear();
2024 endRemoveRows();
2025 }
2026 }
2027}
2028
2029void WatchModel::addVarChild(const QString &parentName, const QString &name,
2030 const QString &exp, int numChild, const QString &value,
2031 const QString &type, bool hasMore)
2032{
2033 PWatchVar var = mVarIndex.value(parentName,PWatchVar());
2034 if (!var)
2035 return;
2036 beginInsertRows(index(var),var->children.count(),var->children.count());
2037 PWatchVar child = std::make_shared<WatchVar>();
2038 child->name = name;
2039 child->expression = exp;
2040 child->numChild = numChild;
2041 child->value = value;
2042 child->type = type;
2043 child->hasMore = hasMore;
2044 child->parent = var.get();
2045 var->children.append(child);
2046 endInsertRows();
2047 mVarIndex.insert(name,child);
2048}
2049
2050void WatchModel::updateVarValue(const QString &name, const QString &val, const QString &inScope, bool typeChanged, const QString &newType, int newNumChildren, bool hasMore)
2051{
2052 PWatchVar var = mVarIndex.value(name,PWatchVar());
2053 if (!var)
2054 return;
2055 if (inScope == "true") {
2056 var->value = val;
2057 } else{
2058 var->value = tr("Not Valid");
2059 }
2060 if (typeChanged) {
2061 var->type = newType;
2062 }
2063 QModelIndex idx = index(var);
2064 bool oldHasMore = var->hasMore;
2065 var->hasMore = hasMore;
2066 if (newNumChildren>=0
2067 && var->numChild!=newNumChildren) {
2068 var->numChild = newNumChildren;
2069 fetchMore(idx);
2070 } else if (!oldHasMore && hasMore) {
2071 fetchMore(idx);
2072 }
2073 emit dataChanged(idx,createIndex(idx.row(),2,var.get()));
2074}
2075
2076void WatchModel::updateAllHasMoreVars()
2077{
2078 foreach (const PWatchVar& var, mVarIndex.values()) {
2079 if (var->hasMore) {
2080 QModelIndex idx = index(var);
2081 fetchMore(idx);
2082 }
2083 }
2084}
2085
2086void WatchModel::clearAllVarInfos()
2087{
2088 beginResetModel();
2089 foreach (PWatchVar var, mWatchVars) {
2090 var->name.clear();
2091 var->value = tr("Execute to evaluate");
2092 var->numChild = 0;
2093 var->hasMore = false;
2094 var->type.clear();
2095 var->children.clear();
2096 }
2097 mVarIndex.clear();
2098 endResetModel();
2099}
2100
2101void WatchModel::beginUpdate()
2102{
2103 if (mUpdateCount == 0) {
2104 beginResetModel();
2105 }
2106 mUpdateCount++;
2107}
2108
2109void WatchModel::endUpdate()
2110{
2111 mUpdateCount--;
2112 if (mUpdateCount == 0) {
2113 endResetModel();
2114 }
2115}
2116
2117void WatchModel::notifyUpdated(PWatchVar var)
2118{
2119 if (!var)
2120 return;
2121 int row;
2122 if (var->parent==nullptr) {
2123 row = mWatchVars.indexOf(var);
2124 } else {
2125 row = var->parent->children.indexOf(var);
2126 }
2127 if (row<0)
2128 return;
2129 //qDebug()<<"dataChanged"<<row<<":"<<var->text;
2130 emit dataChanged(createIndex(row,0,var.get()),createIndex(row,0,var.get()));
2131}
2132
2133void WatchModel::save(const QString &filename)
2134{
2135 QFile file(filename);
2136 if (file.open(QFile::WriteOnly | QFile::Truncate)) {
2137 QJsonArray array;
2138 foreach (const PWatchVar& watchVar, mWatchVars) {
2139 QJsonObject obj;
2140 obj["expression"]=watchVar->expression;
2141 array.append(obj);
2142 }
2143 QJsonDocument doc;
2144 doc.setArray(array);
2145 if (file.write(doc.toJson())<0) {
2146 throw FileError(tr("Save file '%1' failed.")
2147 .arg(filename));
2148 }
2149 } else {
2150 throw FileError(tr("Can't open file '%1' for write.")
2151 .arg(filename));
2152 }
2153}
2154
2155void WatchModel::load(const QString &filename)
2156{
2157 clear();
2158 QFile file(filename);
2159 if (!file.exists())
2160 return;
2161 if (file.open(QFile::ReadOnly)) {
2162 QByteArray content = file.readAll();
2163 QJsonParseError error;
2164 QJsonDocument doc(QJsonDocument::fromJson(content,&error));
2165 if (error.error != QJsonParseError::NoError) {
2166 throw FileError(tr("Error in json file '%1':%2 : %3")
2167 .arg(filename)
2168 .arg(error.offset)
2169 .arg(error.errorString()));
2170 }
2171 QJsonArray array = doc.array();
2172 for (int i=0;i<array.count();i++) {
2173 QJsonValue value = array[i];
2174 QJsonObject obj=value.toObject();
2175 PWatchVar var = std::make_shared<WatchVar>();
2176 var->parent= nullptr;
2177 var->expression = obj["expression"].toString();
2178 var->value = tr("Execute to evaluate");
2179 var->numChild = 0;
2180 var->hasMore=false;
2181 var->parent = nullptr;
2182
2183 addWatchVar(var);
2184 }
2185 } else {
2186 throw FileError(tr("Can't open file '%1' for read.")
2187 .arg(filename));
2188 }
2189}
2190
2191QModelIndex WatchModel::index(PWatchVar var) const
2192{
2193 if (!var)
2194 return QModelIndex();
2195 return index(var.get());
2196}
2197
2198QModelIndex WatchModel::index(WatchVar* pVar) const {
2199 if (pVar==nullptr)
2200 return QModelIndex();
2201 if (pVar->parent) {
2202 int row=-1;
2203 for (int i=0;i<pVar->parent->children.count();i++) {
2204 if (pVar->parent->children[i].get() == pVar) {
2205 row = i;
2206 break;
2207 }
2208 }
2209 if (row<0)
2210 return QModelIndex();
2211 return createIndex(row,0,pVar);
2212 } else {
2213 int row=-1;
2214 for (int i=0;i<mWatchVars.count();i++) {
2215 if (mWatchVars[i].get() == pVar) {
2216 row = i;
2217 break;
2218 }
2219 }
2220 if (row<0)
2221 return QModelIndex();
2222 return createIndex(row,0,pVar);
2223 }
2224}
2225
2226bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int role)
2227{
2228 if (!index.isValid()) {
2229 return false;
2230 }
2231 if (index.column()==2 && role == Qt::EditRole) {
2232 WatchVar* item = static_cast<WatchVar*>(index.internalPointer());
2233 emit setWatchVarValue(item->name,value.toString());
2234 }
2235 return false;
2236
2237}
2238
2239Qt::ItemFlags WatchModel::flags(const QModelIndex &index) const
2240{
2241 Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
2242 if (!index.isValid()) {
2243 return Qt::ItemIsEnabled;
2244 }
2245 if (index.column() == 2) {
2246 WatchVar* item = static_cast<WatchVar*>(index.internalPointer());
2247 if (item->numChild==0 && !item->type.isEmpty())
2248 flags |= Qt::ItemIsEditable;
2249 }
2250 return flags;
2251}
2252
2253
2254QVariant WatchModel::headerData(int section, Qt::Orientation orientation, int role) const
2255{
2256 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
2257 switch(section) {
2258 case 0:
2259 return tr("Expression");
2260 case 1:
2261 return tr("Type");
2262 case 2:
2263 return tr("Value");
2264 }
2265 }
2266 return QVariant();
2267}
2268
2269void WatchModel::fetchMore(const QModelIndex &parent)
2270{
2271 if (!parent.isValid()) {
2272 return;
2273 }
2274 WatchVar* item = static_cast<WatchVar*>(parent.internalPointer());
2275 item->hasMore = false;
2276 item->numChild = item->children.count();
2277 emit fetchChildren(item->name);
2278}
2279
2280bool WatchModel::canFetchMore(const QModelIndex &parent) const
2281{
2282 if (!parent.isValid()) {
2283 return false;
2284 }
2285 WatchVar* item = static_cast<WatchVar*>(parent.internalPointer());
2286 return item->numChild>item->children.count() || item->hasMore;
2287}
2288
2289bool WatchModel::hasChildren(const QModelIndex &parent) const
2290{
2291 if (!parent.isValid()) {
2292 return true;
2293 }
2294 WatchVar* item = static_cast<WatchVar*>(parent.internalPointer());
2295 return item->numChild>0 || item->hasMore;
2296}
2297
2298RegisterModel::RegisterModel(QObject *parent):QAbstractTableModel(parent)
2299{
2300
2301}
2302
2303int RegisterModel::rowCount(const QModelIndex &) const
2304{
2305 return mRegisterNames.count();
2306}
2307
2308int RegisterModel::columnCount(const QModelIndex &) const
2309{
2310 return 2;
2311}
2312
2313QVariant RegisterModel::data(const QModelIndex &index, int role) const
2314{
2315 if (!index.isValid())
2316 return QVariant();
2317 if (index.row()<0 || index.row() >= static_cast<int>(mRegisterNames.size()))
2318 return QVariant();
2319 switch (role) {
2320 case Qt::DisplayRole:
2321 switch (index.column()) {
2322 case 0:
2323 return mRegisterNames[index.row()];
2324 case 1:
2325 return mRegisterValues.value(index.row(),"");
2326 default:
2327 return QVariant();
2328 }
2329 default:
2330 return QVariant();
2331 }
2332}
2333
2334QVariant RegisterModel::headerData(int section, Qt::Orientation orientation, int role) const
2335{
2336 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
2337 switch(section) {
2338 case 0:
2339 return tr("Register");
2340 case 1:
2341 return tr("Value");
2342 }
2343 }
2344 return QVariant();
2345}
2346
2347void RegisterModel::updateNames(const QStringList &regNames)
2348{
2349 beginResetModel();
2350 mRegisterNames = regNames;
2351 endResetModel();
2352}
2353
2354void RegisterModel::updateValues(const QHash<int, QString> registerValues)
2355{
2356 mRegisterValues= registerValues;
2357 emit dataChanged(createIndex(0,1),
2358 createIndex(mRegisterNames.count()-1,1));
2359}
2360
2361
2362void RegisterModel::clear()
2363{
2364 beginResetModel();
2365 mRegisterNames.clear();
2366 mRegisterValues.clear();
2367 endResetModel();
2368}
2369
2370DebugTarget::DebugTarget(
2371 const QString &inferior,
2372 const QString &GDBServer,
2373 int port,
2374 QObject *parent):
2375 QThread(parent),
2376 mInferior(inferior),
2377 mGDBServer(GDBServer),
2378 mPort(port),
2379 mStop(false),
2380 mStartSemaphore(0),
2381 mErrorOccured(false)
2382{
2383 mProcess = nullptr;
2384}
2385
2386void DebugTarget::setInputFile(const QString &inputFile)
2387{
2388 mInputFile = inputFile;
2389}
2390
2391void DebugTarget::stopDebug()
2392{
2393 mStop = true;
2394}
2395
2396void DebugTarget::waitStart()
2397{
2398 mStartSemaphore.acquire(1);
2399}
2400
2401const QStringList &DebugTarget::binDirs() const
2402{
2403 return mBinDirs;
2404}
2405
2406void DebugTarget::addBinDirs(const QStringList &binDirs)
2407{
2408 mBinDirs.append(binDirs);
2409}
2410
2411void DebugTarget::addBinDir(const QString &binDir)
2412{
2413 mBinDirs.append(binDir);
2414}
2415
2416void DebugTarget::run()
2417{
2418 mStop = false;
2419 mErrorOccured = false;
2420
2421 //find first available port
2422 QString cmd;
2423 QString arguments;
2424#ifdef Q_OS_WIN
2425 cmd= mGDBServer;
2426 arguments = QString(" localhost:%1 \"%2\"").arg(mPort).arg(mInferior);
2427#else
2428 cmd= pSettings->environment().terminalPath();
2429 arguments = QString(" -e \"%1\" localhost:%2 \"%3\"").arg(mGDBServer).arg(mPort).arg(mInferior);
2430#endif
2431 QString workingDir = QFileInfo(mInferior).path();
2432
2433 mProcess = std::make_shared<QProcess>();
2434 auto action = finally([&]{
2435 mProcess.reset();
2436 });
2437 mProcess->setProgram(cmd);
2438 mProcess->setArguments(splitProcessCommand(arguments));
2439 mProcess->setProcessChannelMode(QProcess::MergedChannels);
2440 QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
2441 QString path = env.value("PATH");
2442 QStringList pathAdded = mBinDirs;
2443 if (!path.isEmpty()) {
2444 path = pathAdded.join(PATH_SEPARATOR) + PATH_SEPARATOR + path;
2445 } else {
2446 path = pathAdded.join(PATH_SEPARATOR);
2447 }
2448 QString cmdDir = extractFileDir(cmd);
2449 if (!cmdDir.isEmpty()) {
2450 path = cmdDir + PATH_SEPARATOR + path;
2451 }
2452 env.insert("PATH",path);
2453 mProcess->setProcessEnvironment(env);
2454 mProcess->setWorkingDirectory(workingDir);
2455
2456#ifdef Q_OS_WIN
2457 mProcess->setCreateProcessArgumentsModifier([this](QProcess::CreateProcessArguments * args){
2458 if (programHasConsole(mInferior)) {
2459 args->flags |= CREATE_NEW_CONSOLE;
2460 args->flags &= ~CREATE_NO_WINDOW;
2461 }
2462 if (mInputFile.isEmpty()) {
2463 args->startupInfo -> dwFlags &= ~STARTF_USESTDHANDLES;
2464 } else {
2465 args->startupInfo->hStdOutput = NULL;
2466 args->startupInfo->hStdError = NULL;
2467 }
2468
2469 });
2470#endif
2471
2472 connect(mProcess.get(), &QProcess::errorOccurred,
2473 [&](){
2474 mErrorOccured= true;
2475 });
2476 mProcess->start();
2477 mProcess->waitForStarted(5000);
2478 mStartSemaphore.release(1);
2479 if (mProcess->state()==QProcess::Running && !mInputFile.isEmpty()) {
2480 mProcess->write(readFileToByteArray(mInputFile));
2481 mProcess->waitForFinished(0);
2482 }
2483 bool writeChannelClosed = false;
2484 while (true) {
2485 if (mProcess->bytesToWrite()==0 && !writeChannelClosed) {
2486 writeChannelClosed = true;
2487 mProcess->closeWriteChannel();
2488 }
2489 mProcess->waitForFinished(1);
2490 if (mProcess->state()!=QProcess::Running) {
2491 break;
2492 }
2493 if (mStop) {
2494 mProcess->terminate();
2495 mProcess->kill();
2496 break;
2497 }
2498 if (mErrorOccured)
2499 break;
2500 msleep(1);
2501 }
2502 if (mErrorOccured) {
2503 emit processError(mProcess->error());
2504 }
2505}
2506
2507MemoryModel::MemoryModel(int dataPerLine, QObject *parent):
2508 QAbstractTableModel(parent),
2509 mDataPerLine(dataPerLine),
2510 mStartAddress(0)
2511{
2512}
2513
2514void MemoryModel::updateMemory(const QStringList &value)
2515{
2516 int maxDataPerLine=-1;
2517 QRegExp delimiter("(\\s+)");
2518 QList<PMemoryLine> newModel;
2519 for (int i=0;i<value.length();i++) {
2520 QString line = value[i].trimmed();
2521#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
2522 QStringList dataLst = line.split(delimiter,Qt::SkipEmptyParts);
2523#else
2524 QStringList dataLst = line.split(delimiter,QString::SkipEmptyParts);
2525#endif
2526 PMemoryLine memoryLine = std::make_shared<MemoryLine>();
2527 memoryLine->startAddress = -1;
2528 if (dataLst.length()>0) {
2529 bool isOk;
2530 memoryLine->startAddress = stringToHex(dataLst[0],isOk);
2531 if (isOk) {
2532 if (dataLst.length()-1>maxDataPerLine)
2533 maxDataPerLine = dataLst.length()-1;
2534 for (int j=1;j<dataLst.length();j++) {
2535 qulonglong data = stringToHex(dataLst[j],isOk);
2536 if (isOk)
2537 memoryLine->datas.append((unsigned char)data);
2538 else
2539 memoryLine->datas.append(0);
2540 }
2541 } else {
2542 memoryLine->startAddress=0;
2543 }
2544
2545 }
2546 newModel.append(memoryLine);
2547 }
2548 if (newModel.count()>0 && newModel.count()== mLines.count() &&
2549 newModel[0]->startAddress == mLines[0]->startAddress &&
2550 maxDataPerLine==mDataPerLine) {
2551 for (int i=0;i<newModel.count();i++) {
2552 PMemoryLine newLine = newModel[i];
2553 PMemoryLine oldLine = mLines[i];
2554 for (int j=0;j<newLine->datas.count();j++) {
2555 if (j>=oldLine->datas.count())
2556 break;
2557 if (newLine->datas[j]!=oldLine->datas[j])
2558 newLine->changedDatas.insert(j);
2559 }
2560 }
2561 mLines = newModel;
2562 emit dataChanged(createIndex(0,0),
2563 createIndex(mLines.count()-1,mDataPerLine-1));
2564 } else {
2565 beginResetModel();
2566 if (maxDataPerLine>0)
2567 mDataPerLine=maxDataPerLine;
2568 mLines = newModel;
2569 endResetModel();
2570 }
2571 if (mLines.count()>0) {
2572 mStartAddress = mLines[0]->startAddress;
2573 } else {
2574 mStartAddress = 0;
2575 }
2576 }
2577
2578int MemoryModel::rowCount(const QModelIndex &/*parent*/) const
2579{
2580 return mLines.count();
2581}
2582
2583int MemoryModel::columnCount(const QModelIndex &/*parent*/) const
2584{
2585 return mDataPerLine;
2586}
2587
2588QVariant MemoryModel::data(const QModelIndex &index, int role) const
2589{
2590 if (!index.isValid())
2591 return QVariant();
2592 if (index.row()<0 || index.row()>=mLines.count())
2593 return QVariant();
2594 PMemoryLine line = mLines[index.row()];
2595 int col = index.column();
2596 if (col<0 || col>=line->datas.count())
2597 return QVariant();
2598 if (role == Qt::DisplayRole)
2599 return QString("%1").arg(line->datas[col],2,16,QChar('0'));
2600 return QVariant();
2601}
2602
2603QVariant MemoryModel::headerData(int section, Qt::Orientation orientation, int role) const
2604{
2605 if (orientation == Qt::Vertical && role == Qt::DisplayRole) {
2606 if (section<0 || section>=mLines.count())
2607 return QVariant();
2608 PMemoryLine line = mLines[section];
2609 return QString("0x%1").arg(line->startAddress,0,16,QChar('0'));
2610 }
2611 return QVariant();
2612}
2613
2614bool MemoryModel::setData(const QModelIndex &index, const QVariant &value, int role)
2615{
2616 if (!index.isValid())
2617 return false;
2618 if (index.row()<0 || index.row()>=mLines.count())
2619 return false;
2620 PMemoryLine line = mLines[index.row()];
2621 int col = index.column();
2622 if (col<0 || col>=line->datas.count())
2623 return false;
2624 if (role == Qt::EditRole && mStartAddress>0) {
2625 bool ok;
2626 unsigned char val = ("0x"+value.toString()).toUInt(&ok,16);
2627 if (!ok)
2628 return false;
2629 emit setMemoryData(mStartAddress+mDataPerLine*index.row()+col,val);
2630 return true;
2631 }
2632 return false;
2633}
2634
2635Qt::ItemFlags MemoryModel::flags(const QModelIndex &/*index*/) const
2636{
2637 Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
2638 if (mStartAddress!=0)
2639 flags |= Qt::ItemIsEditable;
2640 return flags;
2641}
2642
2643qulonglong MemoryModel::startAddress() const
2644{
2645 return mStartAddress;
2646}
2647
2648void MemoryModel::reset()
2649{
2650 mStartAddress=0;
2651 mLines.clear();
2652}
2653