1 //
2 // debugview.cpp
3 //
4 // Description: Manages the interaction with GDB
5 //
6 //
7 // SPDX-FileCopyrightText: 2008-2010 Ian Wakeling <ian.wakeling@ntlworld.com>
8 // SPDX-FileCopyrightText: 2011 Kåre Särs <kare.sars@iki.fi>
9 //
10 //  SPDX-License-Identifier: LGPL-2.0-only
11 
12 #include "debugview.h"
13 
14 #include <QFile>
15 #include <QFileInfo>
16 #include <QRegularExpression>
17 #include <QStandardPaths>
18 #include <QTimer>
19 
20 #include <KLocalizedString>
21 #include <KMessageBox>
22 #include <KUrlRequester>
23 
24 #include <signal.h>
25 #include <stdlib.h>
26 
27 static const QString PromptStr = QStringLiteral("(prompt)");
28 
DebugView(QObject * parent)29 DebugView::DebugView(QObject *parent)
30     : QObject(parent)
31     , m_debugProcess(nullptr)
32     , m_state(none)
33     , m_subState(normal)
34     , m_debugLocationChanged(true)
35     , m_queryLocals(false)
36 {
37 }
38 
~DebugView()39 DebugView::~DebugView()
40 {
41     if (m_debugProcess.state() != QProcess::NotRunning) {
42         m_debugProcess.kill();
43         m_debugProcess.blockSignals(true);
44         m_debugProcess.waitForFinished();
45     }
46 }
47 
runDebugger(const GDBTargetConf & conf,const QStringList & ioFifos)48 void DebugView::runDebugger(const GDBTargetConf &conf, const QStringList &ioFifos)
49 {
50     if (conf.executable.isEmpty()) {
51         return;
52     }
53 
54     m_targetConf = conf;
55 
56     // no chance if no debugger configured
57     if (m_targetConf.gdbCmd.isEmpty()) {
58         return;
59     }
60 
61     // only run debugger from PATH or the absolute executable path we specified
62     const auto fullExecutable = QFileInfo(m_targetConf.gdbCmd).isAbsolute() ? m_targetConf.gdbCmd : QStandardPaths::findExecutable(m_targetConf.gdbCmd);
63     if (fullExecutable.isEmpty()) {
64         return;
65     }
66 
67     if (ioFifos.size() == 3) {
68         m_ioPipeString = QStringLiteral("< %1 1> %2 2> %3").arg(ioFifos[0], ioFifos[1], ioFifos[2]);
69     }
70 
71     if (m_state == none) {
72         m_outBuffer.clear();
73         m_errBuffer.clear();
74         m_errorList.clear();
75 
76         // create a process to control GDB
77         m_debugProcess.setWorkingDirectory(m_targetConf.workDir);
78 
79         connect(&m_debugProcess, static_cast<void (QProcess::*)(QProcess::ProcessError)>(&QProcess::errorOccurred), this, &DebugView::slotError);
80 
81         connect(&m_debugProcess, &QProcess::readyReadStandardError, this, &DebugView::slotReadDebugStdErr);
82 
83         connect(&m_debugProcess, &QProcess::readyReadStandardOutput, this, &DebugView::slotReadDebugStdOut);
84 
85         connect(&m_debugProcess, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &DebugView::slotDebugFinished);
86 
87         m_debugProcess.start(fullExecutable, QStringList());
88 
89         m_nextCommands << QStringLiteral("set pagination off");
90         m_state = ready;
91     } else {
92         // On startup the gdb prompt will trigger the "nextCommands",
93         // here we have to trigger it manually.
94         QTimer::singleShot(0, this, &DebugView::issueNextCommand);
95     }
96     m_nextCommands << QStringLiteral("file \"%1\"").arg(m_targetConf.executable);
97     m_nextCommands << QStringLiteral("set args %1 %2").arg(m_targetConf.arguments, m_ioPipeString);
98     m_nextCommands << QStringLiteral("set inferior-tty /dev/null");
99     m_nextCommands << m_targetConf.customInit;
100     m_nextCommands << QStringLiteral("(Q) info breakpoints");
101 }
102 
debuggerRunning() const103 bool DebugView::debuggerRunning() const
104 {
105     return (m_state != none);
106 }
107 
debuggerBusy() const108 bool DebugView::debuggerBusy() const
109 {
110     return (m_state == executingCmd);
111 }
112 
hasBreakpoint(const QUrl & url,int line)113 bool DebugView::hasBreakpoint(const QUrl &url, int line)
114 {
115     for (const auto &breakpoint : qAsConst(m_breakPointList)) {
116         if ((url == breakpoint.file) && (line == breakpoint.line)) {
117             return true;
118         }
119     }
120     return false;
121 }
122 
toggleBreakpoint(QUrl const & url,int line)123 void DebugView::toggleBreakpoint(QUrl const &url, int line)
124 {
125     if (m_state == ready) {
126         QString cmd;
127         if (hasBreakpoint(url, line)) {
128             cmd = QStringLiteral("clear %1:%2").arg(url.path()).arg(line);
129         } else {
130             cmd = QStringLiteral("break %1:%2").arg(url.path()).arg(line);
131         }
132         issueCommand(cmd);
133     }
134 }
135 
slotError()136 void DebugView::slotError()
137 {
138     KMessageBox::sorry(nullptr, i18n("Could not start debugger process"));
139 }
140 
slotReadDebugStdOut()141 void DebugView::slotReadDebugStdOut()
142 {
143     m_outBuffer += QString::fromLocal8Bit(m_debugProcess.readAllStandardOutput().data());
144     int end = 0;
145     // handle one line at a time
146     do {
147         end = m_outBuffer.indexOf(QLatin1Char('\n'));
148         if (end < 0) {
149             break;
150         }
151         processLine(m_outBuffer.mid(0, end));
152         m_outBuffer.remove(0, end + 1);
153     } while (1);
154 
155     if (m_outBuffer == QLatin1String("(gdb) ") || m_outBuffer == QLatin1String(">")) {
156         m_outBuffer.clear();
157         processLine(PromptStr);
158     }
159 }
160 
slotReadDebugStdErr()161 void DebugView::slotReadDebugStdErr()
162 {
163     m_errBuffer += QString::fromLocal8Bit(m_debugProcess.readAllStandardError().data());
164     int end = 0;
165     // add whole lines at a time to the error list
166     do {
167         end = m_errBuffer.indexOf(QLatin1Char('\n'));
168         if (end < 0) {
169             break;
170         }
171         m_errorList << m_errBuffer.mid(0, end);
172         m_errBuffer.remove(0, end + 1);
173     } while (1);
174 
175     processErrors();
176 }
177 
slotDebugFinished(int,QProcess::ExitStatus status)178 void DebugView::slotDebugFinished(int /*exitCode*/, QProcess::ExitStatus status)
179 {
180     if (status != QProcess::NormalExit) {
181         Q_EMIT outputText(i18n("*** gdb exited normally ***") + QLatin1Char('\n'));
182     }
183 
184     m_state = none;
185     Q_EMIT readyForInput(false);
186 
187     // remove all old breakpoints
188     BreakPoint bPoint;
189     while (!m_breakPointList.empty()) {
190         bPoint = m_breakPointList.takeFirst();
191         Q_EMIT breakPointCleared(bPoint.file, bPoint.line - 1);
192     }
193 
194     Q_EMIT gdbEnded();
195 }
196 
movePC(QUrl const & url,int line)197 void DebugView::movePC(QUrl const &url, int line)
198 {
199     if (m_state == ready) {
200         QString cmd = QStringLiteral("tbreak %1:%2").arg(url.path()).arg(line);
201         m_nextCommands << QStringLiteral("jump %1:%2").arg(url.path()).arg(line);
202         issueCommand(cmd);
203     }
204 }
205 
runToCursor(QUrl const & url,int line)206 void DebugView::runToCursor(QUrl const &url, int line)
207 {
208     if (m_state == ready) {
209         QString cmd = QStringLiteral("tbreak %1:%2").arg(url.path()).arg(line);
210         m_nextCommands << QStringLiteral("continue");
211         issueCommand(cmd);
212     }
213 }
214 
slotInterrupt()215 void DebugView::slotInterrupt()
216 {
217     if (m_state == executingCmd) {
218         m_debugLocationChanged = true;
219     }
220     const auto pid = m_debugProcess.processId();
221     if (pid != 0) {
222         ::kill(pid, SIGINT);
223     }
224 }
225 
slotKill()226 void DebugView::slotKill()
227 {
228     if (m_state != ready) {
229         slotInterrupt();
230         m_state = ready;
231     }
232     issueCommand(QStringLiteral("kill"));
233 }
234 
slotReRun()235 void DebugView::slotReRun()
236 {
237     slotKill();
238     m_nextCommands << QStringLiteral("file \"%1\"").arg(m_targetConf.executable);
239     m_nextCommands << QStringLiteral("set args %1 %2").arg(m_targetConf.arguments).arg(m_ioPipeString);
240     m_nextCommands << QStringLiteral("set inferior-tty /dev/null");
241     m_nextCommands << m_targetConf.customInit;
242     m_nextCommands << QStringLiteral("(Q) info breakpoints");
243 
244     m_nextCommands << QStringLiteral("tbreak main");
245     m_nextCommands << QStringLiteral("run");
246     m_nextCommands << QStringLiteral("p setvbuf(stdout, 0, %1, 1024)").arg(_IOLBF);
247     m_nextCommands << QStringLiteral("continue");
248 }
249 
slotStepInto()250 void DebugView::slotStepInto()
251 {
252     issueCommand(QStringLiteral("step"));
253 }
254 
slotStepOver()255 void DebugView::slotStepOver()
256 {
257     issueCommand(QStringLiteral("next"));
258 }
259 
slotStepOut()260 void DebugView::slotStepOut()
261 {
262     issueCommand(QStringLiteral("finish"));
263 }
264 
slotContinue()265 void DebugView::slotContinue()
266 {
267     issueCommand(QStringLiteral("continue"));
268 }
269 
270 static const QRegularExpression breakpointList(QStringLiteral("\\ANum\\s+Type\\s+Disp\\s+Enb\\s+Address\\s+What.*\\z"));
271 static const QRegularExpression breakpointListed(QStringLiteral("\\A(\\d)\\s+breakpoint\\s+keep\\sy\\s+0x[\\da-f]+\\sin\\s.+\\sat\\s([^:]+):(\\d+).*\\z"));
272 static const QRegularExpression stackFrameAny(QStringLiteral("\\A#(\\d+)\\s(.*)\\z"));
273 static const QRegularExpression stackFrameFile(QStringLiteral("\\A#(\\d+)\\s+(?:0x[\\da-f]+\\s*in\\s)*(\\S+)(\\s\\(.*\\)) at ([^:]+):(\\d+).*\\z"));
274 static const QRegularExpression changeFile(
275     QStringLiteral("\\A(?:(?:Temporary\\sbreakpoint|Breakpoint)\\s*\\d+,\\s*|0x[\\da-f]+\\s*in\\s*)?[^\\s]+\\s*\\([^)]*\\)\\s*at\\s*([^:]+):(\\d+).*\\z"));
276 static const QRegularExpression changeLine(QStringLiteral("\\A(\\d+)\\s+.*\\z"));
277 static const QRegularExpression breakPointReg(QStringLiteral("\\ABreakpoint\\s+(\\d+)\\s+at\\s+0x[\\da-f]+:\\s+file\\s+([^\\,]+)\\,\\s+line\\s+(\\d+).*\\z"));
278 static const QRegularExpression breakPointMultiReg(QStringLiteral("\\ABreakpoint\\s+(\\d+)\\s+at\\s+0x[\\da-f]+:\\s+([^\\,]+):(\\d+).*\\z"));
279 static const QRegularExpression breakPointDel(QStringLiteral("\\ADeleted\\s+breakpoint.*\\z"));
280 static const QRegularExpression exitProgram(QStringLiteral("\\A(?:Program|.*Inferior.*)\\s+exited.*\\z"));
281 static const QRegularExpression threadLine(QStringLiteral("\\A\\**\\s+(\\d+)\\s+Thread.*\\z"));
282 
processLine(QString line)283 void DebugView::processLine(QString line)
284 {
285     if (line.isEmpty()) {
286         return;
287     }
288 
289     static QRegularExpressionMatch match;
290     switch (m_state) {
291     case none:
292     case ready:
293         if (PromptStr == line) {
294             // we get here after initialization
295             QTimer::singleShot(0, this, &DebugView::issueNextCommand);
296         }
297         break;
298 
299     case executingCmd:
300         if (breakpointList.match(line).hasMatch()) {
301             m_state = listingBreakpoints;
302             Q_EMIT clearBreakpointMarks();
303             m_breakPointList.clear();
304         } else if (line.contains(QLatin1String("No breakpoints or watchpoints."))) {
305             Q_EMIT clearBreakpointMarks();
306             m_breakPointList.clear();
307         } else if ((match = stackFrameAny.match(line)).hasMatch()) {
308             if (m_lastCommand.contains(QLatin1String("info stack"))) {
309                 Q_EMIT stackFrameInfo(match.captured(1), match.captured(2));
310             } else {
311                 m_subState = (m_subState == normal) ? stackFrameSeen : stackTraceSeen;
312 
313                 m_newFrameLevel = match.captured(1).toInt();
314 
315                 if ((match = stackFrameFile.match(line)).hasMatch()) {
316                     m_newFrameFile = match.captured(4);
317                 }
318             }
319         } else if ((match = changeFile.match(line)).hasMatch()) {
320             m_currentFile = match.captured(1).trimmed();
321             int lineNum = match.captured(2).toInt();
322 
323             if (!m_nextCommands.contains(QLatin1String("continue"))) {
324                 // GDB uses 1 based line numbers, kate uses 0 based...
325                 Q_EMIT debugLocationChanged(resolveFileName(m_currentFile), lineNum - 1);
326             }
327             m_debugLocationChanged = true;
328         } else if ((match = changeLine.match(line)).hasMatch()) {
329             int lineNum = match.captured(1).toInt();
330 
331             if (m_subState == stackFrameSeen) {
332                 m_currentFile = m_newFrameFile;
333             }
334             if (!m_nextCommands.contains(QLatin1String("continue"))) {
335                 // GDB uses 1 based line numbers, kate uses 0 based...
336                 Q_EMIT debugLocationChanged(resolveFileName(m_currentFile), lineNum - 1);
337             }
338             m_debugLocationChanged = true;
339         } else if ((match = breakPointReg.match(line)).hasMatch()) {
340             BreakPoint breakPoint;
341             breakPoint.number = match.captured(1).toInt();
342             breakPoint.file = resolveFileName(match.captured(2));
343             breakPoint.line = match.captured(3).toInt();
344             m_breakPointList << breakPoint;
345             Q_EMIT breakPointSet(breakPoint.file, breakPoint.line - 1);
346         } else if ((match = breakPointMultiReg.match(line)).hasMatch()) {
347             BreakPoint breakPoint;
348             breakPoint.number = match.captured(1).toInt();
349             breakPoint.file = resolveFileName(match.captured(2));
350             breakPoint.line = match.captured(3).toInt();
351             m_breakPointList << breakPoint;
352             Q_EMIT breakPointSet(breakPoint.file, breakPoint.line - 1);
353         } else if (breakPointDel.match(line).hasMatch()) {
354             line.remove(QStringLiteral("Deleted breakpoint"));
355             line.remove(QLatin1Char('s')); // in case of multiple breakpoints
356             QStringList numbers = line.split(QLatin1Char(' '), Qt::SkipEmptyParts);
357             for (int i = 0; i < numbers.size(); i++) {
358                 for (int j = 0; j < m_breakPointList.size(); j++) {
359                     if (numbers[i].toInt() == m_breakPointList[j].number) {
360                         Q_EMIT breakPointCleared(m_breakPointList[j].file, m_breakPointList[j].line - 1);
361                         m_breakPointList.removeAt(j);
362                         break;
363                     }
364                 }
365             }
366         } else if (exitProgram.match(line).hasMatch() || line.contains(QLatin1String("The program no longer exists"))
367                    || line.contains(QLatin1String("Kill the program being debugged"))) {
368             // if there are still commands to execute remove them to remove unneeded output
369             // except  if the "kill was for "re-run"
370             if ((!m_nextCommands.empty()) && !m_nextCommands[0].contains(QLatin1String("file"))) {
371                 m_nextCommands.clear();
372             }
373             m_debugLocationChanged = false; // do not insert (Q) commands
374             Q_EMIT programEnded();
375         } else if (PromptStr == line) {
376             if (m_subState == stackFrameSeen) {
377                 Q_EMIT stackFrameChanged(m_newFrameLevel);
378             }
379             m_state = ready;
380 
381             // Give the error a possibility get noticed since stderr and stdout are not in sync
382             QTimer::singleShot(0, this, &DebugView::issueNextCommand);
383         }
384         break;
385 
386     case listingBreakpoints:;
387         if ((match = breakpointListed.match(line)).hasMatch()) {
388             BreakPoint breakPoint;
389             breakPoint.number = match.captured(1).toInt();
390             breakPoint.file = resolveFileName(match.captured(2));
391             breakPoint.line = match.captured(3).toInt();
392             m_breakPointList << breakPoint;
393             Q_EMIT breakPointSet(breakPoint.file, breakPoint.line - 1);
394         } else if (PromptStr == line) {
395             m_state = ready;
396             QTimer::singleShot(0, this, &DebugView::issueNextCommand);
397         }
398         break;
399     case infoArgs:
400         if (PromptStr == line) {
401             m_state = ready;
402             QTimer::singleShot(0, this, &DebugView::issueNextCommand);
403         } else {
404             Q_EMIT infoLocal(line);
405         }
406         break;
407     case printThis:
408         if (PromptStr == line) {
409             m_state = ready;
410             QTimer::singleShot(0, this, &DebugView::issueNextCommand);
411         } else {
412             Q_EMIT infoLocal(line);
413         }
414         break;
415     case infoLocals:
416         if (PromptStr == line) {
417             m_state = ready;
418             Q_EMIT infoLocal(QString());
419             QTimer::singleShot(0, this, &DebugView::issueNextCommand);
420         } else {
421             Q_EMIT infoLocal(line);
422         }
423         break;
424     case infoStack:
425         if (PromptStr == line) {
426             m_state = ready;
427             Q_EMIT stackFrameInfo(QString(), QString());
428             QTimer::singleShot(0, this, &DebugView::issueNextCommand);
429         } else if ((match = stackFrameAny.match(line)).hasMatch()) {
430             Q_EMIT stackFrameInfo(match.captured(1), match.captured(2));
431         }
432         break;
433     case infoThreads:
434         if (PromptStr == line) {
435             m_state = ready;
436             QTimer::singleShot(0, this, &DebugView::issueNextCommand);
437         } else if ((match = threadLine.match(line)).hasMatch()) {
438             Q_EMIT threadInfo(match.captured(1).toInt(), (line[0] == QLatin1Char('*')));
439         }
440         break;
441     }
442     outputTextMaybe(line);
443 }
444 
processErrors()445 void DebugView::processErrors()
446 {
447     QString error;
448     while (!m_errorList.empty()) {
449         error = m_errorList.takeFirst();
450         // qDebug() << error;
451         if (error == QLatin1String("The program is not being run.")) {
452             if (m_lastCommand == QLatin1String("continue")) {
453                 m_nextCommands.clear();
454                 m_nextCommands << QStringLiteral("tbreak main");
455                 m_nextCommands << QStringLiteral("run");
456                 m_nextCommands << QStringLiteral("p setvbuf(stdout, 0, %1, 1024)").arg(_IOLBF);
457                 m_nextCommands << QStringLiteral("continue");
458                 QTimer::singleShot(0, this, &DebugView::issueNextCommand);
459             } else if ((m_lastCommand == QLatin1String("step")) || (m_lastCommand == QLatin1String("next")) || (m_lastCommand == QLatin1String("finish"))) {
460                 m_nextCommands.clear();
461                 m_nextCommands << QStringLiteral("tbreak main");
462                 m_nextCommands << QStringLiteral("run");
463                 m_nextCommands << QStringLiteral("p setvbuf(stdout, 0, %1, 1024)").arg(_IOLBF);
464                 QTimer::singleShot(0, this, &DebugView::issueNextCommand);
465             } else if ((m_lastCommand == QLatin1String("kill"))) {
466                 if (!m_nextCommands.empty()) {
467                     if (!m_nextCommands[0].contains(QLatin1String("file"))) {
468                         m_nextCommands.clear();
469                         m_nextCommands << QStringLiteral("quit");
470                     }
471                     // else continue with "ReRun"
472                 } else {
473                     m_nextCommands << QStringLiteral("quit");
474                 }
475                 m_state = ready;
476                 QTimer::singleShot(0, this, &DebugView::issueNextCommand);
477             }
478             // else do nothing
479         } else if (error.contains(QLatin1String("No line ")) || error.contains(QLatin1String("No source file named"))) {
480             // setting a breakpoint failed. Do not continue.
481             m_nextCommands.clear();
482             Q_EMIT readyForInput(true);
483         } else if (error.contains(QLatin1String("No stack"))) {
484             m_nextCommands.clear();
485             Q_EMIT programEnded();
486         }
487 
488         if ((m_lastCommand == QLatin1String("(Q)print *this")) && error.contains(QLatin1String("No symbol \"this\" in current context."))) {
489             continue;
490         }
491         Q_EMIT outputError(error + QLatin1Char('\n'));
492     }
493 }
494 
issueCommand(QString const & cmd)495 void DebugView::issueCommand(QString const &cmd)
496 {
497     if (m_state == ready) {
498         Q_EMIT readyForInput(false);
499         m_state = executingCmd;
500         if (cmd == QLatin1String("(Q)info locals")) {
501             m_state = infoLocals;
502         } else if (cmd == QLatin1String("(Q)info args")) {
503             m_state = infoArgs;
504         } else if (cmd == QLatin1String("(Q)print *this")) {
505             m_state = printThis;
506         } else if (cmd == QLatin1String("(Q)info stack")) {
507             m_state = infoStack;
508         } else if (cmd == QLatin1String("(Q)info thread")) {
509             Q_EMIT threadInfo(-1, false);
510             m_state = infoThreads;
511         }
512         m_subState = normal;
513         m_lastCommand = cmd;
514 
515         if (cmd.startsWith(QLatin1String("(Q)"))) {
516             m_debugProcess.write(qPrintable(cmd.mid(3)));
517         } else {
518             Q_EMIT outputText(QStringLiteral("(gdb) ") + cmd + QLatin1Char('\n'));
519             m_debugProcess.write(qPrintable(cmd));
520         }
521         m_debugProcess.write("\n");
522     }
523 }
524 
issueNextCommand()525 void DebugView::issueNextCommand()
526 {
527     if (m_state == ready) {
528         if (!m_nextCommands.empty()) {
529             QString cmd = m_nextCommands.takeFirst();
530             // qDebug() << "Next command" << cmd;
531             issueCommand(cmd);
532         } else {
533             // FIXME "thread" needs a better generic solution
534             if (m_debugLocationChanged || m_lastCommand.startsWith(QLatin1String("thread"))) {
535                 m_debugLocationChanged = false;
536                 if (m_queryLocals && !m_lastCommand.startsWith(QLatin1String("(Q)"))) {
537                     m_nextCommands << QStringLiteral("(Q)info stack");
538                     m_nextCommands << QStringLiteral("(Q)frame");
539                     m_nextCommands << QStringLiteral("(Q)info args");
540                     m_nextCommands << QStringLiteral("(Q)print *this");
541                     m_nextCommands << QStringLiteral("(Q)info locals");
542                     m_nextCommands << QStringLiteral("(Q)info thread");
543                     issueNextCommand();
544                     return;
545                 }
546             }
547             Q_EMIT readyForInput(true);
548         }
549     }
550 }
551 
resolveFileName(const QString & fileName)552 QUrl DebugView::resolveFileName(const QString &fileName)
553 {
554     QFileInfo fInfo = QFileInfo(fileName);
555     // did we end up with an absolute path or a relative one?
556     if (fInfo.exists()) {
557         return QUrl::fromUserInput(fInfo.absoluteFilePath());
558     }
559 
560     if (fInfo.isAbsolute()) {
561         // we can not do anything just return the fileName
562         return QUrl::fromUserInput(fileName);
563     }
564 
565     // Now try to add the working path
566     fInfo = QFileInfo(m_targetConf.workDir + fileName);
567     if (fInfo.exists()) {
568         return QUrl::fromUserInput(fInfo.absoluteFilePath());
569     }
570 
571     // now try the executable path
572     fInfo = QFileInfo(QFileInfo(m_targetConf.executable).absolutePath() + fileName);
573     if (fInfo.exists()) {
574         return QUrl::fromUserInput(fInfo.absoluteFilePath());
575     }
576 
577     for (const QString &srcPath : qAsConst(m_targetConf.srcPaths)) {
578         fInfo = QFileInfo(srcPath + QDir::separator() + fileName);
579         if (fInfo.exists()) {
580             return QUrl::fromUserInput(fInfo.absoluteFilePath());
581         }
582     }
583 
584     // we can not do anything just return the fileName
585     Q_EMIT sourceFileNotFound(fileName);
586     return QUrl::fromUserInput(fileName);
587 }
588 
outputTextMaybe(const QString & text)589 void DebugView::outputTextMaybe(const QString &text)
590 {
591     if (!m_lastCommand.startsWith(QLatin1String("(Q)")) && !text.contains(PromptStr)) {
592         Q_EMIT outputText(text + QLatin1Char('\n'));
593     }
594 }
595 
slotQueryLocals(bool query)596 void DebugView::slotQueryLocals(bool query)
597 {
598     m_queryLocals = query;
599     if (query && (m_state == ready) && (m_nextCommands.empty())) {
600         m_nextCommands << QStringLiteral("(Q)info stack");
601         m_nextCommands << QStringLiteral("(Q)frame");
602         m_nextCommands << QStringLiteral("(Q)info args");
603         m_nextCommands << QStringLiteral("(Q)print *this");
604         m_nextCommands << QStringLiteral("(Q)info locals");
605         m_nextCommands << QStringLiteral("(Q)info thread");
606         issueNextCommand();
607     }
608 }
609 
targetName() const610 QString DebugView::targetName() const
611 {
612     return m_targetConf.targetName;
613 }
614 
setFileSearchPaths(const QStringList & paths)615 void DebugView::setFileSearchPaths(const QStringList &paths)
616 {
617     m_targetConf.srcPaths = paths;
618 }
619