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