1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "gdbengine.h"
27 
28 #include <debugger/debuggerinternalconstants.h>
29 #include <debugger/debuggerruncontrol.h>
30 #include <debugger/disassemblerlines.h>
31 
32 #include <debugger/debuggeractions.h>
33 #include <debugger/debuggercore.h>
34 #include <debugger/debuggermainwindow.h>
35 #include <debugger/debuggerplugin.h>
36 #include <debugger/debuggerprotocol.h>
37 #include <debugger/debuggertooltipmanager.h>
38 #include <debugger/disassembleragent.h>
39 #include <debugger/memoryagent.h>
40 #include <debugger/sourceutils.h>
41 #include <debugger/terminal.h>
42 
43 #include <debugger/breakhandler.h>
44 #include <debugger/moduleshandler.h>
45 #include <debugger/registerhandler.h>
46 #include <debugger/sourcefileshandler.h>
47 #include <debugger/stackhandler.h>
48 #include <debugger/threadshandler.h>
49 #include <debugger/debuggersourcepathmappingwidget.h>
50 #include <debugger/logwindow.h>
51 #include <debugger/procinterrupt.h>
52 #include <debugger/shared/hostutils.h>
53 
54 #include <coreplugin/icore.h>
55 #include <coreplugin/messagebox.h>
56 
57 #include <projectexplorer/devicesupport/deviceprocess.h>
58 #include <projectexplorer/projectexplorer.h>
59 #include <projectexplorer/taskhub.h>
60 
61 #include <app/app_version.h>
62 #include <utils/algorithm.h>
63 #include <utils/hostosinfo.h>
64 #include <utils/qtcassert.h>
65 #include <utils/qtcprocess.h>
66 #include <utils/stringutils.h>
67 #include <utils/temporaryfile.h>
68 
69 #include <QDirIterator>
70 #include <QMessageBox>
71 #include <QProcess>
72 #include <QPushButton>
73 #include <QRegularExpression>
74 #include <QJsonArray>
75 
76 using namespace Core;
77 using namespace ProjectExplorer;
78 using namespace Utils;
79 
80 namespace Debugger {
81 namespace Internal {
82 
83 enum { debugPending = 0 };
84 
85 #define PENDING_DEBUG(s) do { if (debugPending) qDebug() << s; } while (0)
86 
87 #define CB(callback) [this](const DebuggerResponse &r) { callback(r); }
88 
89 #define CHECK_STATE(s) do { checkState(s, __FILE__, __LINE__); } while (0)
90 
currentToken()91 static int &currentToken()
92 {
93     static int token = 0;
94     return token;
95 }
96 
isMostlyHarmlessMessage(const QStringView msg)97 static bool isMostlyHarmlessMessage(const QStringView msg)
98 {
99     return msg == u"warning: GDB: Failed to set controlling terminal: "
100                    "Inappropriate ioctl for device\\n"
101         || msg == u"warning: GDB: Failed to set controlling terminal: "
102                    "Invalid argument\\n";
103 }
104 
showMessageBox(QMessageBox::Icon icon,const QString & title,const QString & text,QMessageBox::StandardButtons buttons)105 static QMessageBox *showMessageBox(QMessageBox::Icon icon,
106                                    const QString &title, const QString &text,
107                                    QMessageBox::StandardButtons buttons)
108 {
109     auto mb = new QMessageBox(icon, title, text, buttons, ICore::dialogParent());
110     mb->setAttribute(Qt::WA_DeleteOnClose);
111     mb->setTextInteractionFlags(Qt::TextSelectableByMouse);
112     mb->show();
113     return mb;
114 }
115 
116 enum class TracepointCaptureType
117 {
118     Address,
119     Caller,
120     Callstack,
121     FilePos,
122     Function,
123     Pid,
124     ProcessName,
125     Tick,
126     Tid,
127     ThreadName,
128     Expression
129 };
130 
131 struct TracepointCaptureData
132 {
133     TracepointCaptureType type;
134     QVariant expression;
135     int start;
136     int end;
137 };
138 
139 const char tracepointCapturePropertyName[] = "GDB.TracepointCapture";
140 
141 ///////////////////////////////////////////////////////////////////////
142 //
143 // GdbEngine
144 //
145 ///////////////////////////////////////////////////////////////////////
146 
GdbEngine()147 GdbEngine::GdbEngine()
148 {
149     setObjectName("GdbEngine");
150     setDebuggerName("GDB");
151 
152     m_gdbOutputCodec = QTextCodec::codecForLocale();
153     m_inferiorOutputCodec = QTextCodec::codecForLocale();
154 
155     m_commandTimer.setSingleShot(true);
156     connect(&m_commandTimer, &QTimer::timeout,
157             this, &GdbEngine::commandTimeout);
158 
159     DebuggerSettings &s = *debuggerSettings();
160     connect(&s.autoDerefPointers, &BaseAspect::changed,
161             this, &GdbEngine::reloadLocals);
162     connect(s.createFullBacktrace.action(), &QAction::triggered,
163             this, &GdbEngine::createFullBacktrace);
164     connect(&s.useDebuggingHelpers, &BaseAspect::changed,
165             this, &GdbEngine::reloadLocals);
166     connect(&s.useDynamicType, &BaseAspect::changed,
167             this, &GdbEngine::reloadLocals);
168 
169     connect(&m_gdbProc, &QtcProcess::errorOccurred,
170             this, &GdbEngine::handleGdbError);
171     connect(&m_gdbProc, &QtcProcess::finished,
172             this, &GdbEngine::handleGdbFinished);
173     connect(&m_gdbProc, &QtcProcess::readyReadStandardOutput,
174             this, &GdbEngine::readGdbStandardOutput);
175     connect(&m_gdbProc, &QtcProcess::readyReadStandardError,
176             this, &GdbEngine::readGdbStandardError);
177 
178     // Output
179     connect(&m_outputCollector, &OutputCollector::byteDelivery,
180             this, &GdbEngine::readDebuggeeOutput);
181 }
182 
~GdbEngine()183 GdbEngine::~GdbEngine()
184 {
185     // Prevent sending error messages afterwards.
186     disconnect();
187 }
188 
failedToStartMessage()189 QString GdbEngine::failedToStartMessage()
190 {
191     return tr("The gdb process failed to start.");
192 }
193 
194 // Parse "~:gdb: unknown target exception 0xc0000139 at 0x77bef04e\n"
195 // and return an exception message
msgWinException(const QString & data,unsigned * exCodeIn=nullptr)196 static QString msgWinException(const QString &data, unsigned *exCodeIn = nullptr)
197 {
198     if (exCodeIn)
199         *exCodeIn = 0;
200     const int exCodePos = data.indexOf("0x");
201     const int blankPos = exCodePos != -1 ? data.indexOf(' ', exCodePos + 1) : -1;
202     const int addressPos = blankPos != -1 ? data.indexOf("0x", blankPos + 1) : -1;
203     if (addressPos < 0)
204         return GdbEngine::tr("An exception was triggered.");
205     const unsigned exCode = data.mid(exCodePos, blankPos - exCodePos).toUInt(nullptr, 0);
206     if (exCodeIn)
207         *exCodeIn = exCode;
208     const quint64 address = data.mid(addressPos).trimmed().toULongLong(nullptr, 0);
209     QString rc;
210     QTextStream str(&rc);
211     str << GdbEngine::tr("An exception was triggered:") << ' ';
212     formatWindowsException(exCode, address, 0, 0, 0, str);
213     str << '.';
214     return rc;
215 }
216 
isNameChar(int c)217 static bool isNameChar(int c)
218 {
219     // could be 'stopped' or 'shlibs-added'
220     return (c >= 'a' && c <= 'z') || c == '-';
221 }
222 
contains(const QString & message,const QString & pattern,int size)223 static bool contains(const QString &message, const QString &pattern, int size)
224 {
225     const int s = message.size();
226     if (s < size)
227         return false;
228     const int pos = message.indexOf(pattern);
229     if (pos == -1)
230         return false;
231     const bool beginFits = pos == 0 || message.at(pos - 1) == '\n';
232     const bool endFits = pos + size == s || message.at(pos + size) == '\n';
233     return beginFits && endFits;
234 }
235 
isGdbConnectionError(const QString & message)236 static bool isGdbConnectionError(const QString &message)
237 {
238     // Handle messages gdb client produces when the target exits (gdbserver)
239     //
240     // we get this as response either to a specific command, e.g.
241     //    31^error,msg="Remote connection closed"
242     // or as informative output:
243     //    &Remote connection closed
244 
245     const char msg1[] = "Remote connection closed";
246     const char msg2[] = "Remote communication error.  Target disconnected.: No error.";
247     const char msg3[] = "Quit";
248 
249     return contains(message, msg1, sizeof(msg1) - 1)
250         || contains(message, msg2, sizeof(msg2) - 1)
251         || contains(message, msg3, sizeof(msg3) - 1);
252 }
253 
handleResponse(const QString & buff)254 void GdbEngine::handleResponse(const QString &buff)
255 {
256     showMessage(buff, LogOutput);
257 
258     if (buff.isEmpty() || buff == "(gdb) ")
259         return;
260 
261     DebuggerOutputParser parser(buff);
262 
263     const int token = parser.readInt();
264 
265     // Next char decides kind of response.
266     switch (parser.readChar().unicode()) {
267         case '*':
268         case '+':
269         case '=': {
270             const QString asyncClass = parser.readString(isNameChar);
271             GdbMi result;
272             while (!parser.isAtEnd()) {
273                 GdbMi data;
274                 if (!parser.isCurrent(',')) {
275                     // happens on archer where we get
276                     // 23^running <NL> *running,thread-id="all" <NL> (gdb)
277                     result.m_type = GdbMi::Tuple;
278                     break;
279                 }
280                 parser.advance(); // skip ','
281                 data.parseResultOrValue(parser);
282                 if (data.isValid()) {
283                     //qDebug() << "parsed result:" << data.toString();
284                     result.addChild(data);
285                     result.m_type = GdbMi::Tuple;
286                 }
287             }
288             handleAsyncOutput(asyncClass, result);
289             break;
290         }
291 
292         case '~': {
293             QString data = parser.readCString();
294             if (data.startsWith("bridgemessage={")) {
295                 // It's already logged.
296                 break;
297             }
298             if (data.startsWith("interpreterresult={")) {
299                 GdbMi allData;
300                 allData.fromStringMultiple(data);
301                 DebuggerResponse response;
302                 response.resultClass = ResultDone;
303                 response.data = allData["interpreterresult"];
304                 response.token = allData["token"].toInt();
305                 handleResultRecord(&response);
306                 break;
307             }
308             if (data.startsWith("interpreterasync={")) {
309                 GdbMi allData;
310                 allData.fromStringMultiple(data);
311                 QString asyncClass = allData["asyncclass"].data();
312                 if (asyncClass == "breakpointmodified")
313                     handleInterpreterBreakpointModified(allData["interpreterasync"]);
314                 break;
315             }
316             if (data.startsWith("tracepointhit={")) {
317                 GdbMi allData;
318                 allData.fromStringMultiple(data);
319                 handleTracepointHit(allData["tracepointhit"]);
320                 break;
321             }
322             if (data.startsWith("tracepointmodified=")) {
323                 GdbMi allData;
324                 allData.fromStringMultiple(data);
325                 handleTracepointModified(allData["tracepointmodified"]);
326                 break;
327             }
328             m_pendingConsoleStreamOutput += data;
329 
330             // Fragile, but it's all we have.
331             if (data.contains("\nNo more reverse-execution history.\n"))
332                 handleBeginOfRecordingReached();
333 
334             // Show some messages to give the impression something happens.
335             if (data.startsWith("Reading symbols from ")) {
336                 showStatusMessage(tr("Reading %1...").arg(data.mid(21)), 1000);
337                 progressPing();
338             } else if (data.startsWith("[New ") || data.startsWith("[Thread ")) {
339                 if (data.endsWith('\n'))
340                     data.chop(1);
341                 progressPing();
342                 showStatusMessage(data, 1000);
343             } else if (data.startsWith("gdb: unknown target exception 0x")) {
344                 // [Windows, most likely some DLL/Entry point not found]:
345                 // "gdb: unknown target exception 0xc0000139 at 0x77bef04e"
346                 // This may be fatal and cause the target to exit later
347                 unsigned exCode;
348                 m_lastWinException = msgWinException(data, &exCode);
349                 showMessage(m_lastWinException, LogMisc);
350                 const Task::TaskType type = isFatalWinException(exCode) ? Task::Error : Task::Warning;
351                 TaskHub::addTask(Task(type, m_lastWinException, {}, -1,
352                                       Constants::TASK_CATEGORY_DEBUGGER_RUNTIME));
353             }
354             break;
355         }
356 
357         case '@': {
358             QString data = parser.readCString();
359             QString msg = data.left(data.size() - 1);
360             showMessage(msg, AppOutput);
361             break;
362         }
363 
364         case '&': {
365             QString data = parser.readCString();
366             // On Windows, the contents seem to depend on the debugger
367             // version and/or OS version used.
368             if (data.startsWith("warning:")) {
369                 showMessage(data.mid(9), AppStuff); // Cut "warning: "
370                 if (data.contains("is not compatible with target architecture"))
371                     m_ignoreNextTrap = true;
372             }
373 
374             m_pendingLogStreamOutput += data;
375 
376             if (isGdbConnectionError(data)) {
377                 notifyInferiorExited();
378                 break;
379             }
380 
381             break;
382         }
383 
384         case '^': {
385             DebuggerResponse response;
386 
387             response.token = token;
388 
389             QString resultClass = parser.readString(isNameChar);
390 
391             if (resultClass == "done")
392                 response.resultClass = ResultDone;
393             else if (resultClass == "running")
394                 response.resultClass = ResultRunning;
395             else if (resultClass == "connected")
396                 response.resultClass = ResultConnected;
397             else if (resultClass == "error")
398                 response.resultClass = ResultError;
399             else if (resultClass == "exit")
400                 response.resultClass = ResultExit;
401             else
402                 response.resultClass = ResultUnknown;
403 
404             if (!parser.isAtEnd()) {
405                 if (parser.isCurrent(',')) {
406                     parser.advance();
407                     response.data.parseTuple_helper(parser);
408                     response.data.m_type = GdbMi::Tuple;
409                     response.data.m_name = "data";
410                 } else {
411                     // Archer has this.
412                     response.data.m_type = GdbMi::Tuple;
413                     response.data.m_name = "data";
414                 }
415             }
416 
417             response.logStreamOutput = m_pendingLogStreamOutput;
418             response.consoleStreamOutput =  m_pendingConsoleStreamOutput;
419             m_pendingLogStreamOutput.clear();
420             m_pendingConsoleStreamOutput.clear();
421 
422             if (response.data.data().isEmpty())
423                 response.data.fromString(response.consoleStreamOutput);
424 
425             handleResultRecord(&response);
426             break;
427         }
428         default: {
429             qDebug() << "UNKNOWN RESPONSE TYPE '" << parser.current() << "'. BUFFER: "
430                      << parser.buffer();
431             break;
432         }
433     }
434 }
435 
handleAsyncOutput(const QString & asyncClass,const GdbMi & result)436 void GdbEngine::handleAsyncOutput(const QString &asyncClass, const GdbMi &result)
437 {
438     if (asyncClass == "stopped") {
439         if (m_inUpdateLocals) {
440             showMessage("UNEXPECTED *stopped NOTIFICATION IGNORED", LogWarning);
441         } else {
442             handleStopResponse(result);
443             m_pendingLogStreamOutput.clear();
444             m_pendingConsoleStreamOutput.clear();
445         }
446     } else if (asyncClass == "running") {
447         if (m_inUpdateLocals) {
448             showMessage("UNEXPECTED *running NOTIFICATION IGNORED", LogWarning);
449         } else {
450             GdbMi threads = result["thread-id"];
451             threadsHandler()->notifyRunning(threads.data());
452             if (runParameters().toolChainAbi.os() == Abi::WindowsOS) {
453                 // NOTE: Each created thread spits out a *running message. We completely ignore them
454                 // on Windows, and handle only numbered responses
455 
456                 // FIXME: Breakpoints on Windows are exceptions which are thrown in newly
457                 // created threads so we have to filter out the running threads messages when
458                 // we request a stop.
459             } else if (state() == InferiorRunOk || state() == EngineSetupRequested) {
460                 // We get multiple *running after thread creation and in Windows terminals.
461                 showMessage(QString("NOTE: INFERIOR STILL RUNNING IN STATE %1.").
462                             arg(DebuggerEngine::stateName(state())));
463             } else {
464                 notifyInferiorRunOk();
465             }
466         }
467     } else if (asyncClass == "library-loaded") {
468         // Archer has 'id="/usr/lib/libdrm.so.2",
469         // target-name="/usr/lib/libdrm.so.2",
470         // host-name="/usr/lib/libdrm.so.2",
471         // symbols-loaded="0"
472 
473         // id="/lib/i386-linux-gnu/libc.so.6"
474         // target-name="/lib/i386-linux-gnu/libc.so.6"
475         // host-name="/lib/i386-linux-gnu/libc.so.6"
476         // symbols-loaded="0",thread-group="i1"
477         QString id = result["id"].data();
478         if (!id.isEmpty())
479             showStatusMessage(tr("Library %1 loaded.").arg(id), 1000);
480         progressPing();
481         Module module;
482         module.startAddress = 0;
483         module.endAddress = 0;
484         module.hostPath = result["host-name"].data();
485         module.modulePath = result["target-name"].data();
486         module.moduleName = QFileInfo(module.hostPath).baseName();
487         modulesHandler()->updateModule(module);
488     } else if (asyncClass == "library-unloaded") {
489         // Archer has 'id="/usr/lib/libdrm.so.2",
490         // target-name="/usr/lib/libdrm.so.2",
491         // host-name="/usr/lib/libdrm.so.2"
492         QString id = result["id"].data();
493         modulesHandler()->removeModule(result["target-name"].data());
494         progressPing();
495         showStatusMessage(tr("Library %1 unloaded.").arg(id), 1000);
496     } else if (asyncClass == "thread-group-added") {
497         // 7.1-symbianelf has "{id="i1"}"
498     } else if (asyncClass == "thread-group-started") {
499         // Archer had only "{id="28902"}" at some point of 6.8.x.
500         // *-started seems to be standard in 7.1, but in early
501         // 7.0.x, there was a *-created instead.
502         progressPing();
503         // 7.1.50 has thread-group-started,id="i1",pid="3529"
504         QString id = result["id"].data();
505         showStatusMessage(tr("Thread group %1 created.").arg(id), 1000);
506         notifyInferiorPid(result["pid"].toProcessHandle());
507         handleThreadGroupCreated(result);
508     } else if (asyncClass == "thread-created") {
509         //"{id="1",group-id="28902"}"
510         QString id = result["id"].data();
511         showStatusMessage(tr("Thread %1 created.").arg(id), 1000);
512         ThreadData thread;
513         thread.id = id;
514         thread.groupId = result["group-id"].data();
515         threadsHandler()->updateThread(thread);
516     } else if (asyncClass == "thread-group-exited") {
517         // Archer has "{id="28902"}"
518         QString id = result["id"].data();
519         showStatusMessage(tr("Thread group %1 exited.").arg(id), 1000);
520         handleThreadGroupExited(result);
521     } else if (asyncClass == "thread-exited") {
522         //"{id="1",group-id="28902"}"
523         QString id = result["id"].data();
524         QString groupid = result["group-id"].data();
525         showStatusMessage(tr("Thread %1 in group %2 exited.")
526                           .arg(id).arg(groupid), 1000);
527         threadsHandler()->removeThread(id);
528     } else if (asyncClass == "thread-selected") {
529         QString id = result["id"].data();
530         showStatusMessage(tr("Thread %1 selected.").arg(id), 1000);
531         //"{id="2"}"
532     } else if (asyncClass == "breakpoint-modified") {
533         // New in FSF gdb since 2011-04-27.
534         // "{bkpt={number="3",type="breakpoint",disp="keep",
535         // enabled="y",addr="<MULTIPLE>",times="1",
536         // original-location="\\",simple_gdbtest_app.cpp\\":135"},
537         // {number="3.1",enabled="y",addr="0x0805ff68",
538         // func="Vector<int>::Vector(int)",
539         // file="simple_gdbtest_app.cpp",
540         // fullname="/data/...line="135"},{number="3.2"...}}.."
541 
542         // Note the leading comma in original-location. Filter it out.
543         // We don't need the field anyway.
544         QString ba = result.toString();
545         ba = '[' + ba.mid(6, ba.size() - 7) + ']';
546         const int pos1 = ba.indexOf(",original-location");
547         const int pos2 = ba.indexOf("\":", pos1 + 2);
548         const int pos3 = ba.indexOf('"', pos2 + 2);
549         ba.remove(pos1, pos3 - pos1 + 1);
550         GdbMi res;
551         res.fromString(ba);
552         BreakHandler *handler = breakHandler();
553         Breakpoint bp;
554         for (const GdbMi &bkpt : res) {
555             const QString nr = bkpt["number"].data();
556             if (nr.contains('.')) {
557                 // A sub-breakpoint.
558                 QTC_ASSERT(bp, continue);
559                 SubBreakpoint loc = bp->findOrCreateSubBreakpoint(nr);
560                 loc->params.updateFromGdbOutput(bkpt);
561                 loc->params.type = bp->type();
562             } else {
563                 // A primary breakpoint.
564                 bp = handler->findBreakpointByResponseId(nr);
565                 if (bp)
566                     bp->updateFromGdbOutput(bkpt);
567             }
568         }
569         if (bp)
570             bp->adjustMarker();
571     } else if (asyncClass == "breakpoint-created") {
572         // "{bkpt={number="1",type="breakpoint",disp="del",enabled="y",
573         //  addr="<PENDING>",pending="main",times="0",
574         //  original-location="main"}}" -- or --
575         // {bkpt={number="2",type="hw watchpoint",disp="keep",enabled="y",
576         // what="*0xbfffed48",times="0",original-location="*0xbfffed48"}}
577         BreakHandler *handler = breakHandler();
578         for (const GdbMi &bkpt : result) {
579             const QString nr = bkpt["number"].data();
580             BreakpointParameters br;
581             br.type = BreakpointByFileAndLine;
582             br.updateFromGdbOutput(bkpt);
583             handler->handleAlienBreakpoint(nr, br);
584         }
585     } else if (asyncClass == "breakpoint-deleted") {
586         // "breakpoint-deleted" "{id="1"}"
587         // New in FSF gdb since 2011-04-27.
588         const QString nr = result["id"].data();
589         // This also triggers when a temporary breakpoint is hit.
590         // We do not really want that, as this loses all information.
591         // FIXME: Use a special marker for this case?
592         // if (!bp.isOneShot()) ... is not sufficient.
593         // It keeps temporary "Jump" breakpoints alive.
594         breakHandler()->removeAlienBreakpoint(nr);
595     } else if (asyncClass == "cmd-param-changed") {
596         // New since 2012-08-09
597         //  "{param="debug remote",value="1"}"
598     } else if (asyncClass == "memory-changed") {
599         // New since 2013
600         //   "{thread-group="i1",addr="0x0918a7a8",len="0x10"}"
601     } else if (asyncClass == "tsv-created") {
602         // New since 2013-02-06
603     } else if (asyncClass == "tsv-modified") {
604         // New since 2013-02-06
605     } else {
606         qDebug() << "IGNORED ASYNC OUTPUT"
607                  << asyncClass << result.toString();
608     }
609 }
610 
readGdbStandardError()611 void GdbEngine::readGdbStandardError()
612 {
613     QString err = QString::fromUtf8(m_gdbProc.readAllStandardError());
614     showMessage("UNEXPECTED GDB STDERR: " + err);
615     if (err == "Undefined command: \"bb\".  Try \"help\".\n")
616         return;
617     if (err.startsWith("BFD: reopening"))
618         return;
619     qWarning() << "Unexpected GDB stderr:" << err;
620 }
621 
readDebuggeeOutput(const QByteArray & ba)622 void GdbEngine::readDebuggeeOutput(const QByteArray &ba)
623 {
624     const QString msg = m_inferiorOutputCodec->toUnicode(ba.constData(), ba.size(),
625                                                          &m_inferiorOutputCodecState);
626 
627     if (msg.startsWith("&\"") && isMostlyHarmlessMessage(QStringView{msg}.mid(2, msg.size() - 4)))
628         showMessage("Mostly harmless terminal warning suppressed.", LogWarning);
629     else
630         showMessage(msg, AppStuff);
631 }
632 
readGdbStandardOutput()633 void GdbEngine::readGdbStandardOutput()
634 {
635     m_commandTimer.start(); // Restart timer.
636 
637     int newstart = 0;
638     int scan = m_inbuffer.size();
639 
640     QByteArray out = m_gdbProc.readAllStandardOutput();
641     m_inbuffer.append(out);
642 
643     // This can trigger when a dialog starts a nested event loop.
644     if (m_busy)
645         return;
646 
647     while (newstart < m_inbuffer.size()) {
648         int start = newstart;
649         int end = m_inbuffer.indexOf('\n', scan);
650         if (end < 0) {
651             m_inbuffer.remove(0, start);
652             return;
653         }
654         newstart = end + 1;
655         scan = newstart;
656         if (end == start)
657             continue;
658         if (m_inbuffer.at(end - 1) == '\r') {
659             --end;
660             if (end == start)
661                 continue;
662         }
663         m_busy = true;
664 
665         QString msg = m_gdbOutputCodec->toUnicode(m_inbuffer.constData() + start, end - start,
666                                                   &m_gdbOutputCodecState);
667 
668         handleResponse(msg);
669         m_busy = false;
670     }
671     m_inbuffer.clear();
672 }
673 
interruptInferior()674 void GdbEngine::interruptInferior()
675 {
676     // A core never runs, so this cannot be called.
677     QTC_ASSERT(!isCoreEngine(), return);
678 
679     CHECK_STATE(InferiorStopRequested);
680 
681     if (usesExecInterrupt()) {
682         runCommand({"-exec-interrupt"});
683     } else {
684         showStatusMessage(tr("Stop requested..."), 5000);
685         showMessage("TRYING TO INTERRUPT INFERIOR");
686         if (HostOsInfo::isWindowsHost() && !m_isQnxGdb) {
687             IDevice::ConstPtr dev = device();
688             QTC_ASSERT(dev, notifyInferiorStopFailed(); return);
689             DeviceProcessSignalOperation::Ptr signalOperation = dev->signalOperation();
690             QTC_ASSERT(signalOperation, notifyInferiorStopFailed(); return);
691             connect(signalOperation.data(), &DeviceProcessSignalOperation::finished,
692                     this, [this, signalOperation](const QString &error) {
693                         if (error.isEmpty()) {
694                             showMessage("Interrupted " + QString::number(inferiorPid()));
695                             notifyInferiorStopOk();
696                         } else {
697                             showMessage(error, LogError);
698                             notifyInferiorStopFailed();
699                         }
700                     });
701             signalOperation->setDebuggerCommand(runParameters().debugger.executable.toString());
702             signalOperation->interruptProcess(inferiorPid());
703         } else {
704             interruptInferior2();
705         }
706     }
707 }
708 
runCommand(const DebuggerCommand & command)709 void GdbEngine::runCommand(const DebuggerCommand &command)
710 {
711     const int token = ++currentToken();
712 
713     DebuggerCommand cmd = command;
714 
715     if (cmd.function.isEmpty()) {
716         showMessage(QString("EMPTY FUNCTION RUN, TOKEN: %1 ARGS: %2")
717                         .arg(token).arg(cmd.args.toString()));
718         QTC_ASSERT(false, return);
719     }
720 
721     if (m_gdbProc.state() != QProcess::Running) {
722         showMessage(QString("NO GDB PROCESS RUNNING, CMD IGNORED: %1 %2")
723             .arg(cmd.function).arg(state()));
724         if (cmd.callback) {
725             DebuggerResponse response;
726             response.resultClass = ResultError;
727             cmd.callback(response);
728         }
729         return;
730     }
731 
732     if (cmd.flags & (NeedsTemporaryStop|NeedsFullStop)) {
733         showMessage("RUNNING NEEDS-STOP COMMAND " + cmd.function);
734         const bool wantContinue = bool(cmd.flags & NeedsTemporaryStop);
735         cmd.flags &= ~(NeedsTemporaryStop|NeedsFullStop);
736         if (state() == InferiorStopRequested) {
737             if (cmd.flags & LosesChild) {
738                 notifyInferiorIll();
739                 return;
740             }
741             showMessage("CHILD ALREADY BEING INTERRUPTED. STILL HOPING.");
742             // Calling shutdown() here breaks all situations where two
743             // NeedsStop commands are issued in quick succession.
744             m_onStop.append(cmd, wantContinue);
745             return;
746         }
747         if (state() == InferiorRunOk) {
748             showStatusMessage(tr("Stopping temporarily."), 1000);
749             m_onStop.append(cmd, wantContinue);
750             setState(InferiorStopRequested);
751             interruptInferior();
752             return;
753         }
754         if (state() != InferiorStopOk)
755             showMessage("UNSAFE STATE FOR QUEUED COMMAND. EXECUTING IMMEDIATELY");
756     }
757 
758     if (!(cmd.flags & Discardable))
759         ++m_nonDiscardableCount;
760 
761     bool isPythonCommand = true;
762     if ((cmd.flags & NativeCommand) || cmd.function.contains('-') || cmd.function.contains(' '))
763         isPythonCommand = false;
764     if (isPythonCommand) {
765         cmd.arg("token", token);
766         cmd.function = "python theDumper." + cmd.function + "(" + cmd.argsToPython() + ")";
767     }
768 
769     QTC_ASSERT(m_gdbProc.state() == QProcess::Running, return);
770 
771     cmd.postTime = QTime::currentTime().msecsSinceStartOfDay();
772     m_commandForToken[token] = cmd;
773     m_flagsForToken[token] = cmd.flags;
774     if (cmd.flags & ConsoleCommand)
775         cmd.function = "-interpreter-exec console \"" + cmd.function + '"';
776     cmd.function = QString::number(token) + cmd.function;
777     showMessage(cmd.function, LogInput);
778 
779     if (m_scheduledTestResponses.contains(token)) {
780         // Fake response for test cases.
781         QString buffer = m_scheduledTestResponses.value(token);
782         buffer.replace("@TOKEN@", QString::number(token));
783         m_scheduledTestResponses.remove(token);
784         showMessage(QString("FAKING TEST RESPONSE (TOKEN: %2, RESPONSE: %3)")
785                     .arg(token).arg(buffer));
786         QMetaObject::invokeMethod(this, [this, buffer] { handleResponse(buffer); });
787     } else {
788         m_gdbProc.write(cmd.function.toUtf8() + "\r\n");
789         if (command.flags & NeedsFlush)
790             m_gdbProc.write("p 0\r\n");
791 
792         // Start Watchdog.
793         if (m_commandTimer.interval() <= 20000)
794             m_commandTimer.setInterval(commandTimeoutTime());
795         // The process can die for external reason between the "-gdb-exit" was
796         // sent and a response could be retrieved. We don't want the watchdog
797         // to bark in that case since the only possible outcome is a dead
798         // process anyway.
799         if (!cmd.function.endsWith("-gdb-exit"))
800             m_commandTimer.start();
801 
802         //if (cmd.flags & LosesChild)
803         //    notifyInferiorIll();
804     }
805 }
806 
commandTimeoutTime() const807 int GdbEngine::commandTimeoutTime() const
808 {
809     const int time = debuggerSettings()->gdbWatchdogTimeout.value();
810     return 1000 * qMax(20, time);
811 }
812 
commandTimeout()813 void GdbEngine::commandTimeout()
814 {
815     QList<int> keys = m_commandForToken.keys();
816     Utils::sort(keys);
817     bool killIt = false;
818     foreach (int key, keys) {
819         const DebuggerCommand &cmd = m_commandForToken.value(key);
820         killIt = true;
821         showMessage(QString::number(key) + ": " + cmd.function);
822     }
823     QStringList commands;
824     foreach (const DebuggerCommand &cmd, m_commandForToken)
825         commands << QString("\"%1\"").arg(cmd.function);
826     if (killIt) {
827         showMessage(QString("TIMED OUT WAITING FOR GDB REPLY. "
828                       "COMMANDS STILL IN PROGRESS: ") + commands.join(", "));
829         int timeOut = m_commandTimer.interval();
830         //m_commandTimer.stop();
831         const QString msg = tr("The gdb process has not responded "
832             "to a command within %n seconds. This could mean it is stuck "
833             "in an endless loop or taking longer than expected to perform "
834             "the operation.\nYou can choose between waiting "
835             "longer or aborting debugging.", nullptr, timeOut / 1000);
836         QMessageBox *mb = showMessageBox(QMessageBox::Critical,
837             tr("GDB Not Responding"), msg,
838             QMessageBox::Ok | QMessageBox::Cancel);
839         mb->button(QMessageBox::Cancel)->setText(tr("Give GDB More Time"));
840         mb->button(QMessageBox::Ok)->setText(tr("Stop Debugging"));
841         if (mb->exec() == QMessageBox::Ok) {
842             showMessage("KILLING DEBUGGER AS REQUESTED BY USER");
843             // This is an undefined state, so we just pull the emergency brake.
844             m_gdbProc.kill();
845             notifyEngineShutdownFinished();
846         } else {
847             showMessage("CONTINUE DEBUGGER AS REQUESTED BY USER");
848         }
849     } else {
850         showMessage(QString("\nNON-CRITICAL TIMEOUT\nCOMMANDS STILL IN PROGRESS: ")
851                     + commands.join(", "));
852     }
853 }
854 
handleResultRecord(DebuggerResponse * response)855 void GdbEngine::handleResultRecord(DebuggerResponse *response)
856 {
857     //qDebug() << "TOKEN:" << response->token
858     //    << " ACCEPTABLE:" << m_oldestAcceptableToken;
859     //qDebug() << "\nRESULT" << response->token << response->toString();
860 
861     int token = response->token;
862     if (token == -1)
863         return;
864 
865     if (!m_commandForToken.contains(token)) {
866         // In theory this should not happen (rather the error should be
867         // reported in the "first" response to the command) in practice it
868         // does. We try to handle a few situations we are aware of gracefully.
869         // Ideally, this code should not be present at all.
870         showMessage(QString("COOKIE FOR TOKEN %1 ALREADY EATEN (%2). "
871                             "TWO RESPONSES FOR ONE COMMAND?").arg(token).
872                     arg(stateName(state())));
873         if (response->resultClass == ResultError) {
874             QString msg = response->data["msg"].data();
875             if (msg == "Cannot find new threads: generic error") {
876                 // Handle a case known to occur on Linux/gdb 6.8 when debugging moc
877                 // with helpers enabled. In this case we get a second response with
878                 // msg="Cannot find new threads: generic error"
879                 showMessage("APPLYING WORKAROUND #1");
880                 AsynchronousMessageBox::critical(tr("Executable Failed"), msg);
881                 showStatusMessage(tr("Process failed to start."));
882                 //shutdown();
883                 notifyInferiorIll();
884             } else if (msg == "\"finish\" not meaningful in the outermost frame.") {
885                 // Handle a case known to appear on GDB 6.4 symbianelf when
886                 // the stack is cut due to access to protected memory.
887                 //showMessage("APPLYING WORKAROUND #2");
888                 notifyInferiorStopOk();
889             } else if (msg.startsWith("Cannot find bounds of current function")) {
890                 // Happens when running "-exec-next" in a function for which
891                 // there is no debug information. Divert to "-exec-next-step"
892                 showMessage("APPLYING WORKAROUND #3");
893                 notifyInferiorStopOk();
894                 executeStepOver(true);
895             } else if (msg.startsWith("Couldn't get registers: No such process.")) {
896                 // Happens on archer-tromey-python 6.8.50.20090910-cvs
897                 // There might to be a race between a process shutting down
898                 // and library load messages.
899                 showMessage("APPLYING WORKAROUND #4");
900                 notifyInferiorStopOk();
901                 //notifyInferiorIll();
902                 //showStatusMessage(tr("Executable failed: %1").arg(msg));
903                 //shutdown();
904                 //AsynchronousMessageBox::critical(tr("Executable Failed"), msg);
905             } else if (msg.contains("Cannot insert breakpoint")) {
906                 // For breakpoints set by address to non-existent addresses we
907                 // might get something like "6^error,msg="Warning:\nCannot insert
908                 // breakpoint 3.\nError accessing memory address 0x34592327:
909                 // Input/output error.\nCannot insert breakpoint 4.\nError
910                 // accessing memory address 0x34592335: Input/output error.\n".
911                 // This should not stop us from proceeding.
912                 // Most notably, that happens after a "6^running" and "*running"
913                 // We are probably sitting at _start and can't proceed as
914                 // long as the breakpoints are enabled.
915                 // FIXME: Should we silently disable the offending breakpoints?
916                 showMessage("APPLYING WORKAROUND #5");
917                 AsynchronousMessageBox::critical(tr("Setting Breakpoints Failed"), msg);
918                 QTC_CHECK(state() == InferiorRunOk);
919                 notifyInferiorSpontaneousStop();
920                 notifyEngineIll();
921             } else if (msg.startsWith("Process record: failed to record execution log.")) {
922                 // Reverse execution recording failed. Full message is something like
923                 // ~"Process record does not support instruction 0xfae64 at address 0x7ffff7dec6f8.\n"
924                 notifyInferiorSpontaneousStop();
925                 handleRecordingFailed();
926             } else if (msg.startsWith("Target multi-thread does not support this command.")) {
927                 notifyInferiorSpontaneousStop();
928                 handleRecordingFailed();
929             } else if (isGdbConnectionError(msg)) {
930                 notifyInferiorExited();
931             } else {
932                 // Windows: Some DLL or some function not found. Report
933                 // the exception now in a box.
934                 if (msg.startsWith("During startup program exited with"))
935                     notifyInferiorExited();
936                 else if (msg.contains("Command aborted."))
937                     notifyInferiorSpontaneousStop();
938                 QString logMsg;
939                 if (!m_lastWinException.isEmpty())
940                     logMsg = m_lastWinException + '\n';
941                 logMsg += msg;
942                 AsynchronousMessageBox::critical(tr("Executable Failed"), logMsg);
943                 showStatusMessage(tr("Executable failed: %1").arg(logMsg));
944             }
945         }
946         return;
947     }
948 
949     DebuggerCommand cmd = m_commandForToken.take(token);
950     const int flags = m_flagsForToken.take(token);
951     if (debuggerSettings()->logTimeStamps.value()) {
952         showMessage(QString("Response time: %1: %2 s")
953             .arg(cmd.function)
954             .arg(QTime::fromMSecsSinceStartOfDay(cmd.postTime).msecsTo(QTime::currentTime()) / 1000.),
955             LogTime);
956     }
957 
958     if (response->token < m_oldestAcceptableToken && (flags & Discardable)) {
959         //showMessage(_("### SKIPPING OLD RESULT") + response.toString());
960         return;
961     }
962 
963     bool isExpectedResult =
964            (response->resultClass == ResultError) // Can always happen.
965         || (response->resultClass == ResultRunning && (flags & RunRequest))
966         || (response->resultClass == ResultExit && (flags & ExitRequest))
967         || (response->resultClass == ResultDone);
968         // ResultDone can almost "always" happen. Known examples are:
969         //  (response->resultClass == ResultDone && cmd.function == "continue")
970         // Happens with some incarnations of gdb 6.8 for "jump to line"
971         //  (response->resultClass == ResultDone && cmd.function.startsWith("jump"))
972         //  (response->resultClass == ResultDone && cmd.function.startsWith("detach"))
973         // Happens when stepping finishes very quickly and issues *stopped and ^done
974         // instead of ^running and *stopped
975         //  (response->resultClass == ResultDone && (cmd.flags & RunRequest));
976 
977     if (!isExpectedResult) {
978         const DebuggerRunParameters &rp = runParameters();
979         Abi abi = rp.toolChainAbi;
980         if (abi.os() == Abi::WindowsOS
981             && cmd.function.startsWith("attach")
982             && (rp.startMode == AttachToLocalProcess || terminal()))
983         {
984             // Ignore spurious 'running' responses to 'attach'.
985         } else {
986             QString rsp = DebuggerResponse::stringFromResultClass(response->resultClass);
987             rsp = "UNEXPECTED RESPONSE '" + rsp + "' TO COMMAND '" + cmd.function + "'";
988             qWarning("%s", qPrintable(rsp));
989             showMessage(rsp);
990         }
991     }
992 
993     if (!(flags & Discardable))
994         --m_nonDiscardableCount;
995 
996     m_inUpdateLocals = (flags & InUpdateLocals);
997 
998     if (cmd.callback)
999         cmd.callback(*response);
1000 
1001     PENDING_DEBUG("MISSING TOKENS: " << m_commandForToken.keys());
1002 
1003     if (m_commandForToken.isEmpty())
1004         m_commandTimer.stop();
1005 }
1006 
acceptsDebuggerCommands() const1007 bool GdbEngine::acceptsDebuggerCommands() const
1008 {
1009     return true;
1010 //    return state() == InferiorStopOk
1011 //        || state() == InferiorUnrunnable;
1012 }
1013 
executeDebuggerCommand(const QString & command)1014 void GdbEngine::executeDebuggerCommand(const QString &command)
1015 {
1016     QTC_CHECK(acceptsDebuggerCommands());
1017     runCommand({command, NativeCommand});
1018 }
1019 
1020 // This is triggered when switching snapshots.
updateAll()1021 void GdbEngine::updateAll()
1022 {
1023     //PENDING_DEBUG("UPDATING ALL\n");
1024     QTC_CHECK(state() == InferiorUnrunnable || state() == InferiorStopOk);
1025     DebuggerCommand cmd(stackCommand(debuggerSettings()->maximalStackDepth.value()));
1026     cmd.callback = [this](const DebuggerResponse &r) { handleStackListFrames(r, false); };
1027     runCommand(cmd);
1028     stackHandler()->setCurrentIndex(0);
1029     runCommand({"-thread-info", CB(handleThreadInfo)});
1030     reloadRegisters();
1031     reloadPeripheralRegisters();
1032     updateLocals();
1033 }
1034 
handleQuerySources(const DebuggerResponse & response)1035 void GdbEngine::handleQuerySources(const DebuggerResponse &response)
1036 {
1037     m_sourcesListUpdating = false;
1038     if (response.resultClass == ResultDone) {
1039         QMap<QString, QString> oldShortToFull = m_shortToFullName;
1040         m_shortToFullName.clear();
1041         m_fullToShortName.clear();
1042         // "^done,files=[{file="../../../../bin/dumper/dumper.cpp",
1043         // fullname="/data5/dev/ide/main/bin/dumper/dumper.cpp"},
1044         for (const GdbMi &item : response.data["files"]) {
1045             GdbMi fileName = item["file"];
1046             if (fileName.data().endsWith("<built-in>"))
1047                 continue;
1048             GdbMi fullName = item["fullname"];
1049             QString file = fileName.data();
1050             QString full;
1051             if (fullName.isValid()) {
1052                 full = cleanupFullName(fullName.data());
1053                 m_fullToShortName[full] = file;
1054             }
1055             m_shortToFullName[file] = full;
1056         }
1057         if (m_shortToFullName != oldShortToFull)
1058             sourceFilesHandler()->setSourceFiles(m_shortToFullName);
1059     }
1060 }
1061 
handleExecuteJumpToLine(const DebuggerResponse & response)1062 void GdbEngine::handleExecuteJumpToLine(const DebuggerResponse &response)
1063 {
1064     if (response.resultClass == ResultRunning) {
1065         // All is fine. Waiting for a *running
1066         // and the temporary breakpoint to be hit.
1067         notifyInferiorRunOk(); // Only needed for gdb < 7.0.
1068     } else if (response.resultClass == ResultError) {
1069         // Could be "Unreasonable jump request" or similar.
1070         QString out = tr("Cannot jump. Stopped.");
1071         QString msg = response.data["msg"].data();
1072         if (!msg.isEmpty())
1073             out += ". " + msg;
1074         showStatusMessage(out);
1075         notifyInferiorRunFailed();
1076     } else if (response.resultClass == ResultDone) {
1077         // This happens on old gdb. Trigger the effect of a '*stopped'.
1078         showStatusMessage(tr("Jumped. Stopped."));
1079         notifyInferiorSpontaneousStop();
1080         handleStop2(response.data);
1081     }
1082 }
1083 
handleExecuteRunToLine(const DebuggerResponse & response)1084 void GdbEngine::handleExecuteRunToLine(const DebuggerResponse &response)
1085 {
1086     if (response.resultClass == ResultRunning) {
1087         // All is fine. Waiting for a *running
1088         // and the temporary breakpoint to be hit.
1089     } else if (response.resultClass == ResultDone) {
1090         // This happens on old gdb (Mac). gdb is not stopped yet,
1091         // but merely accepted the continue.
1092         // >&"continue\n"
1093         // >~"Continuing.\n"
1094         //>~"testArray () at ../simple/app.cpp:241\n"
1095         //>~"241\t    s[1] = \"b\";\n"
1096         //>122^done
1097         showStatusMessage(tr("Target line hit, and therefore stopped."));
1098         notifyInferiorRunOk();
1099     }
1100 }
1101 
isExitedReason(const QString & reason)1102 static bool isExitedReason(const QString &reason)
1103 {
1104     return reason == "exited-normally"   // inferior exited normally
1105         || reason == "exited-signalled"  // inferior exited because of a signal
1106         //|| reason == "signal-received" // inferior received signal
1107         || reason == "exited";           // inferior exited
1108 }
1109 
handleStopResponse(const GdbMi & data)1110 void GdbEngine::handleStopResponse(const GdbMi &data)
1111 {
1112     // Ignore trap on Windows terminals, which results in
1113     // spurious "* stopped" message.
1114     if (m_expectTerminalTrap) {
1115         m_expectTerminalTrap = false;
1116         if ((!data.isValid() || !data["reason"].isValid())
1117                 && Abi::hostAbi().os() == Abi::WindowsOS) {
1118             showMessage("IGNORING TERMINAL SIGTRAP", LogMisc);
1119             return;
1120         }
1121     }
1122 
1123     if (isDying()) {
1124         notifyInferiorStopOk();
1125         return;
1126     }
1127 
1128     GdbMi threads = data["stopped-thread"];
1129     threadsHandler()->notifyStopped(threads.data());
1130 
1131     const QString reason = data["reason"].data();
1132     if (isExitedReason(reason)) {
1133         //   // The user triggered a stop, but meanwhile the app simply exited ...
1134         //    QTC_ASSERT(state() == InferiorStopRequested
1135         //            /*|| state() == InferiorStopRequested_Kill*/,
1136         //               qDebug() << state());
1137         QString msg;
1138         if (reason == "exited") {
1139             const int exitCode = data["exit-code"].toInt();
1140             notifyExitCode(exitCode);
1141             msg = tr("Application exited with exit code %1").arg(exitCode);
1142         } else if (reason == "exited-signalled" || reason == "signal-received") {
1143             msg = tr("Application exited after receiving signal %1")
1144                 .arg(data["signal-name"].toString());
1145         } else {
1146             msg = tr("Application exited normally.");
1147         }
1148         // Only show the message. Ramp-down will be triggered by -thread-group-exited.
1149         showStatusMessage(msg);
1150         return;
1151     }
1152 
1153     if (!m_onStop.isEmpty()) {
1154         notifyInferiorStopOk();
1155         showMessage("HANDLING QUEUED COMMANDS AFTER TEMPORARY STOP", LogMisc);
1156         DebuggerCommandSequence seq = m_onStop;
1157         m_onStop = DebuggerCommandSequence();
1158         for (const DebuggerCommand &cmd : seq.commands())
1159             runCommand(cmd);
1160         if (seq.wantContinue())
1161             continueInferiorInternal();
1162         return;
1163     }
1164 
1165     const QString nr = data["bkptno"].data();
1166     int lineNumber = 0;
1167     QString fullName;
1168     QString function;
1169     QString language;
1170     const GdbMi frame = data["frame"];
1171     if (frame.isValid()) {
1172         const GdbMi lineNumberG = frame["line"];
1173         function = frame["function"].data(); // V4 protocol
1174         if (function.isEmpty())
1175             function = frame["func"].data(); // GDB's *stopped messages
1176         language = frame["language"].data();
1177         if (lineNumberG.isValid()) {
1178             lineNumber = lineNumberG.toInt();
1179             fullName = cleanupFullName(frame["fullname"].data());
1180             if (fullName.isEmpty())
1181                 fullName = frame["file"].data();
1182         } // found line number
1183     } else {
1184         showMessage("INVALID STOPPED REASON", LogWarning);
1185     }
1186 
1187     const FilePath fileName = FilePath::fromString(fullName);
1188 
1189     if (!nr.isEmpty() && frame.isValid()) {
1190         // Use opportunity to update the breakpoint marker position.
1191         if (Breakpoint bp = breakHandler()->findBreakpointByResponseId(nr)) {
1192             const FilePath &bpFileName = bp->fileName();
1193             if (!bpFileName.isEmpty())
1194                 bp->setMarkerFileAndLine(bpFileName, lineNumber);
1195             else if (!fileName.isEmpty())
1196                 bp->setMarkerFileAndLine(fileName, lineNumber);
1197         }
1198     }
1199 
1200     //qDebug() << "BP " << rid << data.toString();
1201     // Quickly set the location marker.
1202     if (lineNumber
1203             && !operatesByInstruction()
1204             && fileName.exists()
1205             && function != "qt_v4TriggeredBreakpointHook"
1206             && function != "qt_qmlDebugMessageAvailable"
1207             && language != "js")
1208         gotoLocation(Location(fileName, lineNumber));
1209 
1210     if (state() == InferiorRunOk) {
1211         // Stop triggered by a breakpoint or otherwise not directly
1212         // initiated by the user.
1213         notifyInferiorSpontaneousStop();
1214     } else if (state() == InferiorRunRequested) {
1215         // Stop triggered by something like "-exec-step\n"
1216         //  "&"Cannot access memory at address 0xbfffedd4\n"
1217         // or, on S40,
1218         //  "*running,thread-id="30""
1219         //  "&"Warning:\n""
1220         //  "&"Cannot insert breakpoint -33.\n"
1221         //  "&"Error accessing memory address 0x11673fc: Input/output error.\n""
1222         // In this case a proper response 94^error,msg="" will follow and
1223         // be handled in the result handler.
1224         // -- or --
1225         // *stopped arriving earlier than ^done response to an -exec-step
1226         notifyInferiorRunOk();
1227         notifyInferiorSpontaneousStop();
1228     } else if (state() == InferiorStopOk) {
1229         // That's expected.
1230     } else if (state() == InferiorStopRequested) {
1231         notifyInferiorStopOk();
1232     } else if (state() == EngineRunRequested) {
1233         // This is gdb 7+'s initial *stopped in response to attach that
1234         // appears before the ^done is seen for local setups.
1235         notifyEngineRunAndInferiorStopOk();
1236         if (terminal()) {
1237             continueInferiorInternal();
1238             return;
1239         }
1240     } else {
1241         QTC_CHECK(false);
1242     }
1243 
1244     CHECK_STATE(InferiorStopOk);
1245 
1246     handleStop1(data);
1247 }
1248 
stopSignal(const Abi & abi)1249 static QString stopSignal(const Abi &abi)
1250 {
1251     return QLatin1String(abi.os() == Abi::WindowsOS ? "SIGTRAP" : "SIGINT");
1252 }
1253 
handleStop1(const GdbMi & data)1254 void GdbEngine::handleStop1(const GdbMi &data)
1255 {
1256     CHECK_STATE(InferiorStopOk);
1257     QTC_ASSERT(!isDying(), return);
1258     const GdbMi frame = data["frame"];
1259     const QString reason = data["reason"].data();
1260 
1261     // This was seen on XP after removing a breakpoint while running
1262     //  >945*stopped,reason="signal-received",signal-name="SIGTRAP",
1263     //  signal-meaning="Trace/breakpoint trap",thread-id="2",
1264     //  frame={addr="0x7c91120f",func="ntdll!DbgUiConnectToDbg",
1265     //  args=[],from="C:\\WINDOWS\\system32\\ntdll.dll"}
1266     // also seen on gdb 6.8-symbianelf without qXfer:libraries:read+;
1267     // FIXME: remote.c parses "loaded" reply. It should be turning
1268     // that into a TARGET_WAITKIND_LOADED. Does it?
1269     // The bandaid here has the problem that it breaks for 'next' over a
1270     // statement that indirectly loads shared libraries
1271     // 6.1.2010: Breaks interrupting inferiors, disabled:
1272      if (m_ignoreNextTrap && reason == "signal-received"
1273              && data["signal-name"].data() == "SIGTRAP") {
1274          m_ignoreNextTrap = false;
1275          continueInferiorInternal();
1276          return;
1277      }
1278 
1279     // Jump over well-known frames.
1280     static int stepCounter = 0;
1281     if (debuggerSettings()->skipKnownFrames.value()) {
1282         if (reason == "end-stepping-range" || reason == "function-finished") {
1283             //showMessage(frame.toString());
1284             QString funcName = frame["function"].data();
1285             QString fileName = frame["file"].data();
1286             if (isLeavableFunction(funcName, fileName)) {
1287                 //showMessage(_("LEAVING ") + funcName);
1288                 ++stepCounter;
1289                 executeStepOut();
1290                 return;
1291             }
1292             if (isSkippableFunction(funcName, fileName)) {
1293                 //showMessage(_("SKIPPING ") + funcName);
1294                 ++stepCounter;
1295                 executeStepIn(false);
1296                 return;
1297             }
1298             //if (stepCounter)
1299             //    qDebug() << "STEPCOUNTER:" << stepCounter;
1300             stepCounter = 0;
1301         }
1302     }
1303 
1304     // Show return value if possible, usually with reason "function-finished".
1305     // *stopped,reason="function-finished",frame={addr="0x080556da",
1306     // func="testReturnValue",args=[],file="/../app.cpp",
1307     // fullname="/../app.cpp",line="1611"},gdb-result-var="$1",
1308     // return-value="{d = 0x808d998}",thread-id="1",stopped-threads="all",
1309     // core="1"
1310     GdbMi resultVar = data["gdb-result-var"];
1311     if (resultVar.isValid())
1312         m_resultVarName = resultVar.data();
1313     else
1314         m_resultVarName.clear();
1315 
1316     if (!m_systemDumpersLoaded) {
1317         m_systemDumpersLoaded = true;
1318         if (m_gdbVersion >= 70400 && debuggerSettings()->loadGdbDumpers.value())
1319             runCommand({"importPlainDumpers on"});
1320         else
1321             runCommand({"importPlainDumpers off"});
1322     }
1323 
1324     handleStop2(data);
1325 }
1326 
handleStop2(const GdbMi & data)1327 void GdbEngine::handleStop2(const GdbMi &data)
1328 {
1329     CHECK_STATE(InferiorStopOk);
1330     QTC_ASSERT(!isDying(), return);
1331 
1332     // A user initiated stop looks like the following. Note that there is
1333     // this extra "stopper thread" created and "properly" reported by gdb.
1334     //
1335     // dNOTE: INFERIOR RUN OK
1336     // dState changed from InferiorRunRequested(10) to InferiorRunOk(11).
1337     // >*running,thread-id="all"
1338     // >=thread-exited,id="11",group-id="i1"
1339     // sThread 11 in group i1 exited
1340     // dState changed from InferiorRunOk(11) to InferiorStopRequested(13).
1341     // dCALL: INTERRUPT INFERIOR
1342     // sStop requested...
1343     // dTRYING TO INTERRUPT INFERIOR
1344     // >=thread-created,id="12",group-id="i1"
1345     // sThread 12 created
1346     // >~"[New Thread 8576.0x1154]\n"
1347     // s[New Thread 8576.0x1154]
1348     // >*running,thread-id="all"
1349     // >~"[Switching to Thread 8576.0x1154]\n"
1350     // >*stopped,reason="signal-received",signal-name="SIGTRAP",
1351     // signal-meaning="Trace/breakpointtrap",frame={addr="0x7c90120f",func=
1352     // "ntdll!DbgUiConnectToDbg",args=[],from="C:\\WINDOWS\\system32\\ntdll.dll"},
1353     // thread-id="12",stopped-threads="all"
1354     // dNOTE: INFERIOR STOP OK
1355     // dState changed from InferiorStopRequested(13) to InferiorStopOk(14).
1356 
1357     const QString reason = data["reason"].data();
1358     const DebuggerRunParameters &rp = runParameters();
1359 
1360     bool isStopperThread = false;
1361 
1362     if (rp.toolChainAbi.os() == Abi::WindowsOS
1363             && terminal()
1364             && reason == "signal-received"
1365             && data["signal-name"].data() == "SIGTRAP")
1366     {
1367         // This is the stopper thread. That also means that the
1368         // reported thread is not the one we'd like to expose
1369         // to the user.
1370         isStopperThread = true;
1371     }
1372 
1373     if (reason == "watchpoint-trigger") {
1374         // *stopped,reason="watchpoint-trigger",wpt={number="2",exp="*0xbfffed40"},
1375         // value={old="1",new="0"},frame={addr="0x00451e1b",
1376         // func="QScopedPointer",args=[{name="this",value="0xbfffed40"},
1377         // {name="p",value="0x0"}],file="x.h",fullname="/home/.../x.h",line="95"},
1378         // thread-id="1",stopped-threads="all",core="2"
1379         const GdbMi wpt = data["wpt"];
1380         const QString rid = wpt["number"].data();
1381         const Breakpoint bp = breakHandler()->findBreakpointByResponseId(rid);
1382         const quint64 bpAddress = wpt["exp"].data().mid(1).toULongLong(nullptr, 0);
1383         QString msg;
1384         if (bp) {
1385             if (bp->type() == WatchpointAtExpression)
1386                 msg = bp->msgWatchpointByExpressionTriggered(bp->expression());
1387             if (bp->type() == WatchpointAtAddress)
1388                 msg = bp->msgWatchpointByAddressTriggered(bpAddress);
1389         }
1390         GdbMi value = data["value"];
1391         GdbMi oldValue = value["old"];
1392         GdbMi newValue = value["new"];
1393         if (oldValue.isValid() && newValue.isValid()) {
1394             msg += ' ';
1395             msg += tr("Value changed from %1 to %2.")
1396                 .arg(oldValue.data()).arg(newValue.data());
1397         }
1398         showStatusMessage(msg);
1399     } else if (reason == "breakpoint-hit") {
1400         GdbMi gNumber = data["bkptno"]; // 'number' or 'bkptno'?
1401         if (!gNumber.isValid())
1402             gNumber = data["number"];
1403         const QString rid = gNumber.data();
1404         const QString threadId = data["thread-id"].data();
1405         m_currentThread = threadId;
1406         if (const Breakpoint bp = breakHandler()->findBreakpointByResponseId(rid)) {
1407             showStatusMessage(bp->msgBreakpointTriggered(threadId));
1408             const QString commands = bp->command().trimmed();
1409             // Can be either c or cont[inue]
1410             const QRegularExpression contExp("(^|\\n)\\s*c(ont(i(n(ue?)?)?)?)?$");
1411             QTC_CHECK(contExp.isValid());
1412             if (contExp.match(commands).hasMatch()) {
1413                 notifyInferiorRunRequested();
1414                 notifyInferiorRunOk();
1415                 return;
1416             }
1417         }
1418     } else {
1419         QString reasontr = msgStopped(reason);
1420         if (reason == "signal-received") {
1421             QString name = data["signal-name"].data();
1422             QString meaning = data["signal-meaning"].data();
1423             // Ignore these as they are showing up regularly when
1424             // stopping debugging.
1425             if (name == stopSignal(rp.toolChainAbi) || rp.expectedSignals.contains(name)) {
1426                 showMessage(name + " CONSIDERED HARMLESS. CONTINUING.");
1427             } else if (m_isQnxGdb && name == "0" && meaning == "Signal 0") {
1428                 showMessage("SIGNAL 0 CONSIDERED BOGUS.");
1429             } else {
1430                 showMessage("HANDLING SIGNAL " + name);
1431                 if (debuggerSettings()->useMessageBoxForSignals.value() && !isStopperThread)
1432                     if (!showStoppedBySignalMessageBox(meaning, name)) {
1433                         showMessage("SIGNAL RECEIVED WHILE SHOWING SIGNAL MESSAGE");
1434                         return;
1435                     }
1436                 if (!name.isEmpty() && !meaning.isEmpty())
1437                     reasontr = msgStoppedBySignal(meaning, name);
1438             }
1439         }
1440         if (reason.isEmpty())
1441             showStatusMessage(msgStopped());
1442         else
1443             showStatusMessage(reasontr);
1444     }
1445 
1446     // Let the event loop run before deciding whether to update the stack.
1447     m_stackNeeded = true; // setTokenBarrier() might reset this.
1448     QTimer::singleShot(0, this, &GdbEngine::handleStop3);
1449 }
1450 
handleStop3()1451 void GdbEngine::handleStop3()
1452 {
1453     DebuggerCommand cmd("-thread-info", Discardable);
1454     cmd.callback = CB(handleThreadInfo);
1455     runCommand(cmd);
1456 }
1457 
handleShowVersion(const DebuggerResponse & response)1458 void GdbEngine::handleShowVersion(const DebuggerResponse &response)
1459 {
1460     showMessage("PARSING VERSION: " + response.toString());
1461     if (response.resultClass == ResultDone) {
1462         bool isMacGdb = false;
1463         int gdbBuildVersion = -1;
1464         m_gdbVersion = 100;
1465         m_isQnxGdb = false;
1466         QString msg = response.consoleStreamOutput;
1467         extractGdbVersion(msg,
1468               &m_gdbVersion, &gdbBuildVersion, &isMacGdb, &m_isQnxGdb);
1469 
1470         // On Mac, FSF GDB does not work sufficiently well,
1471         // and on Linux and Windows we require at least 7.4.1,
1472         // on Android 7.3.1.
1473         bool isSupported = m_gdbVersion >= 70300;
1474         if (isSupported)
1475             showMessage("SUPPORTED GDB VERSION " + msg);
1476         else
1477             showMessage("UNSUPPORTED GDB VERSION " + msg);
1478 
1479         showMessage(QString("USING GDB VERSION: %1, BUILD: %2%3").arg(m_gdbVersion)
1480             .arg(gdbBuildVersion).arg(QLatin1String(isMacGdb ? " (APPLE)" : "")));
1481 
1482         if (usesExecInterrupt())
1483             runCommand({"set target-async on", ConsoleCommand});
1484         else
1485             runCommand({"set target-async off", ConsoleCommand});
1486 
1487         //runCommand("set build-id-verbose 2", ConsoleCommand);
1488     }
1489 }
1490 
handlePythonSetup(const DebuggerResponse & response)1491 void GdbEngine::handlePythonSetup(const DebuggerResponse &response)
1492 {
1493     CHECK_STATE(EngineSetupRequested);
1494     if (response.resultClass == ResultDone) {
1495         GdbMi data = response.data;
1496         watchHandler()->addDumpers(data["dumpers"]);
1497         m_pythonVersion = data["python"].toInt();
1498         if (m_pythonVersion < 20700) {
1499             int pythonMajor = m_pythonVersion / 10000;
1500             int pythonMinor = (m_pythonVersion / 100) % 100;
1501             QString out = "<p>"
1502                 + tr("The selected build of GDB supports Python scripting, "
1503                      "but the used version %1.%2 is not sufficient for "
1504                      "%3. Supported versions are Python 2.7 and 3.x.")
1505                     .arg(pythonMajor).arg(pythonMinor)
1506                     .arg(Core::Constants::IDE_DISPLAY_NAME);
1507             showStatusMessage(out);
1508             AsynchronousMessageBox::critical(tr("Execution Error"), out);
1509         }
1510         loadInitScript();
1511         CHECK_STATE(EngineSetupRequested);
1512         showMessage("ENGINE SUCCESSFULLY STARTED");
1513         setupInferior();
1514     } else {
1515         QString msg = response.data["msg"].data();
1516         if (msg.contains("Python scripting is not supported in this copy of GDB.")) {
1517             QString out1 = "The selected build of GDB does not support Python scripting.";
1518             QString out2 = QStringLiteral("It cannot be used in %1.")
1519                     .arg(Core::Constants::IDE_DISPLAY_NAME);
1520             showStatusMessage(out1 + ' ' + out2);
1521             AsynchronousMessageBox::critical(tr("Execution Error"), out1 + "<br>" + out2);
1522         }
1523         notifyEngineSetupFailed();
1524     }
1525 }
1526 
showExecutionError(const QString & message)1527 void GdbEngine::showExecutionError(const QString &message)
1528 {
1529     AsynchronousMessageBox::critical(tr("Execution Error"),
1530        tr("Cannot continue debugged process:") + '\n' + message);
1531 }
1532 
handleExecuteContinue(const DebuggerResponse & response)1533 void GdbEngine::handleExecuteContinue(const DebuggerResponse &response)
1534 {
1535     CHECK_STATE(InferiorRunRequested);
1536     if (response.resultClass == ResultRunning) {
1537         // All is fine. Waiting for a *running.
1538         notifyInferiorRunOk(); // Only needed for gdb < 7.0.
1539         return;
1540     }
1541     QString msg = response.data["msg"].data();
1542     if (msg.startsWith("Cannot find bounds of current function")) {
1543         notifyInferiorRunFailed();
1544         if (isDying())
1545             return;
1546         CHECK_STATE(InferiorStopOk);
1547         showStatusMessage(tr("Stopped."), 5000);
1548         reloadStack();
1549     } else if (msg.startsWith("Cannot access memory at address")) {
1550         // Happens on single step on ARM prolog and epilogs.
1551     } else if (msg.startsWith("\"finish\" not meaningful in the outermost frame")) {
1552         notifyInferiorRunFailed();
1553         if (isDying())
1554             return;
1555         CHECK_STATE(InferiorStopOk);
1556         // FIXME: Fix translation in master.
1557         showStatusMessage(msg, 5000);
1558         gotoCurrentLocation();
1559     } else if (msg.startsWith("Cannot execute this command while the selected thread is running.")) {
1560         showExecutionError(msg);
1561         notifyInferiorRunFailed() ;
1562     } else {
1563         showExecutionError(msg);
1564         notifyInferiorIll();
1565     }
1566 }
1567 
fullName(const QString & fileName)1568 QString GdbEngine::fullName(const QString &fileName)
1569 {
1570     if (fileName.isEmpty())
1571         return QString();
1572     QTC_ASSERT(!m_sourcesListUpdating, /* */);
1573     return m_shortToFullName.value(fileName, QString());
1574 }
1575 
cleanupFullName(const QString & fileName)1576 QString GdbEngine::cleanupFullName(const QString &fileName)
1577 {
1578     QString cleanFilePath = fileName;
1579 
1580     // Gdb running on windows often delivers "fullnames" which
1581     // (a) have no drive letter and (b) are not normalized.
1582     if (Abi::hostAbi().os() == Abi::WindowsOS) {
1583         if (fileName.isEmpty())
1584             return QString();
1585         QFileInfo fi(fileName);
1586         if (fi.isReadable())
1587             cleanFilePath = QDir::cleanPath(fi.absoluteFilePath());
1588     }
1589 
1590     if (!debuggerSettings()->autoEnrichParameters.value())
1591         return cleanFilePath;
1592 
1593     const QString sysroot = runParameters().sysRoot.toString();
1594     if (QFileInfo(cleanFilePath).isReadable())
1595         return cleanFilePath;
1596     if (!sysroot.isEmpty() && fileName.startsWith('/')) {
1597         cleanFilePath = sysroot + fileName;
1598         if (QFileInfo(cleanFilePath).isReadable())
1599             return cleanFilePath;
1600     }
1601     if (m_baseNameToFullName.isEmpty()) {
1602         QString debugSource = sysroot + "/usr/src/debug";
1603         if (QFileInfo(debugSource).isDir()) {
1604             QDirIterator it(debugSource, QDirIterator::Subdirectories);
1605             while (it.hasNext()) {
1606                 it.next();
1607                 QString name = it.fileName();
1608                 if (!name.startsWith('.')) {
1609                     QString path = it.filePath();
1610                     m_baseNameToFullName.insert(name, path);
1611                 }
1612             }
1613         }
1614     }
1615 
1616     cleanFilePath.clear();
1617     const QString base = FilePath::fromString(fileName).fileName();
1618 
1619     QMultiMap<QString, QString>::const_iterator jt = m_baseNameToFullName.constFind(base);
1620     while (jt != m_baseNameToFullName.constEnd() && jt.key() == base) {
1621         // FIXME: Use some heuristics to find the "best" match.
1622         return jt.value();
1623         //++jt;
1624     }
1625 
1626     return cleanFilePath;
1627 }
1628 
shutdownInferior()1629 void GdbEngine::shutdownInferior()
1630 {
1631     CHECK_STATE(InferiorShutdownRequested);
1632     if (runParameters().startMode == AttachToCore) {
1633         notifyInferiorShutdownFinished();
1634         return;
1635     }
1636     DebuggerCommand cmd;
1637     cmd.function = QLatin1String(runParameters().closeMode == DetachAtClose ? "detach " : "kill ");
1638     cmd.callback = CB(handleInferiorShutdown);
1639     cmd.flags = NeedsTemporaryStop|LosesChild;
1640     runCommand(cmd);
1641 }
1642 
handleInferiorShutdown(const DebuggerResponse & response)1643 void GdbEngine::handleInferiorShutdown(const DebuggerResponse &response)
1644 {
1645     if (response.resultClass == ResultDone) {
1646         // We'll get async thread-group-exited responses to which we react.
1647         // Nothing to do here.
1648         // notifyInferiorShutdownFinished();
1649         return;
1650     }
1651     // "kill" got stuck, gdb was kill -9'd, or similar.
1652     CHECK_STATE(InferiorShutdownRequested);
1653     QString msg = response.data["msg"].data();
1654     if (msg.contains(": No such file or directory.")) {
1655         // This happens when someone removed the binary behind our back.
1656         // It is not really an error from a user's point of view.
1657         showMessage("NOTE: " + msg);
1658     } else if (m_gdbProc.state() == QProcess::Running) {
1659         AsynchronousMessageBox::critical(tr("Failed to Shut Down Application"),
1660                                          msgInferiorStopFailed(msg));
1661     }
1662     notifyInferiorShutdownFinished();
1663 }
1664 
handleGdbExit(const DebuggerResponse & response)1665 void GdbEngine::handleGdbExit(const DebuggerResponse &response)
1666 {
1667     if (response.resultClass == ResultExit) {
1668         showMessage("GDB CLAIMS EXIT; WAITING");
1669         // Don't set state here, this will be handled in handleGdbFinished()
1670         //notifyEngineShutdownOk();
1671     } else {
1672         QString msg = msgGdbStopFailed(response.data["msg"].data());
1673         qDebug() << QString("GDB WON'T EXIT (%1); KILLING IT").arg(msg);
1674         showMessage(QString("GDB WON'T EXIT (%1); KILLING IT").arg(msg));
1675         m_gdbProc.kill();
1676         notifyEngineShutdownFinished();
1677     }
1678 }
1679 
setLinuxOsAbi()1680 void GdbEngine::setLinuxOsAbi()
1681 {
1682     // In case GDB has multiple supported targets, the default osabi can be Cygwin.
1683     if (!HostOsInfo::isWindowsHost())
1684         return;
1685     const DebuggerRunParameters &rp = runParameters();
1686     bool isElf = (rp.toolChainAbi.binaryFormat() == Abi::ElfFormat);
1687     if (!isElf && !rp.inferior.executable.isEmpty()) {
1688         isElf = Utils::anyOf(Abi::abisOfBinary(rp.inferior.executable), [](const Abi &abi) {
1689             return abi.binaryFormat() == Abi::ElfFormat;
1690         });
1691     }
1692     if (isElf)
1693         runCommand({"set osabi GNU/Linux"});
1694 }
1695 
detachDebugger()1696 void GdbEngine::detachDebugger()
1697 {
1698     CHECK_STATE(InferiorStopOk);
1699     QTC_CHECK(runParameters().startMode != AttachToCore);
1700     DebuggerCommand cmd("detach", NativeCommand | ExitRequest);
1701     cmd.callback = [this](const DebuggerResponse &) {
1702         CHECK_STATE(InferiorStopOk);
1703         notifyInferiorExited();
1704     };
1705     runCommand(cmd);
1706 }
1707 
handleThreadGroupCreated(const GdbMi & result)1708 void GdbEngine::handleThreadGroupCreated(const GdbMi &result)
1709 {
1710     QString groupId = result["id"].data();
1711     QString pid = result["pid"].data();
1712     threadsHandler()->notifyGroupCreated(groupId, pid);
1713 }
1714 
handleThreadGroupExited(const GdbMi & result)1715 void GdbEngine::handleThreadGroupExited(const GdbMi &result)
1716 {
1717     QString groupId = result["id"].data();
1718     if (threadsHandler()->notifyGroupExited(groupId)) {
1719         const int exitCode = result["exit-code"].toInt();
1720         notifyExitCode(exitCode);
1721         if (m_rerunPending)
1722             m_rerunPending = false;
1723         else
1724             notifyInferiorExited();
1725     }
1726 }
1727 
msgNoGdbBinaryForToolChain(const Abi & tc)1728 static QString msgNoGdbBinaryForToolChain(const Abi &tc)
1729 {
1730     return GdbEngine::tr("There is no GDB binary available for binaries in format \"%1\".")
1731         .arg(tc.toString());
1732 }
1733 
hasCapability(unsigned cap) const1734 bool GdbEngine::hasCapability(unsigned cap) const
1735 {
1736     if (cap & (AutoDerefPointersCapability
1737                | DisassemblerCapability
1738                | RegisterCapability
1739                | ShowMemoryCapability
1740                | CreateFullBacktraceCapability
1741                | AddWatcherCapability
1742                | ShowModuleSymbolsCapability
1743                | ShowModuleSectionsCapability
1744                | OperateByInstructionCapability
1745                | WatchComplexExpressionsCapability
1746                | MemoryAddressCapability
1747                | AdditionalQmlStackCapability)) {
1748         return true;
1749     }
1750 
1751     if (runParameters().startMode == AttachToCore)
1752         return false;
1753 
1754     return cap & (JumpToLineCapability
1755                   | ReloadModuleCapability
1756                   | ReloadModuleSymbolsCapability
1757                   | BreakOnThrowAndCatchCapability
1758                   | BreakConditionCapability
1759                   | BreakIndividualLocationsCapability
1760                   | TracePointCapability
1761                   | ReturnFromFunctionCapability
1762                   | WatchpointByAddressCapability
1763                   | WatchpointByExpressionCapability
1764                   | AddWatcherWhileRunningCapability
1765                   | WatchWidgetsCapability
1766                   | CatchCapability
1767                   | RunToLineCapability
1768                   | MemoryAddressCapability
1769                   | AdditionalQmlStackCapability
1770                   | ResetInferiorCapability
1771                   | SnapshotCapability
1772                   | ReverseSteppingCapability);
1773 }
1774 
1775 
continueInferiorInternal()1776 void GdbEngine::continueInferiorInternal()
1777 {
1778     CHECK_STATE(InferiorStopOk);
1779     notifyInferiorRunRequested();
1780     showStatusMessage(tr("Running requested..."), 5000);
1781     CHECK_STATE(InferiorRunRequested);
1782     if (isNativeMixedActiveFrame()) {
1783         DebuggerCommand cmd("executeContinue", RunRequest);
1784         cmd.callback = CB(handleExecuteContinue);
1785         runCommand(cmd);
1786     } else {
1787         DebuggerCommand cmd("-exec-continue");
1788         cmd.flags = RunRequest | NeedsFlush;
1789         cmd.callback = CB(handleExecuteContinue);
1790         runCommand(cmd);
1791     }
1792 }
1793 
continueInferior()1794 void GdbEngine::continueInferior()
1795 {
1796     CHECK_STATE(InferiorStopOk);
1797     setTokenBarrier();
1798     continueInferiorInternal();
1799 }
1800 
executeStepIn(bool byInstruction)1801 void GdbEngine::executeStepIn(bool byInstruction)
1802 {
1803     CHECK_STATE(InferiorStopOk);
1804     setTokenBarrier();
1805     notifyInferiorRunRequested();
1806     showStatusMessage(tr("Step requested..."), 5000);
1807 
1808     DebuggerCommand cmd;
1809     if (isNativeMixedActiveFrame()) {
1810         cmd.flags = RunRequest;
1811         cmd.function = "executeStep";
1812         cmd.callback = CB(handleExecuteStep);
1813     } else if (byInstruction) {
1814         cmd.flags = RunRequest|NeedsFlush;
1815         cmd.function = "-exec-step-instruction";
1816         if (isReverseDebugging())
1817             cmd.function += "--reverse";
1818         cmd.callback = CB(handleExecuteContinue);
1819     } else {
1820         cmd.flags = RunRequest|NeedsFlush;
1821         cmd.function = "-exec-step";
1822         if (isReverseDebugging())
1823             cmd.function += " --reverse";
1824         cmd.callback = CB(handleExecuteStep);
1825     }
1826     runCommand(cmd);
1827 }
1828 
handleExecuteStep(const DebuggerResponse & response)1829 void GdbEngine::handleExecuteStep(const DebuggerResponse &response)
1830 {
1831     if (response.resultClass == ResultDone) {
1832         // Step was finishing too quick, and a '*stopped' messages should
1833         // have preceded it, so just ignore this result.
1834         QTC_CHECK(state() == InferiorStopOk);
1835         return;
1836     }
1837     CHECK_STATE(InferiorRunRequested);
1838     if (response.resultClass == ResultRunning) {
1839         // All is fine. Waiting for a *running.
1840         notifyInferiorRunOk(); // Only needed for gdb < 7.0.
1841         return;
1842     }
1843     QString msg = response.data["msg"].data();
1844     if (msg.startsWith("Cannot find bounds of current function")
1845             || msg.contains("Error accessing memory address")
1846             || msg.startsWith("Cannot access memory at address")) {
1847         // On S40: "40^error,msg="Warning:\nCannot insert breakpoint -39.\n"
1848         //" Error accessing memory address 0x11673fc: Input/output error.\n"
1849         notifyInferiorRunFailed();
1850         if (isDying())
1851             return;
1852         executeStepIn(true); // Fall back to instruction-wise stepping.
1853     } else if (msg.startsWith("Cannot execute this command while the selected thread is running.")) {
1854         showExecutionError(msg);
1855         notifyInferiorRunFailed();
1856     } else if (msg.startsWith("warning: SuspendThread failed")) {
1857         // On Win: would lead to "PC register is not available" or "\312"
1858         continueInferiorInternal();
1859     } else {
1860         showExecutionError(msg);
1861         notifyInferiorIll();
1862     }
1863 }
1864 
executeStepOut()1865 void GdbEngine::executeStepOut()
1866 {
1867     CHECK_STATE(InferiorStopOk);
1868     runCommand({"-stack-select-frame 0", Discardable});
1869     setTokenBarrier();
1870     notifyInferiorRunRequested();
1871     showStatusMessage(tr("Finish function requested..."), 5000);
1872     if (isNativeMixedActiveFrame()) {
1873         runCommand({"executeStepOut", RunRequest});
1874     } else {
1875         // -exec-finish in 'main' results (correctly) in
1876         //  40^error,msg="\"finish\" not meaningful in the outermost frame."
1877         // However, this message does not seem to get flushed before
1878         // anything else happen - i.e. "never". Force some extra output.
1879         runCommand({"-exec-finish", RunRequest|NeedsFlush, CB(handleExecuteContinue)});
1880     }
1881 }
1882 
executeStepOver(bool byInstruction)1883 void GdbEngine::executeStepOver(bool byInstruction)
1884 {
1885     CHECK_STATE(InferiorStopOk);
1886     setTokenBarrier();
1887     notifyInferiorRunRequested();
1888     showStatusMessage(tr("Step next requested..."), 5000);
1889 
1890     DebuggerCommand cmd;
1891     cmd.flags = RunRequest;
1892     if (isNativeMixedActiveFrame()) {
1893         cmd.function = "executeNext";
1894     } else if (byInstruction) {
1895         cmd.function = "-exec-next-instruction";
1896         if (isReverseDebugging())
1897             cmd.function += " --reverse";
1898         cmd.callback = CB(handleExecuteContinue);
1899     } else {
1900         cmd.function = "-exec-next";
1901         if (isReverseDebugging())
1902             cmd.function += " --reverse";
1903         cmd.callback = CB(handleExecuteNext);
1904     }
1905     runCommand(cmd);
1906 }
1907 
handleExecuteNext(const DebuggerResponse & response)1908 void GdbEngine::handleExecuteNext(const DebuggerResponse &response)
1909 {
1910     if (response.resultClass == ResultDone) {
1911         // Step was finishing too quick, and a '*stopped' messages should
1912         // have preceded it, so just ignore this result.
1913         CHECK_STATE(InferiorStopOk);
1914         return;
1915     }
1916     CHECK_STATE(InferiorRunRequested);
1917     if (response.resultClass == ResultRunning) {
1918         // All is fine. Waiting for a *running.
1919         notifyInferiorRunOk(); // Only needed for gdb < 7.0.
1920         return;
1921     }
1922     CHECK_STATE(InferiorStopOk);
1923     QString msg = response.data["msg"].data();
1924     if (msg.startsWith("Cannot find bounds of current function")
1925             || msg.contains("Error accessing memory address ")) {
1926         notifyInferiorRunFailed();
1927         if (!isDying())
1928             executeStepOver(true); // Fall back to instruction-wise stepping.
1929     } else if (msg.startsWith("Cannot execute this command while the selected thread is running.")) {
1930         showExecutionError(msg);
1931         notifyInferiorRunFailed();
1932     } else if (msg.startsWith("Target multi-thread does not support this command.")) {
1933         notifyInferiorRunFailed();
1934         handleRecordingFailed();
1935     } else {
1936         AsynchronousMessageBox::warning(tr("Execution Error"),
1937            tr("Cannot continue debugged process:") + '\n' + msg);
1938         //notifyInferiorIll();
1939     }
1940 }
1941 
addressSpec(quint64 address)1942 static QString addressSpec(quint64 address)
1943 {
1944     return "*0x" + QString::number(address, 16);
1945 }
1946 
executeRunToLine(const ContextData & data)1947 void GdbEngine::executeRunToLine(const ContextData &data)
1948 {
1949     CHECK_STATE(InferiorStopOk);
1950     setTokenBarrier();
1951     notifyInferiorRunRequested();
1952     showStatusMessage(tr("Run to line %1 requested...").arg(data.lineNumber), 5000);
1953 #if 1
1954     QString loc;
1955     if (data.address)
1956         loc = addressSpec(data.address);
1957     else
1958         loc = '"' + breakLocation(data.fileName.toString()) + '"' + ':' + QString::number(data.lineNumber);
1959     runCommand({"tbreak " + loc});
1960 
1961     runCommand({"continue", NativeCommand|RunRequest, CB(handleExecuteRunToLine)});
1962 #else
1963     // Seems to jump to unpredicatable places. Observed in the manual
1964     // tests in the Foo::Foo() constructor with both gdb 6.8 and 7.1.
1965     QString args = '"' + breakLocation(fileName) + '"' + ':' + QString::number(lineNumber);
1966     runCommand("-exec-until " + args, RunRequest, CB(handleExecuteContinue));
1967 #endif
1968 }
1969 
executeRunToFunction(const QString & functionName)1970 void GdbEngine::executeRunToFunction(const QString &functionName)
1971 {
1972     CHECK_STATE(InferiorStopOk);
1973     setTokenBarrier();
1974     runCommand({"-break-insert -t " + functionName});
1975     showStatusMessage(tr("Run to function %1 requested...").arg(functionName), 5000);
1976     continueInferiorInternal();
1977 }
1978 
executeJumpToLine(const ContextData & data)1979 void GdbEngine::executeJumpToLine(const ContextData &data)
1980 {
1981     CHECK_STATE(InferiorStopOk);
1982     QString loc;
1983     if (data.address)
1984         loc = addressSpec(data.address);
1985     else
1986         loc = '"' + breakLocation(data.fileName.toString()) + '"' + ':' + QString::number(data.lineNumber);
1987     runCommand({"tbreak " + loc});
1988     notifyInferiorRunRequested();
1989 
1990     runCommand({"jump " + loc, RunRequest, CB(handleExecuteJumpToLine)});
1991     // will produce something like
1992     //  &"jump \"/home/apoenitz/dev/work/test1/test1.cpp\":242"
1993     //  ~"Continuing at 0x4058f3."
1994     //  ~"run1 (argc=1, argv=0x7fffbf1f5538) at test1.cpp:242"
1995     //  ~"242\t x *= 2;"
1996     //  23^done"
1997 }
1998 
executeReturn()1999 void GdbEngine::executeReturn()
2000 {
2001     CHECK_STATE(InferiorStopOk);
2002     setTokenBarrier();
2003     notifyInferiorRunRequested();
2004     showStatusMessage(tr("Immediate return from function requested..."), 5000);
2005     runCommand({"-exec-return", RunRequest, CB(handleExecuteReturn)});
2006 }
2007 
executeRecordReverse(bool record)2008 void GdbEngine::executeRecordReverse(bool record)
2009 {
2010     if (record)
2011         runCommand({"record full"});
2012     else
2013         runCommand({"record stop"});
2014 }
2015 
handleExecuteReturn(const DebuggerResponse & response)2016 void GdbEngine::handleExecuteReturn(const DebuggerResponse &response)
2017 {
2018     if (response.resultClass == ResultDone) {
2019         notifyInferiorStopOk();
2020         updateAll();
2021         return;
2022     }
2023     notifyInferiorRunFailed();
2024 }
2025 
2026 /*!
2027     Discards the results of all pending watch-updating commands.
2028 
2029     This function is called at the beginning of all step, next, finish, and so on,
2030     debugger functions.
2031     If non-watch-updating commands with call-backs are still in the pipe,
2032     it will complain.
2033 */
setTokenBarrier()2034 void GdbEngine::setTokenBarrier()
2035 {
2036     //QTC_ASSERT(m_nonDiscardableCount == 0, /**/);
2037     bool good = true;
2038     for (auto it = m_commandForToken.cbegin(), end = m_commandForToken.cend(); it != end; ++it) {
2039         if (!(m_flagsForToken.value(it.key()) & Discardable)) {
2040             qDebug() << "TOKEN: " << it.key() << "CMD:" << it.value().function;
2041             good = false;
2042         }
2043     }
2044     QTC_ASSERT(good, return);
2045     PENDING_DEBUG("\n--- token barrier ---\n");
2046     showMessage("--- token barrier ---", LogMiscInput);
2047     if (debuggerSettings()->logTimeStamps.value())
2048         showMessage(LogWindow::logTimeStamp(), LogMiscInput);
2049     m_oldestAcceptableToken = currentToken();
2050     m_stackNeeded = false;
2051 }
2052 
2053 
2054 //////////////////////////////////////////////////////////////////////
2055 //
2056 // Breakpoint specific stuff
2057 //
2058 //////////////////////////////////////////////////////////////////////
2059 
breakLocation(const QString & file) const2060 QString GdbEngine::breakLocation(const QString &file) const
2061 {
2062     QString where = m_fullToShortName.value(file);
2063     if (where.isEmpty())
2064         return FilePath::fromString(file).fileName();
2065     return where;
2066 }
2067 
breakpointLocation(const BreakpointParameters & data)2068 QString GdbEngine::breakpointLocation(const BreakpointParameters &data)
2069 {
2070     QTC_ASSERT(data.type != UnknownBreakpointType, return QString());
2071     // FIXME: Non-GCC-runtime
2072     if (data.type == BreakpointAtThrow)
2073         return QLatin1String("__cxa_throw");
2074     if (data.type == BreakpointAtCatch)
2075         return QLatin1String("__cxa_begin_catch");
2076     if (data.type == BreakpointAtMain)
2077         return mainFunction();
2078     if (data.type == BreakpointByFunction)
2079         return "--function \"" + data.functionName + '"';
2080     if (data.type == BreakpointByAddress)
2081         return addressSpec(data.address);
2082 
2083     BreakpointPathUsage usage = data.pathUsage;
2084     if (usage == BreakpointPathUsageEngineDefault)
2085         usage = BreakpointUseShortPath;
2086 
2087     const QString fileName = usage == BreakpointUseFullPath
2088         ? data.fileName.toString() : breakLocation(data.fileName.toString());
2089     // The argument is simply a C-quoted version of the argument to the
2090     // non-MI "break" command, including the "original" quoting it wants.
2091     return "\"\\\"" + GdbMi::escapeCString(fileName) + "\\\":"
2092         + QString::number(data.lineNumber) + '"';
2093 }
2094 
breakpointLocation2(const BreakpointParameters & data)2095 QString GdbEngine::breakpointLocation2(const BreakpointParameters &data)
2096 {
2097     BreakpointPathUsage usage = data.pathUsage;
2098     if (usage == BreakpointPathUsageEngineDefault)
2099         usage = BreakpointUseShortPath;
2100 
2101     const QString fileName = usage == BreakpointUseFullPath
2102         ? data.fileName.toString() : breakLocation(data.fileName.toString());
2103     return GdbMi::escapeCString(fileName) + ':' + QString::number(data.lineNumber);
2104 }
2105 
handleInsertInterpreterBreakpoint(const DebuggerResponse & response,const Breakpoint & bp)2106 void GdbEngine::handleInsertInterpreterBreakpoint(const DebuggerResponse &response,
2107                                                   const Breakpoint &bp)
2108 {
2109     QTC_ASSERT(bp, return);
2110     const bool pending = response.data["pending"].toInt();
2111     if (pending) {
2112         notifyBreakpointInsertOk(bp);
2113     } else {
2114         bp->setResponseId(response.data["number"].data());
2115         bp->updateFromGdbOutput(response.data);
2116         notifyBreakpointInsertOk(bp);
2117     }
2118 }
2119 
handleInterpreterBreakpointModified(const GdbMi & data)2120 void GdbEngine::handleInterpreterBreakpointModified(const GdbMi &data)
2121 {
2122     int modelId = data["modelid"].toInt();
2123     Breakpoint bp = breakHandler()->findBreakpointByModelId(modelId);
2124     QTC_ASSERT(bp, return);
2125     bp->updateFromGdbOutput(data);
2126 }
2127 
handleWatchInsert(const DebuggerResponse & response,const Breakpoint & bp)2128 void GdbEngine::handleWatchInsert(const DebuggerResponse &response, const Breakpoint &bp)
2129 {
2130     if (bp && response.resultClass == ResultDone) {
2131         // "Hardware watchpoint 2: *0xbfffed40\n"
2132         QString ba = response.consoleStreamOutput;
2133         GdbMi wpt = response.data["wpt"];
2134         if (wpt.isValid()) {
2135             // Mac yields:
2136             //>32^done,wpt={number="4",exp="*4355182176"}
2137             bp->setResponseId(wpt["number"].data());
2138             QString exp = wpt["exp"].data();
2139             if (exp.startsWith('*'))
2140                 bp->setAddress(exp.mid(1).toULongLong(nullptr, 0));
2141             QTC_CHECK(!bp->needsChange());
2142             notifyBreakpointInsertOk(bp);
2143         } else if (ba.startsWith("Hardware watchpoint ")
2144                 || ba.startsWith("Watchpoint ")) {
2145             // Non-Mac: "Hardware watchpoint 2: *0xbfffed40\n"
2146             const int end = ba.indexOf(':');
2147             const int begin = ba.lastIndexOf(' ', end) + 1;
2148             const QString address = ba.mid(end + 2).trimmed();
2149             bp->setResponseId(ba.mid(begin, end - begin));
2150             if (address.startsWith('*'))
2151                 bp->setAddress(address.mid(1).toULongLong(nullptr, 0));
2152             QTC_CHECK(!bp->needsChange());
2153             notifyBreakpointInsertOk(bp);
2154         } else {
2155             showMessage("CANNOT PARSE WATCHPOINT FROM " + ba);
2156         }
2157     }
2158 }
2159 
handleCatchInsert(const DebuggerResponse & response,const Breakpoint & bp)2160 void GdbEngine::handleCatchInsert(const DebuggerResponse &response, const Breakpoint &bp)
2161 {
2162     if (response.resultClass == ResultDone)
2163         notifyBreakpointInsertOk(bp);
2164 }
2165 
handleBkpt(const GdbMi & bkpt,const Breakpoint & bp)2166 void GdbEngine::handleBkpt(const GdbMi &bkpt, const Breakpoint &bp)
2167 {
2168     QTC_ASSERT(bp, return);
2169     const bool usePseudoTracepoints = debuggerSettings()->usePseudoTracepoints.value();
2170     const QString nr = bkpt["number"].data();
2171     if (nr.contains('.')) {
2172         // A sub-breakpoint.
2173         SubBreakpoint sub = bp->findOrCreateSubBreakpoint(nr);
2174         QTC_ASSERT(sub, return);
2175         sub->params.updateFromGdbOutput(bkpt);
2176         sub->params.type = bp->type();
2177         if (usePseudoTracepoints && bp->isTracepoint()) {
2178             sub->params.tracepoint = true;
2179             sub->params.message = bp->message();
2180         }
2181         return;
2182     }
2183 
2184     // The MI output format might change, see
2185     // http://permalink.gmane.org/gmane.comp.gdb.patches/83936
2186     const GdbMi locations = bkpt["locations"];
2187     if (locations.isValid()) {
2188         for (const GdbMi &location : locations) {
2189             // A sub-breakpoint.
2190             const QString subnr = location["number"].data();
2191             SubBreakpoint sub = bp->findOrCreateSubBreakpoint(subnr);
2192             QTC_ASSERT(sub, return);
2193             sub->params.updateFromGdbOutput(location);
2194             sub->params.type = bp->type();
2195             if (usePseudoTracepoints && bp->isTracepoint()) {
2196                 sub->params.tracepoint = true;
2197                 sub->params.message = bp->message();
2198             }
2199         }
2200     }
2201 
2202     // A (the?) primary breakpoint.
2203     bp->setResponseId(nr);
2204     bp->updateFromGdbOutput(bkpt);
2205     if (usePseudoTracepoints && bp->isTracepoint())
2206         bp->setMessage(bp->requestedParameters().message);
2207 }
2208 
handleBreakInsert1(const DebuggerResponse & response,const Breakpoint & bp)2209 void GdbEngine::handleBreakInsert1(const DebuggerResponse &response, const Breakpoint &bp)
2210 {
2211     QTC_ASSERT(bp, return);
2212     if (bp->state() == BreakpointRemoveRequested) {
2213         if (response.resultClass == ResultDone) {
2214             // This delete was deferred. Act now.
2215             const GdbMi mainbkpt = response.data["bkpt"];
2216             notifyBreakpointRemoveProceeding(bp);
2217             DebuggerCommand cmd("-break-delete " + mainbkpt["number"].data());
2218             cmd.flags = NeedsTemporaryStop;
2219             runCommand(cmd);
2220             notifyBreakpointRemoveOk(bp);
2221             return;
2222         }
2223     }
2224     if (response.resultClass == ResultDone) {
2225         // The result is a list with the first entry marked "bkpt"
2226         // and "unmarked" rest. The "bkpt" one seems to always be
2227         // the "main" entry. Use the "main" entry to retrieve the
2228         // already known data from the BreakpointManager, and then
2229         // iterate over all items to update main- and sub-data.
2230         for (const GdbMi &bkpt : response.data)
2231             handleBkpt(bkpt, bp);
2232         if (bp->needsChange()) {
2233             bp->gotoState(BreakpointUpdateRequested, BreakpointInsertionProceeding);
2234             updateBreakpoint(bp);
2235         } else {
2236             notifyBreakpointInsertOk(bp);
2237         }
2238     } else if (response.data["msg"].data().contains("Unknown option")) {
2239         // Older version of gdb don't know the -a option to set tracepoints
2240         // ^error,msg="mi_cmd_break_insert: Unknown option ``a''"
2241         const QString fileName = bp->fileName().toString();
2242         const int lineNumber = bp->lineNumber();
2243         DebuggerCommand cmd("trace \"" + GdbMi::escapeCString(fileName) + "\":"
2244                             + QString::number(lineNumber),
2245                             NeedsTemporaryStop);
2246         runCommand(cmd);
2247     } else {
2248         // Some versions of gdb like "GNU gdb (GDB) SUSE (6.8.91.20090930-2.4)"
2249         // know how to do pending breakpoints using CLI but not MI. So try
2250         // again with MI.
2251         DebuggerCommand cmd("break " + breakpointLocation2(bp->requestedParameters()),
2252                             NeedsTemporaryStop);
2253         cmd.callback = [this, bp](const DebuggerResponse &r) { handleBreakInsert2(r, bp); };
2254         runCommand(cmd);
2255     }
2256 }
2257 
handleBreakInsert2(const DebuggerResponse & response,const Breakpoint & bp)2258 void GdbEngine::handleBreakInsert2(const DebuggerResponse &response, const Breakpoint &bp)
2259 {
2260     if (response.resultClass == ResultDone) {
2261         notifyBreakpointInsertOk(bp);
2262     } else {
2263         // Note: gdb < 60800  doesn't "do" pending breakpoints.
2264         // Not much we can do about it except implementing the
2265         // logic on top of shared library events, and that's not
2266         // worth the effort.
2267     }
2268 }
2269 
handleBreakDisable(const DebuggerResponse & response,const Breakpoint & bp)2270 void GdbEngine::handleBreakDisable(const DebuggerResponse &response, const Breakpoint &bp)
2271 {
2272     if (response.resultClass == ResultDone) {
2273         // This should only be the requested state.
2274         QTC_ASSERT(bp, return);
2275         bp->setEnabled(false);
2276         // GDB does *not* touch the subbreakpoints in that case
2277         // bp->forFirstLevelChildren([&](const SubBreakpoint &l) { l->params.enabled = false; });
2278         updateBreakpoint(bp); // Maybe there's more to do.
2279     }
2280 }
2281 
handleBreakEnable(const DebuggerResponse & response,const Breakpoint & bp)2282 void GdbEngine::handleBreakEnable(const DebuggerResponse &response, const Breakpoint &bp)
2283 {
2284     if (response.resultClass == ResultDone) {
2285         QTC_ASSERT(bp, return);
2286         // This should only be the requested state.
2287         bp->setEnabled(true);
2288         // GDB does *not* touch the subbreakpoints in that case
2289         //bp->forFirstLevelChildren([&](const SubBreakpoint &l) { l->params.enabled = true; });
2290         updateBreakpoint(bp); // Maybe there's more to do.
2291     }
2292 }
2293 
handleBreakThreadSpec(const DebuggerResponse & response,const Breakpoint & bp)2294 void GdbEngine::handleBreakThreadSpec(const DebuggerResponse &response, const Breakpoint &bp)
2295 {
2296     QTC_CHECK(response.resultClass == ResultDone);
2297     QTC_ASSERT(bp, return);
2298     // Parsing is fragile. Assume we got what we asked for instead.
2299     bp->setThreadSpec(bp->requestedParameters().threadSpec);
2300     notifyBreakpointNeedsReinsertion(bp);
2301     insertBreakpoint(bp);
2302 }
2303 
handleBreakLineNumber(const DebuggerResponse & response,const Breakpoint & bp)2304 void GdbEngine::handleBreakLineNumber(const DebuggerResponse &response, const Breakpoint &bp)
2305 {
2306     QTC_CHECK(response.resultClass == ResultDone);
2307     QTC_ASSERT(bp, return);
2308     notifyBreakpointNeedsReinsertion(bp);
2309     insertBreakpoint(bp);
2310 }
2311 
handleBreakIgnore(const DebuggerResponse & response,const Breakpoint & bp)2312 void GdbEngine::handleBreakIgnore(const DebuggerResponse &response, const Breakpoint &bp)
2313 {
2314     // gdb 6.8:
2315     // ignore 2 0:
2316     // ~"Will stop next time breakpoint 2 is reached.\n"
2317     // 28^done
2318     // ignore 2 12:
2319     // &"ignore 2 12\n"
2320     // ~"Will ignore next 12 crossings of breakpoint 2.\n"
2321     // 29^done
2322     //
2323     // gdb 6.3 does not produce any console output
2324     QTC_CHECK(response.resultClass == ResultDone);
2325     QTC_ASSERT(bp, return);
2326     //QString msg = _(response.consoleStreamOutput);
2327     //if (msg.contains(__("Will stop next time breakpoint")))
2328     //    response.ignoreCount = _("0");
2329     //else if (msg.contains(__("Will ignore next")))
2330     //    response.ignoreCount = data->ignoreCount;
2331     // FIXME: this assumes it is doing the right thing...
2332     bp->setIgnoreCount(bp->requestedParameters().ignoreCount);
2333     bp->setCommand(bp->requestedParameters().command);
2334     updateBreakpoint(bp); // Maybe there's more to do.
2335 }
2336 
handleBreakCondition(const DebuggerResponse &,const Breakpoint & bp)2337 void GdbEngine::handleBreakCondition(const DebuggerResponse &, const Breakpoint &bp)
2338 {
2339     QTC_ASSERT(bp, return);
2340     // Can happen at invalid condition strings.
2341     //QTC_CHECK(response.resultClass == ResultDone)
2342     // We just assume it was successful. Otherwise we had to parse
2343     // the output stream data.
2344     // The following happens on Mac:
2345     //   QByteArray msg = response.data.findChild("msg").data();
2346     //   if (msg.startsWith("Error parsing breakpoint condition. "
2347     //         " Will try again when we hit the breakpoint."))
2348     bp->setCondition(bp->requestedParameters().condition);
2349     updateBreakpoint(bp); // Maybe there's more to do.
2350 }
2351 
updateTracepointCaptures(const Breakpoint & bp)2352 void GdbEngine::updateTracepointCaptures(const Breakpoint &bp)
2353 {
2354     static QRegularExpression capsRegExp(
2355         "(^|[^\\\\])(\\$(ADDRESS|CALLER|CALLSTACK|FILEPOS|FUNCTION|PID|PNAME|TICK|TID|TNAME)"
2356         "|{[^}]+})");
2357     QString message = bp->globalBreakpoint()->requestedParameters().message;
2358     if (message.isEmpty()) {
2359         bp->setProperty(tracepointCapturePropertyName, {});
2360         return;
2361     }
2362     QVariantList caps;
2363     QRegularExpressionMatch match = capsRegExp.match(message, 0);
2364     while (match.hasMatch()) {
2365         QString t = match.captured(2);
2366         if (t[0] == '$') {
2367             TracepointCaptureType type;
2368             if (t == "$ADDRESS")
2369                 type = TracepointCaptureType::Address;
2370             else if (t == "$CALLER")
2371                 type = TracepointCaptureType::Caller;
2372             else if (t == "$CALLSTACK")
2373                 type = TracepointCaptureType::Callstack;
2374             else if (t == "$FILEPOS")
2375                 type = TracepointCaptureType::FilePos;
2376             else if (t == "$FUNCTION")
2377                 type = TracepointCaptureType::Function;
2378             else if (t == "$PID")
2379                 type = TracepointCaptureType::Pid;
2380             else if (t == "$PNAME")
2381                 type = TracepointCaptureType::ProcessName;
2382             else if (t == "$TICK")
2383                 type = TracepointCaptureType::Tick;
2384             else if (t == "$TID")
2385                 type = TracepointCaptureType::Tid;
2386             else if (t == "$TNAME")
2387                 type = TracepointCaptureType::ThreadName;
2388             else
2389                 QTC_ASSERT(false, continue);
2390             caps << QVariant::fromValue<TracepointCaptureData>(
2391                 {type,
2392                  {},
2393                  static_cast<int>(match.capturedStart(2)),
2394                  static_cast<int>(match.capturedEnd(2))});
2395         } else {
2396             QString expression = t.mid(1, t.length() - 2);
2397             caps << QVariant::fromValue<TracepointCaptureData>(
2398                 {TracepointCaptureType::Expression,
2399                  expression,
2400                  static_cast<int>(match.capturedStart(2)),
2401                  static_cast<int>(match.capturedEnd(2))});
2402         }
2403         match = capsRegExp.match(message, match.capturedEnd());
2404     }
2405     bp->setProperty(tracepointCapturePropertyName, caps);
2406 }
2407 
handleTracepointInsert(const DebuggerResponse & response,const Breakpoint & bp)2408 void GdbEngine::handleTracepointInsert(const DebuggerResponse &response, const Breakpoint &bp)
2409 {
2410     QTC_ASSERT(bp, return);
2411     if (bp->state() == BreakpointRemoveRequested) {
2412         if (response.resultClass == ResultDone) {
2413             // This delete was deferred. Act now.
2414             const GdbMi mainbkpt = response.data["tracepoint"][0];
2415             notifyBreakpointRemoveProceeding(bp);
2416             DebuggerCommand cmd("-break-delete " + mainbkpt["number"].data());
2417             cmd.flags = NeedsTemporaryStop;
2418             runCommand(cmd);
2419             notifyBreakpointRemoveOk(bp);
2420             return;
2421         }
2422     }
2423     if (response.resultClass == ResultDone) {
2424         for (const GdbMi &bkpt : response.data["tracepoint"])
2425             handleBkpt(bkpt, bp);
2426         if (bp->needsChange()) {
2427             bp->gotoState(BreakpointUpdateRequested, BreakpointInsertionProceeding);
2428             updateBreakpoint(bp);
2429         } else {
2430             notifyBreakpointInsertOk(bp);
2431         }
2432     }
2433 }
2434 
handleTracepointHit(const GdbMi & data)2435 void GdbEngine::handleTracepointHit(const GdbMi &data)
2436 {
2437     const GdbMi &result = data["result"];
2438     const QString rid = result["number"].data();
2439     Breakpoint bp = breakHandler()->findBreakpointByResponseId(rid);
2440     QTC_ASSERT(bp, return);
2441     const GdbMi &warnings = data["warnings"];
2442     if (warnings.childCount() > 0) {
2443         for (const GdbMi &warning: warnings) {
2444             emit appendMessageRequested(warning.toString(), ErrorMessageFormat, true);
2445         }
2446     }
2447     QString message = bp->message();
2448     QVariant caps = bp->property(tracepointCapturePropertyName);
2449     if (caps.isValid()) {
2450         QList<QVariant> capsList = caps.toList();
2451         const GdbMi &miCaps = result["caps"];
2452         if (capsList.length() == miCaps.childCount()) {
2453             // reverse iterate to make start/end correct
2454             for (int i = capsList.length() - 1; i >= 0; --i) {
2455                TracepointCaptureData cap = capsList.at(i).value<TracepointCaptureData>();
2456                const GdbMi &miCap = miCaps.childAt(i);
2457                switch (cap.type) {
2458                case TracepointCaptureType::Callstack: {
2459                    QStringList frames;
2460                    for (const GdbMi &frame: miCap) {
2461                        frames.append(frame.data());
2462                    }
2463                    message.replace(cap.start, cap.end - cap.start, frames.join(" <- "));
2464                    break;
2465                }
2466                case TracepointCaptureType::Expression: {
2467                    QString key = miCap.data();
2468                    const GdbMi &expression = data["expressions"][key.toLatin1().data()];
2469                    if (expression.isValid()) {
2470                        QString s = expression.toString();
2471                        // remove '<key>='
2472                        s = s.right(s.length() - key.length() - 1);
2473                        message.replace(cap.start, cap.end - cap.start, s);
2474                    } else {
2475                        QTC_CHECK(false);
2476                    }
2477                    break;
2478                }
2479                default:
2480                    message.replace(cap.start, cap.end - cap.start, miCap.data());
2481                }
2482             }
2483         } else {
2484             QTC_CHECK(false);
2485         }
2486     }
2487     showMessage(message);
2488     emit appendMessageRequested(message, NormalMessageFormat, true);
2489 }
2490 
handleTracepointModified(const GdbMi & data)2491 void GdbEngine::handleTracepointModified(const GdbMi &data)
2492 {
2493     QString ba = data.toString();
2494     // remove original-location
2495     const int pos1 = ba.indexOf("original-location=");
2496     const int pos2 = ba.indexOf(":", pos1 + 17);
2497     int pos3 = ba.indexOf('"', pos2 + 1);
2498     if (ba[pos3 + 1] == ',')
2499         ++pos3;
2500     ba.remove(pos1, pos3 - pos1 + 1);
2501     GdbMi res;
2502     res.fromString(ba);
2503     BreakHandler *handler = breakHandler();
2504     Breakpoint bp;
2505     for (const GdbMi &bkpt : res) {
2506         const QString nr = bkpt["number"].data();
2507         if (nr.contains('.')) {
2508             // A sub-breakpoint.
2509             QTC_ASSERT(bp, continue);
2510             SubBreakpoint loc = bp->findOrCreateSubBreakpoint(nr);
2511             loc->params.updateFromGdbOutput(bkpt);
2512             loc->params.type = bp->type();
2513             if (bp->isTracepoint()) {
2514                 loc->params.tracepoint = true;
2515                 loc->params.message = bp->message();
2516             }
2517         } else {
2518             // A primary breakpoint.
2519             bp = handler->findBreakpointByResponseId(nr);
2520             if (bp)
2521                 bp->updateFromGdbOutput(bkpt);
2522         }
2523     }
2524     QTC_ASSERT(bp, return);
2525     bp->adjustMarker();
2526 }
2527 
acceptsBreakpoint(const BreakpointParameters & bp) const2528 bool GdbEngine::acceptsBreakpoint(const BreakpointParameters &bp) const
2529 {
2530     if (runParameters().startMode == AttachToCore)
2531         return false;
2532     if (bp.isCppBreakpoint())
2533         return true;
2534     return isNativeMixedEnabled();
2535 }
2536 
insertBreakpoint(const Breakpoint & bp)2537 void GdbEngine::insertBreakpoint(const Breakpoint &bp)
2538 {
2539     // Set up fallback in case of pending breakpoints which aren't handled
2540     // by the MI interface.A
2541     QTC_ASSERT(bp, return);
2542     QTC_CHECK(bp->state() == BreakpointInsertionRequested);
2543     notifyBreakpointInsertProceeding(bp);
2544 
2545     const BreakpointParameters &requested = bp->requestedParameters();
2546 
2547     if (!requested.isCppBreakpoint()) {
2548         DebuggerCommand cmd("insertInterpreterBreakpoint", NeedsTemporaryStop);
2549         bp->addToCommand(&cmd);
2550         cmd.callback = [this, bp](const DebuggerResponse &r) { handleInsertInterpreterBreakpoint(r, bp); };
2551         runCommand(cmd);
2552         return;
2553     }
2554 
2555     const auto handleWatch = [this, bp](const DebuggerResponse &r) { handleWatchInsert(r, bp); };
2556     const auto handleCatch = [this, bp](const DebuggerResponse &r) { handleCatchInsert(r, bp); };
2557 
2558     BreakpointType type = requested.type;
2559 
2560     DebuggerCommand cmd;
2561     if (type == WatchpointAtAddress) {
2562         cmd.function = "watch " + addressSpec(requested.address);
2563         cmd.callback = handleWatch;
2564     } else if (type == WatchpointAtExpression) {
2565         cmd.function = "watch " + requested.expression;
2566         cmd.callback = handleWatch;
2567     } else if (type == BreakpointAtFork) {
2568         cmd.function = "catch fork";
2569         cmd.callback = handleCatch;
2570         cmd.flags = NeedsTemporaryStop;
2571         runCommand(cmd);
2572         // Another one...
2573         cmd.function = "catch vfork";
2574     } else if (type == BreakpointAtExec) {
2575         cmd.function = "catch exec";
2576         cmd.callback = handleCatch;
2577     } else if (type == BreakpointAtSysCall) {
2578         cmd.function = "catch syscall";
2579         cmd.callback = handleCatch;
2580     } else {
2581         int spec = requested.threadSpec;
2582         if (requested.isTracepoint()) {
2583 
2584             if (debuggerSettings()->usePseudoTracepoints.value()) {
2585                 cmd.function = "createTracepoint";
2586 
2587                 if (requested.oneShot)
2588                     cmd.arg("temporary", true);
2589 
2590                 if (int ignoreCount = requested.ignoreCount)
2591                     cmd.arg("ignore_count", ignoreCount);
2592 
2593                 QString condition = requested.condition;
2594                 if (!condition.isEmpty())
2595                     cmd.arg("condition", condition.replace('"', "\\\""));
2596 
2597                 if (spec >= 0)
2598                    cmd.arg("thread", spec);
2599 
2600                 updateTracepointCaptures(bp);
2601                 QVariant tpCaps = bp->property(tracepointCapturePropertyName);
2602                 if (tpCaps.isValid()) {
2603                     QJsonArray caps;
2604                     foreach (const auto &tpCap, tpCaps.toList()) {
2605                         TracepointCaptureData data = tpCap.value<TracepointCaptureData>();
2606                         QJsonArray cap;
2607                         cap.append(static_cast<int>(data.type));
2608                         if (data.expression.isValid())
2609                             cap.append(data.expression.toString());
2610                         else
2611                             cap.append(QJsonValue::Null);
2612                         caps.append(cap);
2613                     }
2614                     cmd.arg("caps", caps);
2615                 }
2616 
2617                 // for dumping of expressions
2618                 const static bool alwaysVerbose = qEnvironmentVariableIsSet("QTC_DEBUGGER_PYTHON_VERBOSE");
2619                 const DebuggerSettings &s = *debuggerSettings();
2620                 cmd.arg("passexceptions", alwaysVerbose);
2621                 cmd.arg("fancy", s.useDebuggingHelpers.value());
2622                 cmd.arg("autoderef", s.autoDerefPointers.value());
2623                 cmd.arg("dyntype", s.useDynamicType.value());
2624                 cmd.arg("qobjectnames", s.showQObjectNames.value());
2625                 cmd.arg("nativemixed", isNativeMixedActive());
2626                 cmd.arg("stringcutoff", s.maximalStringLength.value());
2627                 cmd.arg("displaystringlimit", s.displayStringLimit.value());
2628 
2629                 cmd.arg("spec", breakpointLocation2(requested));
2630                 cmd.callback = [this, bp](const DebuggerResponse &r) { handleTracepointInsert(r, bp); };
2631 
2632             } else {
2633                 cmd.function = "-break-insert -a -f ";
2634 
2635                 if (requested.oneShot)
2636                     cmd.function += "-t ";
2637 
2638                 if (!requested.enabled)
2639                     cmd.function += "-d ";
2640 
2641                 if (int ignoreCount = requested.ignoreCount)
2642                     cmd.function += "-i " + QString::number(ignoreCount) + ' ';
2643 
2644                 QString condition = requested.condition;
2645                 if (!condition.isEmpty())
2646                     cmd.function += " -c \"" + condition.replace('"', "\\\"") + "\" ";
2647 
2648                 cmd.function += breakpointLocation(requested);
2649                 cmd.callback = [this, bp](const DebuggerResponse &r) { handleBreakInsert1(r, bp); };
2650 
2651             }
2652 
2653         } else {
2654             cmd.function = "-break-insert ";
2655             if (spec >= 0)
2656                 cmd.function += "-p " + QString::number(spec);
2657             cmd.function += " -f ";
2658 
2659            if (requested.oneShot)
2660                cmd.function += "-t ";
2661 
2662            if (!requested.enabled)
2663                cmd.function += "-d ";
2664 
2665            if (int ignoreCount = requested.ignoreCount)
2666                cmd.function += "-i " + QString::number(ignoreCount) + ' ';
2667 
2668            QString condition = requested.condition;
2669            if (!condition.isEmpty())
2670                cmd.function += " -c \"" + condition.replace('"', "\\\"") + "\" ";
2671 
2672            cmd.function += breakpointLocation(requested);
2673            cmd.callback = [this, bp](const DebuggerResponse &r) { handleBreakInsert1(r, bp); };
2674 
2675        }
2676     }
2677     cmd.flags = NeedsTemporaryStop;
2678     runCommand(cmd);
2679 }
2680 
updateBreakpoint(const Breakpoint & bp)2681 void GdbEngine::updateBreakpoint(const Breakpoint &bp)
2682 {
2683     QTC_ASSERT(bp, return);
2684     const BreakpointParameters &requested = bp->requestedParameters();
2685     QTC_ASSERT(requested.type != UnknownBreakpointType, return);
2686     QTC_ASSERT(!bp->responseId().isEmpty(), return);
2687     const QString bpnr = bp->responseId();
2688     const BreakpointState state = bp->state();
2689     if (state == BreakpointUpdateRequested)
2690         notifyBreakpointChangeProceeding(bp);
2691     const BreakpointState state2 = bp->state();
2692     QTC_ASSERT(state2 == BreakpointUpdateProceeding, qDebug() << state2);
2693 
2694     DebuggerCommand cmd;
2695     // FIXME: See QTCREATORBUG-21611, QTCREATORBUG-21616
2696 //    if (!bp->isPending() && requested.threadSpec != bp->threadSpec()) {
2697 //        // The only way to change this seems to be to re-set the bp completely.
2698 //        cmd.function = "-break-delete " + bpnr;
2699 //        cmd.callback = [this, bp](const DebuggerResponse &r) { handleBreakThreadSpec(r, bp); };
2700 //    } else if (!bp->isPending() && requested.lineNumber != bp->lineNumber()) {
2701 //      // The only way to change this seems to be to re-set the bp completely.
2702 //      cmd.function = "-break-delete " + bpnr;
2703 //      cmd.callback = [this, bp](const DebuggerResponse &r) { handleBreakLineNumber(r, bp); };
2704 //    } else if
2705     if (requested.command != bp->command()) {
2706         cmd.function = "-break-commands " + bpnr;
2707         for (QString command : requested.command.split('\n')) {
2708             if (!command.isEmpty()) {
2709                 // escape backslashes and quotes
2710                 command.replace('\\', "\\\\");
2711                 command.replace('"', "\\\"");
2712                 cmd.function +=  " \"" + command + '"';
2713             }
2714         }
2715         cmd.callback = [this, bp](const DebuggerResponse &r) { handleBreakIgnore(r, bp); };
2716     } else if (!requested.conditionsMatch(bp->condition())) {
2717         cmd.function = "condition " + bpnr + ' '  + requested.condition;
2718         cmd.callback = [this, bp](const DebuggerResponse &r) { handleBreakCondition(r, bp); };
2719     } else if (requested.ignoreCount != bp->ignoreCount()) {
2720         cmd.function = "ignore " + bpnr + ' ' + QString::number(requested.ignoreCount);
2721         cmd.callback = [this, bp](const DebuggerResponse &r) { handleBreakIgnore(r, bp); };
2722     } else if (!requested.enabled && bp->isEnabled()) {
2723         cmd.function = "-break-disable " + bpnr;
2724         cmd.callback = [this, bp](const DebuggerResponse &r) { handleBreakDisable(r, bp); };
2725     } else if (requested.enabled && !bp->isEnabled()) {
2726         cmd.function = "-break-enable " + bpnr;
2727         cmd.callback = [this, bp](const DebuggerResponse &r) { handleBreakEnable(r, bp); };
2728     } else {
2729         notifyBreakpointChangeOk(bp);
2730         return;
2731     }
2732     cmd.flags = NeedsTemporaryStop;
2733     runCommand(cmd);
2734 }
2735 
enableSubBreakpoint(const SubBreakpoint & sbp,bool on)2736 void GdbEngine::enableSubBreakpoint(const SubBreakpoint &sbp, bool on)
2737 {
2738     QTC_ASSERT(sbp, return);
2739     DebuggerCommand cmd((on ? "-break-enable " : "-break-disable ") + sbp->responseId);
2740     runCommand(cmd);
2741 }
2742 
removeBreakpoint(const Breakpoint & bp)2743 void GdbEngine::removeBreakpoint(const Breakpoint &bp)
2744 {
2745     QTC_ASSERT(bp, return);
2746     QTC_CHECK(bp->state() == BreakpointRemoveRequested);
2747 
2748     const BreakpointParameters &requested = bp->requestedParameters();
2749     if (!requested.isCppBreakpoint()) {
2750         DebuggerCommand cmd("removeInterpreterBreakpoint");
2751         bp->addToCommand(&cmd);
2752         runCommand(cmd);
2753         notifyBreakpointRemoveOk(bp);
2754         return;
2755     }
2756 
2757     if (!bp->responseId().isEmpty()) {
2758         // We already have a fully inserted breakpoint.
2759         notifyBreakpointRemoveProceeding(bp);
2760         showMessage(
2761             QString("DELETING BP %1 IN %2").arg(bp->responseId()).arg(bp->fileName().toString()));
2762         DebuggerCommand cmd("-break-delete " + bp->responseId(), NeedsTemporaryStop);
2763         runCommand(cmd);
2764 
2765         // Pretend it succeeds without waiting for response. Feels better.
2766         // Otherwise, clicking in the gutter leaves the breakpoint visible
2767         // for quite some time, so the user assumes a mis-click and clicks
2768         // again, effectivly re-introducing the breakpoint.
2769         notifyBreakpointRemoveOk(bp);
2770 
2771     } else {
2772         // Breakpoint was scheduled to be inserted, but we haven't had
2773         // an answer so far. Postpone activity by doing nothing.
2774     }
2775 }
2776 
2777 
2778 //////////////////////////////////////////////////////////////////////
2779 //
2780 // Modules specific stuff
2781 //
2782 //////////////////////////////////////////////////////////////////////
2783 
dotEscape(QString str)2784 static QString dotEscape(QString str)
2785 {
2786     str.replace(' ', '.');
2787     str.replace('\\', '.');
2788     str.replace('/', '.');
2789     return str;
2790 }
2791 
loadSymbols(const QString & modulePath)2792 void GdbEngine::loadSymbols(const QString &modulePath)
2793 {
2794     // FIXME: gdb does not understand quoted names here (tested with 6.8)
2795     runCommand({"sharedlibrary " + dotEscape(modulePath)});
2796     reloadModulesInternal();
2797     reloadStack();
2798     updateLocals();
2799 }
2800 
loadAllSymbols()2801 void GdbEngine::loadAllSymbols()
2802 {
2803     runCommand({"sharedlibrary .*"});
2804     reloadModulesInternal();
2805     reloadStack();
2806     updateLocals();
2807 }
2808 
loadSymbolsForStack()2809 void GdbEngine::loadSymbolsForStack()
2810 {
2811     bool needUpdate = false;
2812     const Modules &modules = modulesHandler()->modules();
2813     stackHandler()->forItemsAtLevel<2>([modules, &needUpdate, this](StackFrameItem *frameItem) {
2814         const StackFrame &frame = frameItem->frame;
2815         if (frame.function == "??") {
2816             //qDebug() << "LOAD FOR " << frame.address;
2817             for (const Module &module : modules) {
2818                 if (module.startAddress <= frame.address
2819                         && frame.address < module.endAddress) {
2820                     runCommand({"sharedlibrary " + dotEscape(module.modulePath)});
2821                     needUpdate = true;
2822                 }
2823             }
2824         }
2825     });
2826     if (needUpdate) {
2827         reloadStack();
2828         updateLocals();
2829     }
2830 }
2831 
handleShowModuleSymbols(const DebuggerResponse & response,const QString & modulePath,const QString & fileName)2832 static void handleShowModuleSymbols(const DebuggerResponse &response,
2833     const QString &modulePath, const QString &fileName)
2834 {
2835     if (response.resultClass == ResultDone) {
2836         Symbols symbols;
2837         QFile file(fileName);
2838         file.open(QIODevice::ReadOnly);
2839         // Object file /opt/dev/qt/lib/libQtNetworkMyns.so.4:
2840         // [ 0] A 0x16bd64 _DYNAMIC  moc_qudpsocket.cpp
2841         // [12] S 0xe94680 _ZN4myns5QFileC1Ev section .plt  myns::QFile::QFile()
2842         foreach (const QString &line, QString::fromLocal8Bit(file.readAll()).split('\n')) {
2843             if (line.isEmpty())
2844                 continue;
2845             if (line.at(0) != '[')
2846                 continue;
2847             int posCode = line.indexOf(']') + 2;
2848             int posAddress = line.indexOf("0x", posCode);
2849             if (posAddress == -1)
2850                 continue;
2851             int posName = line.indexOf(" ", posAddress);
2852             int lenAddress = posName - posAddress;
2853             int posSection = line.indexOf(" section ");
2854             int lenName = 0;
2855             int lenSection = 0;
2856             int posDemangled = 0;
2857             if (posSection == -1) {
2858                 lenName = line.size() - posName;
2859                 posDemangled = posName;
2860             } else {
2861                 lenName = posSection - posName;
2862                 posSection += 10;
2863                 posDemangled = line.indexOf(' ', posSection + 1);
2864                 if (posDemangled == -1) {
2865                     lenSection = line.size() - posSection;
2866                 } else {
2867                     lenSection = posDemangled - posSection;
2868                     posDemangled += 1;
2869                 }
2870             }
2871             int lenDemangled = 0;
2872             if (posDemangled != -1)
2873                 lenDemangled = line.size() - posDemangled;
2874             Symbol symbol;
2875             symbol.state = line.mid(posCode, 1);
2876             symbol.address = line.mid(posAddress, lenAddress);
2877             symbol.name = line.mid(posName, lenName);
2878             symbol.section = line.mid(posSection, lenSection);
2879             symbol.demangled = line.mid(posDemangled, lenDemangled);
2880             symbols.push_back(symbol);
2881         }
2882         file.close();
2883         file.remove();
2884         DebuggerEngine::showModuleSymbols(modulePath, symbols);
2885     } else {
2886         AsynchronousMessageBox::critical(GdbEngine::tr("Cannot Read Symbols"),
2887             GdbEngine::tr("Cannot read symbols for module \"%1\".").arg(fileName));
2888     }
2889 }
2890 
requestModuleSymbols(const QString & modulePath)2891 void GdbEngine::requestModuleSymbols(const QString &modulePath)
2892 {
2893     Utils::TemporaryFile tf("gdbsymbols");
2894     if (!tf.open())
2895         return;
2896     QString fileName = tf.fileName();
2897     tf.close();
2898     DebuggerCommand cmd("maint print msymbols \"" + fileName + "\" " + modulePath, NeedsTemporaryStop);
2899     cmd.callback = [modulePath, fileName](const DebuggerResponse &r) {
2900         handleShowModuleSymbols(r, modulePath, fileName);
2901     };
2902     runCommand(cmd);
2903 }
2904 
requestModuleSections(const QString & moduleName)2905 void GdbEngine::requestModuleSections(const QString &moduleName)
2906 {
2907     // There seems to be no way to get the symbols from a single .so.
2908     DebuggerCommand cmd("maint info section ALLOBJ", NeedsTemporaryStop);
2909     cmd.callback = [this, moduleName](const DebuggerResponse &r) {
2910         handleShowModuleSections(r, moduleName);
2911     };
2912     runCommand(cmd);
2913 }
2914 
handleShowModuleSections(const DebuggerResponse & response,const QString & moduleName)2915 void GdbEngine::handleShowModuleSections(const DebuggerResponse &response,
2916                                          const QString &moduleName)
2917 {
2918     // ~"  Object file: /usr/lib/i386-linux-gnu/libffi.so.6\n"
2919     // ~"    0xb44a6114->0xb44a6138 at 0x00000114: .note.gnu.build-id ALLOC LOAD READONLY DATA HAS_CONTENTS\n"
2920     if (response.resultClass == ResultDone) {
2921         const QStringList lines = response.consoleStreamOutput.split('\n');
2922         const QString prefix = "  Object file: ";
2923         const QString needle = prefix + moduleName;
2924         Sections sections;
2925         bool active = false;
2926         foreach (const QString &line, lines) {
2927             if (line.startsWith(prefix)) {
2928                 if (active)
2929                     break;
2930                 if (line == needle)
2931                     active = true;
2932             } else {
2933                 if (active) {
2934                     QStringList items = line.split(' ', Qt::SkipEmptyParts);
2935                     QString fromTo = items.value(0, QString());
2936                     const int pos = fromTo.indexOf('-');
2937                     QTC_ASSERT(pos >= 0, continue);
2938                     Section section;
2939                     section.from = fromTo.left(pos);
2940                     section.to = fromTo.mid(pos + 2);
2941                     section.address = items.value(2, QString());
2942                     section.name = items.value(3, QString());
2943                     section.flags = items.value(4, QString());
2944                     sections.append(section);
2945                 }
2946             }
2947         }
2948         if (!sections.isEmpty())
2949             DebuggerEngine::showModuleSections(moduleName, sections);
2950     }
2951 }
2952 
reloadModules()2953 void GdbEngine::reloadModules()
2954 {
2955     if (state() == InferiorRunOk || state() == InferiorStopOk)
2956         reloadModulesInternal();
2957 }
2958 
reloadModulesInternal()2959 void GdbEngine::reloadModulesInternal()
2960 {
2961     runCommand({"info shared", NeedsTemporaryStop, CB(handleModulesList)});
2962 }
2963 
nameFromPath(const QString & path)2964 static QString nameFromPath(const QString &path)
2965 {
2966     return QFileInfo(path).baseName();
2967 }
2968 
handleModulesList(const DebuggerResponse & response)2969 void GdbEngine::handleModulesList(const DebuggerResponse &response)
2970 {
2971     if (response.resultClass == ResultDone) {
2972         ModulesHandler *handler = modulesHandler();
2973         Module module;
2974         // That's console-based output, likely Linux or Windows,
2975         // but we can avoid the target dependency here.
2976         QString data = response.consoleStreamOutput;
2977         QTextStream ts(&data, QIODevice::ReadOnly);
2978         bool found = false;
2979         while (!ts.atEnd()) {
2980             QString line = ts.readLine();
2981             QString symbolsRead;
2982             QTextStream ts(&line, QIODevice::ReadOnly);
2983             if (line.startsWith("0x")) {
2984                 ts >> module.startAddress >> module.endAddress >> symbolsRead;
2985                 module.modulePath = ts.readLine().trimmed();
2986                 module.moduleName = nameFromPath(module.modulePath);
2987                 module.symbolsRead =
2988                     (symbolsRead == "Yes" ? Module::ReadOk : Module::ReadFailed);
2989                 handler->updateModule(module);
2990                 found = true;
2991             } else if (line.trimmed().startsWith("No")) {
2992                 // gdb 6.4 symbianelf
2993                 ts >> symbolsRead;
2994                 QTC_ASSERT(symbolsRead == "No", continue);
2995                 module.startAddress = 0;
2996                 module.endAddress = 0;
2997                 module.modulePath = ts.readLine().trimmed();
2998                 module.moduleName = nameFromPath(module.modulePath);
2999                 handler->updateModule(module);
3000                 found = true;
3001             }
3002         }
3003         if (!found) {
3004             // Mac has^done,shlib-info={num="1",name="dyld",kind="-",
3005             // dyld-addr="0x8fe00000",reason="dyld",requested-state="Y",
3006             // state="Y",path="/usr/lib/dyld",description="/usr/lib/dyld",
3007             // loaded_addr="0x8fe00000",slide="0x0",prefix="__dyld_"},
3008             // shlib-info={...}...
3009             for (const GdbMi &item : response.data) {
3010                 module.modulePath = item["path"].data();
3011                 module.moduleName = nameFromPath(module.modulePath);
3012                 module.symbolsRead = (item["state"].data() == "Y")
3013                         ? Module::ReadOk : Module::ReadFailed;
3014                 module.startAddress =
3015                     item["loaded_addr"].data().toULongLong(nullptr, 0);
3016                 module.endAddress = 0; // FIXME: End address not easily available.
3017                 handler->updateModule(module);
3018             }
3019         }
3020     }
3021 }
3022 
examineModules()3023 void GdbEngine::examineModules()
3024 {
3025     ModulesHandler *handler = modulesHandler();
3026     for (const Module &module : handler->modules()) {
3027         if (module.elfData.symbolsType == UnknownSymbols)
3028             handler->updateModule(module);
3029     }
3030 }
3031 
3032 //////////////////////////////////////////////////////////////////////
3033 //
3034 // Source files specific stuff
3035 //
3036 //////////////////////////////////////////////////////////////////////
3037 
reloadSourceFiles()3038 void GdbEngine::reloadSourceFiles()
3039 {
3040     if ((state() == InferiorRunOk || state() == InferiorStopOk) && !m_sourcesListUpdating) {
3041         m_sourcesListUpdating = true;
3042         DebuggerCommand cmd("-file-list-exec-source-files", NeedsTemporaryStop);
3043         cmd.callback = [this](const DebuggerResponse &response) {
3044             m_sourcesListUpdating = false;
3045             if (response.resultClass == ResultDone) {
3046                 QMap<QString, QString> oldShortToFull = m_shortToFullName;
3047                 m_shortToFullName.clear();
3048                 m_fullToShortName.clear();
3049                 // "^done,files=[{file="../../../../bin/dumper/dumper.cpp",
3050                 // fullname="/data5/dev/ide/main/bin/dumper/dumper.cpp"},
3051                 for (const GdbMi &item : response.data["files"]) {
3052                     GdbMi fileName = item["file"];
3053                     if (fileName.data().endsWith("<built-in>"))
3054                         continue;
3055                     GdbMi fullName = item["fullname"];
3056                     QString file = fileName.data();
3057                     QString full;
3058                     if (fullName.isValid()) {
3059                         full = cleanupFullName(fullName.data());
3060                         m_fullToShortName[full] = file;
3061                     }
3062                     m_shortToFullName[file] = full;
3063                 }
3064                 if (m_shortToFullName != oldShortToFull)
3065                     sourceFilesHandler()->setSourceFiles(m_shortToFullName);
3066             }
3067         };
3068         runCommand(cmd);
3069     }
3070 }
3071 
3072 
3073 //////////////////////////////////////////////////////////////////////
3074 //
3075 // Stack specific stuff
3076 //
3077 //////////////////////////////////////////////////////////////////////
3078 
selectThread(const Thread & thread)3079 void GdbEngine::selectThread(const Thread &thread)
3080 {
3081     showStatusMessage(tr("Retrieving data for stack view thread %1...")
3082         .arg(thread->id()), 10000);
3083     DebuggerCommand cmd("-thread-select " + thread->id(), Discardable);
3084     cmd.callback = [this](const DebuggerResponse &) {
3085         QTC_CHECK(state() == InferiorUnrunnable || state() == InferiorStopOk);
3086         showStatusMessage(tr("Retrieving data for stack view..."), 3000);
3087         reloadStack(); // Will reload registers.
3088         updateLocals();
3089     };
3090     runCommand(cmd);
3091 }
3092 
reloadFullStack()3093 void GdbEngine::reloadFullStack()
3094 {
3095     PENDING_DEBUG("RELOAD FULL STACK");
3096     resetLocation();
3097     DebuggerCommand cmd = stackCommand(-1);
3098     cmd.callback = [this](const DebuggerResponse &r) { handleStackListFrames(r, true); };
3099     cmd.flags = Discardable;
3100     runCommand(cmd);
3101 }
3102 
loadAdditionalQmlStack()3103 void GdbEngine::loadAdditionalQmlStack()
3104 {
3105     DebuggerCommand cmd = stackCommand(-1);
3106     cmd.arg("extraqml", "1");
3107     cmd.callback = [this](const DebuggerResponse &r) { handleStackListFrames(r, true); };
3108     cmd.flags = Discardable;
3109     runCommand(cmd);
3110 }
3111 
stackCommand(int depth)3112 DebuggerCommand GdbEngine::stackCommand(int depth)
3113 {
3114     DebuggerCommand cmd("fetchStack");
3115     cmd.arg("limit", depth);
3116     cmd.arg("nativemixed", isNativeMixedActive());
3117     return cmd;
3118 }
3119 
reloadStack()3120 void GdbEngine::reloadStack()
3121 {
3122     PENDING_DEBUG("RELOAD STACK");
3123     DebuggerCommand cmd = stackCommand(debuggerSettings()->maximalStackDepth.value());
3124     cmd.callback = [this](const DebuggerResponse &r) { handleStackListFrames(r, false); };
3125     cmd.flags = Discardable;
3126     runCommand(cmd);
3127 }
3128 
handleStackListFrames(const DebuggerResponse & response,bool isFull)3129 void GdbEngine::handleStackListFrames(const DebuggerResponse &response, bool isFull)
3130 {
3131     if (response.resultClass != ResultDone) {
3132         // That always happens on symbian gdb with
3133         // ^error,data={msg="Previous frame identical to this frame (corrupt stack?)"
3134         // logStreamOutput: "Previous frame identical to this frame (corrupt stack?)\n"
3135         //qDebug() << "LISTING STACK FAILED: " << response.toString();
3136         reloadRegisters();
3137         reloadPeripheralRegisters();
3138         return;
3139     }
3140 
3141     GdbMi stack = response.data["stack"]; // C++
3142     //if (!frames.isValid() || frames.childCount() == 0) // Mixed.
3143     GdbMi frames = stack["frames"];
3144     if (!frames.isValid())
3145         isFull = true;
3146 
3147     stackHandler()->setFramesAndCurrentIndex(frames, isFull);
3148     activateFrame(stackHandler()->currentIndex());
3149 }
3150 
activateFrame(int frameIndex)3151 void GdbEngine::activateFrame(int frameIndex)
3152 {
3153     if (state() != InferiorStopOk && state() != InferiorUnrunnable)
3154         return;
3155 
3156     StackHandler *handler = stackHandler();
3157 
3158     if (handler->isSpecialFrame(frameIndex)) {
3159         reloadFullStack();
3160         return;
3161     }
3162 
3163     QTC_ASSERT(frameIndex < handler->stackSize(), return);
3164     handler->setCurrentIndex(frameIndex);
3165     gotoCurrentLocation();
3166 
3167     if (handler->frameAt(frameIndex).language != QmlLanguage) {
3168         // Assuming the command always succeeds this saves a roundtrip.
3169         // Otherwise the lines below would need to get triggered
3170         // after a response to this -stack-select-frame here.
3171         //if (!m_currentThread.isEmpty())
3172         //    cmd += " --thread " + m_currentThread;
3173         runCommand({"-stack-select-frame " + QString::number(frameIndex), Discardable});
3174     }
3175 
3176     updateLocals();
3177     reloadRegisters();
3178     reloadPeripheralRegisters();
3179 }
3180 
handleThreadInfo(const DebuggerResponse & response)3181 void GdbEngine::handleThreadInfo(const DebuggerResponse &response)
3182 {
3183     if (response.resultClass == ResultDone) {
3184         ThreadsHandler *handler = threadsHandler();
3185         handler->setThreads(response.data);
3186         updateState(); // Adjust Threads combobox.
3187         if (debuggerSettings()->showThreadNames.value()) {
3188             runCommand({QString("threadnames %1").arg(debuggerSettings()->maximalStackDepth.value()),
3189                 Discardable, CB(handleThreadNames)});
3190         }
3191         reloadStack(); // Will trigger register reload.
3192     } else {
3193         // Fall back for older versions: Try to get at least a list
3194         // of running threads.
3195         runCommand({"-thread-list-ids", Discardable, CB(handleThreadListIds)});
3196     }
3197 }
3198 
handleThreadListIds(const DebuggerResponse & response)3199 void GdbEngine::handleThreadListIds(const DebuggerResponse &response)
3200 {
3201     // "72^done,{thread-ids={thread-id="2",thread-id="1"},number-of-threads="2"}
3202     // In gdb 7.1+ additionally: current-thread-id="1"
3203     ThreadsHandler *handler = threadsHandler();
3204     for (const GdbMi &item : response.data["thread-ids"]) {
3205         ThreadData thread;
3206         thread.id = item.data();
3207         handler->updateThread(thread);
3208     }
3209     reloadStack(); // Will trigger register reload.
3210 }
3211 
handleThreadNames(const DebuggerResponse & response)3212 void GdbEngine::handleThreadNames(const DebuggerResponse &response)
3213 {
3214     if (response.resultClass == ResultDone) {
3215         ThreadsHandler *handler = threadsHandler();
3216         GdbMi names;
3217         names.fromString(response.consoleStreamOutput);
3218         for (const GdbMi &name : names) {
3219             ThreadData thread;
3220             thread.id = name["id"].data();
3221             // Core is unavailable in core dump. Allow the user to provide it.
3222             thread.core = name["core"].data();
3223             thread.name = decodeData(name["value"].data(), name["valueencoded"].data());
3224             handler->updateThread(thread);
3225         }
3226         updateState();
3227     }
3228 }
3229 
3230 
3231 //////////////////////////////////////////////////////////////////////
3232 //
3233 // Snapshot specific stuff
3234 //
3235 //////////////////////////////////////////////////////////////////////
3236 
createSnapshot()3237 void GdbEngine::createSnapshot()
3238 {
3239     QString fileName;
3240     Utils::TemporaryFile tf("gdbsnapshot");
3241     if (tf.open()) {
3242         fileName = tf.fileName();
3243         tf.close();
3244         // This must not be quoted, it doesn't work otherwise.
3245         DebuggerCommand cmd("gcore " + fileName, NeedsTemporaryStop | ConsoleCommand);
3246         cmd.callback = [this, fileName](const DebuggerResponse &r) { handleMakeSnapshot(r, fileName); };
3247         runCommand(cmd);
3248     } else {
3249         AsynchronousMessageBox::critical(tr("Snapshot Creation Error"),
3250             tr("Cannot create snapshot file."));
3251     }
3252 }
3253 
handleMakeSnapshot(const DebuggerResponse & response,const QString & coreFile)3254 void GdbEngine::handleMakeSnapshot(const DebuggerResponse &response, const QString &coreFile)
3255 {
3256     if (response.resultClass == ResultDone) {
3257         emit attachToCoreRequested(coreFile);
3258     } else {
3259         QString msg = response.data["msg"].data();
3260         AsynchronousMessageBox::critical(tr("Snapshot Creation Error"),
3261             tr("Cannot create snapshot:") + '\n' + msg);
3262     }
3263 }
3264 
3265 
3266 //////////////////////////////////////////////////////////////////////
3267 //
3268 // Register specific stuff
3269 //
3270 //////////////////////////////////////////////////////////////////////
3271 
reloadRegisters()3272 void GdbEngine::reloadRegisters()
3273 {
3274     if (!isRegistersWindowVisible())
3275         return;
3276 
3277     if (state() != InferiorStopOk && state() != InferiorUnrunnable)
3278         return;
3279 
3280     if (true) {
3281         if (!m_registerNamesListed) {
3282             // The MI version does not give register size.
3283             // runCommand("-data-list-register-names", CB(handleRegisterListNames));
3284             m_registerNamesListed = true;
3285             runCommand({"maintenance print register-groups",
3286                         CB(handleRegisterListing)});
3287         }
3288         // Can cause i386-linux-nat.c:571: internal-error: Got request
3289         // for bad register number 41.\nA problem internal to GDB has been detected.
3290         runCommand({"-data-list-register-values r", Discardable,
3291                     CB(handleRegisterListValues)});
3292     } else {
3293         runCommand({"maintenance print cooked-registers",
3294                     CB(handleMaintPrintRegisters)});
3295     }
3296 }
3297 
reloadPeripheralRegisters()3298 void GdbEngine::reloadPeripheralRegisters()
3299 {
3300     if (!isPeripheralRegistersWindowVisible())
3301         return;
3302 
3303     const QList<quint64> addresses = peripheralRegisterHandler()->activeRegisters();
3304     if (addresses.isEmpty())
3305         return; // Nothing to update.
3306 
3307     for (const quint64 address : addresses) {
3308         const QString fun = QStringLiteral("x/1u 0x%1")
3309                 .arg(QString::number(address, 16));
3310         runCommand({fun, CB(handlePeripheralRegisterListValues)});
3311     }
3312 }
3313 
readWord(const QString & ba,int * pos)3314 static QString readWord(const QString &ba, int *pos)
3315 {
3316     const int n = ba.size();
3317     while (*pos < n && ba.at(*pos) == ' ')
3318         ++*pos;
3319     const int start = *pos;
3320     while (*pos < n && ba.at(*pos) != ' ' && ba.at(*pos) != '\n')
3321         ++*pos;
3322     return ba.mid(start, *pos - start);
3323 }
3324 
handleMaintPrintRegisters(const DebuggerResponse & response)3325 void GdbEngine::handleMaintPrintRegisters(const DebuggerResponse &response)
3326 {
3327     if (response.resultClass != ResultDone)
3328         return;
3329 
3330     const QString &ba = response.consoleStreamOutput;
3331     RegisterHandler *handler = registerHandler();
3332     //0         1         2         3         4         5         6
3333     //0123456789012345678901234567890123456789012345678901234567890
3334     // Name         Nr  Rel Offset    Size  Type            Raw value
3335     // rax           0    0      0       8 int64_t         0x0000000000000000
3336     // rip          16   16    128       8 *1              0x0000000000400dc9
3337     // eflags       17   17    136       4 i386_eflags     0x00000246
3338     // cs           18   18    140       4 int32_t         0x00000033
3339     // xmm15        55   55    516      16 vec128          0x00000000000000000000000000000000
3340     // mxcsr        56   56    532       4 i386_mxcsr      0x00001fa0
3341     // ''
3342     // st6          30   30    224      10 _i387_ext       0x00000000000000000000
3343     // st7          31   31    234      10 _i387_ext       0x00000000000000000000
3344     // fctrl        32   32    244       4 int             0x0000037f
3345 
3346     const int n = ba.size();
3347     int pos = 0;
3348     while (true) {
3349         // Skip first line, and until '\n' after each line finished.
3350         while (pos < n && ba.at(pos) != '\n')
3351             ++pos;
3352         if (pos >= n)
3353             break;
3354         ++pos; // skip \n
3355         Register reg;
3356         reg.name = readWord(ba, &pos);
3357         if (reg.name == "''" || reg.name == "*1:" || reg.name.isEmpty())
3358             continue;
3359         readWord(ba, &pos); // Nr
3360         readWord(ba, &pos); // Rel
3361         readWord(ba, &pos); // Offset
3362         reg.size = readWord(ba, &pos).toInt();
3363         reg.reportedType = readWord(ba, &pos);
3364         reg.value.fromString(readWord(ba, &pos), HexadecimalFormat);
3365         handler->updateRegister(reg);
3366     }
3367     handler->commitUpdates();
3368 }
3369 
setRegisterValue(const QString & name,const QString & value)3370 void GdbEngine::setRegisterValue(const QString &name, const QString &value)
3371 {
3372     QString fullName = name;
3373     if (name.startsWith("xmm"))
3374         fullName += ".uint128";
3375     runCommand({"set $" + fullName  + "=" + value});
3376     reloadRegisters();
3377 }
3378 
setPeripheralRegisterValue(quint64 address,quint64 value)3379 void GdbEngine::setPeripheralRegisterValue(quint64 address, quint64 value)
3380 {
3381     const QString fun = QStringLiteral("set {int}0x%1=%2")
3382             .arg(QString::number(address, 16))
3383             .arg(value);
3384     runCommand({fun});
3385     reloadPeripheralRegisters();
3386 }
3387 
handleRegisterListNames(const DebuggerResponse & response)3388 void GdbEngine::handleRegisterListNames(const DebuggerResponse &response)
3389 {
3390     if (response.resultClass != ResultDone) {
3391         m_registerNamesListed = false;
3392         return;
3393     }
3394 
3395     m_registers.clear();
3396     int gdbRegisterNumber = 0;
3397     for (const GdbMi &item : response.data["register-names"]) {
3398         if (!item.data().isEmpty()) {
3399             Register reg;
3400             reg.name = item.data();
3401             m_registers[gdbRegisterNumber] = reg;
3402         }
3403         ++gdbRegisterNumber;
3404     }
3405 }
3406 
handleRegisterListing(const DebuggerResponse & response)3407 void GdbEngine::handleRegisterListing(const DebuggerResponse &response)
3408 {
3409     if (response.resultClass != ResultDone) {
3410         m_registerNamesListed = false;
3411         return;
3412     }
3413 
3414     // &"maintenance print raw-registers\n"
3415     // >~" Name         Nr  Rel Offset    Size  Type            Groups\n"
3416     // >~" rax           0    0      0       8 int64_t         general,all,save,restore\n"
3417     // >~" rip          16   16    128       8 *1              general,all,save,restore\n"
3418     // >~" fop          39   39    272       4 int             float,all,save,restore\n"
3419     // >~" xmm0         40   40    276      16 vec128          sse,all,save,restore,vector\n"
3420     // >~" ''          145  145    536       0 int0_t          general\n"
3421     m_registers.clear();
3422     QStringList lines = response.consoleStreamOutput.split('\n');
3423     for (int i = 1; i < lines.size(); ++i) {
3424         const QStringList parts = lines.at(i).split(' ', Qt::SkipEmptyParts);
3425         if (parts.size() < 7)
3426             continue;
3427         int gdbRegisterNumber = parts.at(1).toInt();
3428         Register reg;
3429         reg.name = parts.at(0);
3430         reg.size = parts.at(4).toInt();
3431         reg.reportedType = parts.at(5);
3432         reg.groups = Utils::toSet(parts.at(6).split(','));
3433         m_registers[gdbRegisterNumber] = reg;
3434     }
3435 }
3436 
handleRegisterListValues(const DebuggerResponse & response)3437 void GdbEngine::handleRegisterListValues(const DebuggerResponse &response)
3438 {
3439     if (response.resultClass != ResultDone)
3440         return;
3441 
3442     RegisterHandler *handler = registerHandler();
3443     // 24^done,register-values=[{number="0",value="0xf423f"},...]
3444     for (const GdbMi &item : response.data["register-values"]) {
3445         const int number = item["number"].toInt();
3446         auto reg = m_registers.find(number);
3447         if (reg == m_registers.end())
3448             continue;
3449         QString data = item["value"].data();
3450         if (data.startsWith("0x")) {
3451             reg->value.fromString(data, HexadecimalFormat);
3452         } else if (data == "<error reading variable>") {
3453             // Nothing. See QTCREATORBUG-14029.
3454         } else {
3455             // This is what GDB considers machine readable output:
3456             // value="{v4_float = {0x00000000, 0x00000000, 0x00000000, 0x00000000},
3457             // v2_double = {0x0000000000000000, 0x0000000000000000},
3458             // v16_int8 = {0x00 <repeats 16 times>},
3459             // v8_int16 = {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
3460             // v4_int32 = {0x00000000, 0x00000000, 0x00000000, 0x00000000},
3461             // v2_int64 = {0x0000000000000000, 0x0000000000000000},
3462             // uint128 = <error reading variable>}"}
3463             // Try to make sense of it using the int32 chunks.
3464             // Android gdb 7.10 has u32 = {0x00000000, 0x40340000}.
3465             // Use that if available.
3466             QString result;
3467             int pos1 = data.indexOf("_int32");
3468             if (pos1 == -1)
3469                 pos1 = data.indexOf("u32");
3470             const int pos2 = data.indexOf('{', pos1) + 1;
3471             const int pos3 = data.indexOf('}', pos2);
3472             QString inner = data.mid(pos2, pos3 - pos2);
3473             QStringList list = inner.split(',');
3474             for (int i = list.size(); --i >= 0; ) {
3475                 QString chunk = list.at(i);
3476                 if (chunk.startsWith(' '))
3477                     chunk.remove(0, 1);
3478                 if (chunk.startsWith('<') || chunk.startsWith('{')) // <unavailable>, {v4_float=...
3479                     continue;
3480                 if (chunk.startsWith("0x"))
3481                     chunk.remove(0, 2);
3482                 QTC_ASSERT(chunk.size() == 8, continue);
3483                 result.append(chunk);
3484             }
3485             reg->value.fromString(result, HexadecimalFormat);
3486         }
3487         handler->updateRegister(*reg);
3488     }
3489     handler->commitUpdates();
3490 }
3491 
handlePeripheralRegisterListValues(const DebuggerResponse & response)3492 void GdbEngine::handlePeripheralRegisterListValues(
3493         const DebuggerResponse &response)
3494 {
3495     if (response.resultClass != ResultDone)
3496         return;
3497 
3498     const QString output = response.consoleStreamOutput;
3499     // Regexp to match for '0x50060800:\t0\n'.
3500     const QRegularExpression re("^(0x[0-9A-Fa-f]+):\\t(\\d+)\\n$");
3501     const QRegularExpressionMatch m = re.match(output);
3502     if (!m.hasMatch())
3503         return;
3504     enum { AddressMatch = 1, ValueMatch = 2 };
3505     bool aok = false;
3506     bool vok = false;
3507     const quint64 address = m.captured(AddressMatch).toULongLong(&aok, 16);
3508     const quint64 value = m.captured(ValueMatch).toULongLong(&vok, 10);
3509     if (!aok || !vok)
3510         return;
3511 
3512     peripheralRegisterHandler()->updateRegister(address, value);
3513 }
3514 
3515 //////////////////////////////////////////////////////////////////////
3516 //
3517 // Watch specific stuff
3518 //
3519 //////////////////////////////////////////////////////////////////////
3520 
reloadLocals()3521 void GdbEngine::reloadLocals()
3522 {
3523     // if the engine is not running - do nothing
3524     if (state() == DebuggerState::DebuggerFinished || state() == DebuggerState::DebuggerNotReady)
3525         return;
3526 
3527     setTokenBarrier();
3528     updateLocals();
3529 }
3530 
handleVarAssign(const DebuggerResponse &)3531 void GdbEngine::handleVarAssign(const DebuggerResponse &)
3532 {
3533     // Everything might have changed, force re-evaluation.
3534     setTokenBarrier();
3535     updateLocals();
3536 }
3537 
assignValueInDebugger(WatchItem * item,const QString & expression,const QVariant & value)3538 void GdbEngine::assignValueInDebugger(WatchItem *item,
3539     const QString &expression, const QVariant &value)
3540 {
3541     DebuggerCommand cmd("assignValue");
3542     cmd.arg("type", toHex(item->type));
3543     cmd.arg("expr", toHex(expression));
3544     cmd.arg("value", toHex(value.toString()));
3545     cmd.arg("simpleType", isIntOrFloatType(item->type));
3546     cmd.callback = CB(handleVarAssign);
3547     runCommand(cmd);
3548 }
3549 
3550 class MemoryAgentCookie
3551 {
3552 public:
3553     MemoryAgentCookie() = default;
3554 
3555     QByteArray *accumulator = nullptr; // Shared between split request. Last one cleans up.
3556     uint *pendingRequests = nullptr; // Shared between split request. Last one cleans up.
3557 
3558     QPointer<MemoryAgent> agent;
3559     quint64 base = 0; // base address.
3560     uint offset = 0; // offset to base, and in accumulator
3561     uint length = 0; //
3562 };
3563 
3564 
changeMemory(MemoryAgent * agent,quint64 addr,const QByteArray & data)3565 void GdbEngine::changeMemory(MemoryAgent *agent, quint64 addr, const QByteArray &data)
3566 {
3567     Q_UNUSED(agent)
3568     DebuggerCommand cmd("-data-write-memory 0x" + QString::number(addr, 16) + " d 1", NeedsTemporaryStop);
3569     for (unsigned char c : data)
3570         cmd.function += ' ' + QString::number(uint(c));
3571     cmd.callback = CB(handleVarAssign);
3572     runCommand(cmd);
3573 }
3574 
fetchMemory(MemoryAgent * agent,quint64 addr,quint64 length)3575 void GdbEngine::fetchMemory(MemoryAgent *agent, quint64 addr, quint64 length)
3576 {
3577     MemoryAgentCookie ac;
3578     ac.accumulator = new QByteArray(length, char());
3579     ac.pendingRequests = new uint(1);
3580     ac.agent = agent;
3581     ac.base = addr;
3582     ac.length = length;
3583     fetchMemoryHelper(ac);
3584 }
3585 
fetchMemoryHelper(const MemoryAgentCookie & ac)3586 void GdbEngine::fetchMemoryHelper(const MemoryAgentCookie &ac)
3587 {
3588     DebuggerCommand cmd("-data-read-memory 0x"
3589                         + QString::number(ac.base + ac.offset, 16) + " x 1 1 "
3590                         + QString::number(ac.length),
3591                         NeedsTemporaryStop);
3592     cmd.callback = [this, ac](const DebuggerResponse &r) { handleFetchMemory(r, ac); };
3593     runCommand(cmd);
3594 }
3595 
handleFetchMemory(const DebuggerResponse & response,MemoryAgentCookie ac)3596 void GdbEngine::handleFetchMemory(const DebuggerResponse &response, MemoryAgentCookie ac)
3597 {
3598     // ^done,addr="0x08910c88",nr-bytes="16",total-bytes="16",
3599     // next-row="0x08910c98",prev-row="0x08910c78",next-page="0x08910c98",
3600     // prev-page="0x08910c78",memory=[{addr="0x08910c88",
3601     // data=["1","0","0","0","5","0","0","0","0","0","0","0","0","0","0","0"]}]
3602     --*ac.pendingRequests;
3603     showMessage(QString("PENDING: %1").arg(*ac.pendingRequests));
3604     QTC_ASSERT(ac.agent, return);
3605     if (response.resultClass == ResultDone) {
3606         GdbMi memory = response.data["memory"];
3607         QTC_ASSERT(memory.childCount() <= 1, return);
3608         if (memory.childCount() == 0)
3609             return;
3610         GdbMi memory0 = memory.childAt(0); // we asked for only one 'row'
3611         GdbMi data = memory0["data"];
3612         int i = 0;
3613         for (const GdbMi &child : data) {
3614             bool ok = true;
3615             unsigned char c = '?';
3616             c = child.data().toUInt(&ok, 0);
3617             QTC_ASSERT(ok, return);
3618             (*ac.accumulator)[ac.offset + i++] = c;
3619         }
3620     } else {
3621         // We have an error
3622         if (ac.length > 1) {
3623             // ... and size > 1, split the load and re-try.
3624             *ac.pendingRequests += 2;
3625             uint hunk = ac.length / 2;
3626             MemoryAgentCookie ac1 = ac;
3627             ac1.length = hunk;
3628             ac1.offset = ac.offset;
3629             MemoryAgentCookie ac2 = ac;
3630             ac2.length = ac.length - hunk;
3631             ac2.offset = ac.offset + hunk;
3632             fetchMemoryHelper(ac1);
3633             fetchMemoryHelper(ac2);
3634         }
3635     }
3636 
3637     if (*ac.pendingRequests <= 0) {
3638         ac.agent->addData(ac.base, *ac.accumulator);
3639         delete ac.pendingRequests;
3640         delete ac.accumulator;
3641     }
3642 }
3643 
3644 class DisassemblerAgentCookie
3645 {
3646 public:
DisassemblerAgentCookie()3647     DisassemblerAgentCookie() : agent(nullptr) {}
DisassemblerAgentCookie(DisassemblerAgent * agent_)3648     DisassemblerAgentCookie(DisassemblerAgent *agent_) : agent(agent_) {}
3649 
3650 public:
3651     QPointer<DisassemblerAgent> agent;
3652 };
3653 
fetchDisassembler(DisassemblerAgent * agent)3654 void GdbEngine::fetchDisassembler(DisassemblerAgent *agent)
3655 {
3656     if (debuggerSettings()->intelFlavor.value())
3657         runCommand({"set disassembly-flavor intel"});
3658     else
3659         runCommand({"set disassembly-flavor att"});
3660 
3661     fetchDisassemblerByCliPointMixed(agent);
3662 }
3663 
disassemblerCommand(const Location & location,bool mixed,QChar mixedFlag)3664 static inline QString disassemblerCommand(const Location &location, bool mixed, QChar mixedFlag)
3665 {
3666     QString command = "disassemble /r";
3667     if (mixed)
3668         command += mixedFlag;
3669     command += ' ';
3670     if (const quint64 address = location.address()) {
3671         command += "0x";
3672         command += QString::number(address, 16);
3673     } else if (!location.functionName().isEmpty()) {
3674         command += location.functionName();
3675     } else {
3676         QTC_ASSERT(false, return QString(); );
3677     }
3678     return command;
3679 }
3680 
fetchDisassemblerByCliPointMixed(const DisassemblerAgentCookie & ac)3681 void GdbEngine::fetchDisassemblerByCliPointMixed(const DisassemblerAgentCookie &ac)
3682 {
3683     QTC_ASSERT(ac.agent, return);
3684     DebuggerCommand cmd(disassemblerCommand(ac.agent->location(), true, mixedDisasmFlag()),
3685                         Discardable | ConsoleCommand);
3686     cmd.callback = [this, ac](const DebuggerResponse &response) {
3687         if (response.resultClass == ResultDone)
3688             if (handleCliDisassemblerResult(response.consoleStreamOutput, ac.agent))
3689                 return;
3690         // 'point, plain' can take far too long.
3691         // Skip this feature and immediately fall back to the 'range' version:
3692         fetchDisassemblerByCliRangeMixed(ac);
3693     };
3694     runCommand(cmd);
3695 }
3696 
fetchDisassemblerByCliRangeMixed(const DisassemblerAgentCookie & ac)3697 void GdbEngine::fetchDisassemblerByCliRangeMixed(const DisassemblerAgentCookie &ac)
3698 {
3699     QTC_ASSERT(ac.agent, return);
3700     const quint64 address = ac.agent->address();
3701     QString start = QString::number(address - 20, 16);
3702     QString end = QString::number(address + 100, 16);
3703     DebuggerCommand cmd("disassemble /r" + mixedDisasmFlag() + " 0x" + start + ",0x" + end,
3704                         Discardable | ConsoleCommand);
3705     cmd.callback = [this, ac](const DebuggerResponse &response) {
3706         if (response.resultClass == ResultDone)
3707             if (handleCliDisassemblerResult(response.consoleStreamOutput, ac.agent))
3708                 return;
3709         fetchDisassemblerByCliRangePlain(ac);
3710     };
3711     runCommand(cmd);
3712 }
3713 
fetchDisassemblerByCliRangePlain(const DisassemblerAgentCookie & ac0)3714 void GdbEngine::fetchDisassemblerByCliRangePlain(const DisassemblerAgentCookie &ac0)
3715 {
3716     DisassemblerAgentCookie ac = ac0;
3717     QTC_ASSERT(ac.agent, return);
3718     const quint64 address = ac.agent->address();
3719     QString start = QString::number(address - 20, 16);
3720     QString end = QString::number(address + 100, 16);
3721     DebuggerCommand cmd("disassemble /r 0x" + start + ",0x" + end, Discardable);
3722     cmd.callback = [this, ac](const DebuggerResponse &response) {
3723         if (response.resultClass == ResultDone)
3724             if (handleCliDisassemblerResult(response.consoleStreamOutput, ac.agent))
3725                 return;
3726         // Finally, give up.
3727         //76^error,msg="No function contains program counter for selected..."
3728         //76^error,msg="No function contains specified address."
3729         //>568^error,msg="Line number 0 out of range;
3730         QString msg = response.data["msg"].data();
3731         showStatusMessage(tr("Disassembler failed: %1").arg(msg), 5000);
3732     };
3733     runCommand(cmd);
3734 }
3735 
3736 struct LineData
3737 {
3738     LineData() = default;
LineDataDebugger::Internal::LineData3739     LineData(int i, int f) : index(i), function(f) {}
3740     int index;
3741     int function;
3742 };
3743 
handleCliDisassemblerResult(const QString & output,DisassemblerAgent * agent)3744 bool GdbEngine::handleCliDisassemblerResult(const QString &output, DisassemblerAgent *agent)
3745 {
3746     QTC_ASSERT(agent, return true);
3747     // First line is something like
3748     // "Dump of assembler code from 0xb7ff598f to 0xb7ff5a07:"
3749     DisassemblerLines dlines;
3750     foreach (const QString &line, output.split('\n'))
3751         dlines.appendUnparsed(line);
3752 
3753     QVector<DisassemblerLine> lines = dlines.data();
3754 
3755     using LineMap = QMap<quint64, LineData>;
3756     LineMap lineMap;
3757     int currentFunction = -1;
3758     for (int i = 0, n = lines.size(); i != n; ++i) {
3759         const DisassemblerLine &line = lines.at(i);
3760         if (line.address)
3761             lineMap.insert(line.address, LineData(i, currentFunction));
3762         else
3763             currentFunction = i;
3764     }
3765 
3766     currentFunction = -1;
3767     DisassemblerLines result;
3768     result.setBytesLength(dlines.bytesLength());
3769     for (LineMap::const_iterator it = lineMap.constBegin(), et = lineMap.constEnd(); it != et; ++it) {
3770         LineData d = *it;
3771         if (d.function != currentFunction) {
3772             if (d.function != -1) {
3773                 DisassemblerLine &line = lines[d.function];
3774                 ++line.hunk;
3775                 result.appendLine(line);
3776                 currentFunction = d.function;
3777             }
3778         }
3779         result.appendLine(lines.at(d.index));
3780     }
3781 
3782     if (result.coversAddress(agent->address())) {
3783         agent->setContents(result);
3784         return true;
3785     }
3786 
3787     return false;
3788 }
3789 
mergeStartParametersSourcePathMap(const DebuggerRunParameters & sp,const SourcePathMap & in)3790 static SourcePathMap mergeStartParametersSourcePathMap(const DebuggerRunParameters &sp,
3791                                                        const SourcePathMap &in)
3792 {
3793     // Do not overwrite user settings.
3794     SourcePathMap rc = sp.sourcePathMap;
3795     for (auto it = in.constBegin(), end = in.constEnd(); it != end; ++it)
3796         rc.insert(it.key(), it.value());
3797     return rc;
3798 }
3799 
3800 //
3801 // Starting up & shutting down
3802 //
3803 
setupEngine()3804 void GdbEngine::setupEngine()
3805 {
3806     CHECK_STATE(EngineSetupRequested);
3807     showMessage("TRYING TO START ADAPTER");
3808 
3809     if (isRemoteEngine())
3810         m_gdbProc.setUseCtrlCStub(runParameters().useCtrlCStub); // This is only set for QNX
3811 
3812     const DebuggerRunParameters &rp = runParameters();
3813     CommandLine gdbCommand{rp.debugger.executable};
3814 
3815     if (usesOutputCollector()) {
3816         if (!m_outputCollector.listen()) {
3817             handleAdapterStartFailed(tr("Cannot set up communication with child process: %1")
3818                                      .arg(m_outputCollector.errorString()));
3819             return;
3820         }
3821         gdbCommand.addArg("--tty=" + m_outputCollector.serverName());
3822     }
3823 
3824     const QString tests = QString::fromLocal8Bit(qgetenv("QTC_DEBUGGER_TESTS"));
3825     foreach (const QString &test, tests.split(','))
3826         m_testCases.insert(test.toInt());
3827     foreach (int test, m_testCases)
3828         showMessage("ENABLING TEST CASE: " + QString::number(test));
3829 
3830     m_expectTerminalTrap = terminal();
3831 
3832     if (rp.debugger.executable.isEmpty()) {
3833         handleGdbStartFailed();
3834         handleAdapterStartFailed(
3835             msgNoGdbBinaryForToolChain(rp.toolChainAbi),
3836             Constants::DEBUGGER_COMMON_SETTINGS_ID);
3837         return;
3838     }
3839 
3840     gdbCommand.addArgs({"-i", "mi"});
3841     if (!debuggerSettings()->loadGdbInit.value())
3842         gdbCommand.addArg("-n");
3843 
3844     Environment gdbEnv = rp.debugger.environment;
3845     if (rp.runAsRoot) {
3846         CommandLine wrapped("sudo", {"-A"});
3847         wrapped.addArgs(gdbCommand);
3848         gdbCommand = wrapped;
3849         RunControl::provideAskPassEntry(gdbEnv);
3850     }
3851 
3852     showMessage("STARTING " + gdbCommand.toUserOutput());
3853 
3854     m_gdbProc.setCommand(gdbCommand);
3855     if (QFileInfo(rp.debugger.workingDirectory).isDir())
3856         m_gdbProc.setWorkingDirectory(rp.debugger.workingDirectory);
3857     m_gdbProc.setEnvironment(gdbEnv);
3858     m_gdbProc.setKeepWriteChannelOpen();
3859     m_gdbProc.start();
3860 
3861     if (!m_gdbProc.waitForStarted()) {
3862         handleGdbStartFailed();
3863         QString msg;
3864         FilePath wd = m_gdbProc.workingDirectory();
3865         if (!wd.isReadableDir())
3866             msg = failedToStartMessage() + ' ' + tr("The working directory \"%1\" is not usable.")
3867                 .arg(wd.toUserOutput());
3868         else
3869             msg = RunWorker::userMessageForProcessError(QProcess::FailedToStart, rp.debugger.executable);
3870         handleAdapterStartFailed(msg);
3871         return;
3872     }
3873 
3874     showMessage("GDB STARTED, INITIALIZING IT");
3875     runCommand({"show version", CB(handleShowVersion)});
3876     runCommand({"show debug-file-directory", CB(handleDebugInfoLocation)});
3877 
3878     //runCommand("-enable-timings");
3879     //rurun print static-members off"); // Seemingly doesn't work.
3880     //runCommand("set debug infrun 1");
3881     //runCommand("define hook-stop\n-thread-list-ids\n-stack-list-frames\nend");
3882     //runCommand("define hook-stop\nprint 4\nend");
3883     //runCommand("define hookpost-stop\nprint 5\nend");
3884     //runCommand("define hook-call\nprint 6\nend");
3885     //runCommand("define hookpost-call\nprint 7\nend");
3886     //runCommand("set step-mode on");  // we can't work with that yes
3887     //runCommand("set exec-done-display on");
3888     //runCommand("set print pretty on");
3889     //runCommand("set confirm off");
3890     //runCommand("set pagination off");
3891 
3892     // The following does not work with 6.3.50-20050815 (Apple version gdb-1344)
3893     // (Mac OS 10.6), but does so for gdb-966 (10.5):
3894     //runCommand("set print inferior-events 1");
3895 
3896     runCommand({"set breakpoint pending on"});
3897     runCommand({"set print elements 10000"});
3898 
3899     if (debuggerSettings()->useIndexCache.value())
3900         runCommand({"set index-cache on"});
3901 
3902     // Produces a few messages during symtab loading
3903     //runCommand("set verbose on");
3904 
3905     // one of the following is needed to prevent crashes in gdb on code like:
3906     //  template <class T> T foo() { return T(0); }
3907     //  int main() { return foo<int>(); }
3908     //  (gdb) call 'int foo<int>'()
3909     //  /build/buildd/gdb-6.8/gdb/valops.c:2069: internal-error
3910     // This seems to be fixed, however, with 'on' it seems to _require_
3911     // explicit casting of function pointers:
3912     // GNU gdb (GDB) 7.5.91.20130417-cvs-ubuntu
3913     //  (gdb) p &Myns::QMetaType::typeName  -> $1 = (const char *(*)(int)) 0xb7cf73b0 <Myns::QMetaType::typeName(int)>
3914     //  (gdb) p Myns::QMetaType::typeName(1024)  -> 31^error,msg="Couldn't find method Myns::QMetaType::typeName"
3915     // But we can work around on the dumper side. So let's use the default (i.e. 'on')
3916     //runCommand("set overload-resolution off");
3917 
3918     //runCommand(_("set demangle-style none"));
3919     runCommand({"set unwindonsignal on"});
3920     runCommand({"set width 0"});
3921     runCommand({"set height 0"});
3922 
3923     // FIXME: Provide proper Gui settings for these:
3924     //runCommand("set breakpoint always-inserted on", ConsoleCommand);
3925     // displaced-stepping does not work in Thumb mode.
3926     //runCommand("set displaced-stepping on");
3927     //runCommand("set trust-readonly-sections on", ConsoleCommand);
3928     //runCommand("set remotecache on", ConsoleCommand);
3929     //runCommand("set non-stop on", ConsoleCommand);
3930 
3931     showStatusMessage(tr("Setting up inferior..."));
3932 
3933     // Addint executable to modules list.
3934     Module module;
3935     module.startAddress = 0;
3936     module.endAddress = 0;
3937     module.modulePath = rp.inferior.executable.toString();
3938     module.moduleName = "<executable>";
3939     modulesHandler()->updateModule(module);
3940 
3941     // Apply source path mappings from global options.
3942     //showMessage(_("Assuming Qt is installed at %1").arg(qtInstallPath));
3943     const SourcePathMap sourcePathMap =
3944             mergePlatformQtPath(rp, debuggerSettings()->sourcePathMap.value());
3945     const SourcePathMap completeSourcePathMap =
3946             mergeStartParametersSourcePathMap(rp, sourcePathMap);
3947     for (auto it = completeSourcePathMap.constBegin(), cend = completeSourcePathMap.constEnd();
3948          it != cend;
3949          ++it) {
3950         runCommand({"set substitute-path " + it.key() + " " + expand(it.value())});
3951     }
3952 
3953     // Spaces just will not work.
3954     for (const QString &src : rp.debugSourceLocation) {
3955         if (QDir(src).exists())
3956             runCommand({"directory " + src});
3957         else
3958             showMessage("# directory does not exist: " + src, LogInput);
3959     }
3960 
3961     if (!rp.sysRoot.isEmpty()) {
3962         runCommand({"set sysroot " + rp.sysRoot.toString()});
3963         // sysroot is not enough to correctly locate the sources, so explicitly
3964         // relocate the most likely place for the debug source
3965         runCommand({"set substitute-path /usr/src " + rp.sysRoot.toString() + "/usr/src"});
3966     }
3967 
3968     //QByteArray ba = QFileInfo(sp.dumperLibrary).path().toLocal8Bit();
3969     //if (!ba.isEmpty())
3970     //    runCommand("set solib-search-path " + ba);
3971 
3972     if (debuggerSettings()->multiInferior.value() || runParameters().multiProcess) {
3973         //runCommand("set follow-exec-mode new");
3974         runCommand({"set detach-on-fork off"});
3975     }
3976 
3977     // Finally, set up Python.
3978     // We need to guarantee a roundtrip before the adapter proceeds.
3979     // Make sure this stays the last command in startGdb().
3980     // Don't use ConsoleCommand, otherwise Mac won't markup the output.
3981     const QString dumperSourcePath = ICore::resourcePath("debugger/").toString();
3982 
3983     //if (terminal()->isUsable())
3984     //    runCommand({"set inferior-tty " + QString::fromUtf8(terminal()->slaveDevice())});
3985 
3986     const QFileInfo gdbBinaryFile = rp.debugger.executable.toFileInfo();
3987     const QString uninstalledData = gdbBinaryFile.absolutePath() + "/data-directory/python";
3988 
3989     runCommand({"python sys.path.insert(1, '" + dumperSourcePath + "')"});
3990     runCommand({"python sys.path.append('" + uninstalledData + "')"});
3991     runCommand({"python from gdbbridge import *"});
3992 
3993     const QString path = debuggerSettings()->extraDumperFile.value();
3994     if (!path.isEmpty() && QFileInfo(path).isReadable()) {
3995         DebuggerCommand cmd("addDumperModule");
3996         cmd.arg("path", path);
3997         runCommand(cmd);
3998     }
3999 
4000     const QString commands = debuggerSettings()->extraDumperCommands.value();
4001     if (!commands.isEmpty())
4002         runCommand({commands});
4003 
4004     runCommand({"loadDumpers", CB(handlePythonSetup)});
4005 
4006     // Reload peripheral register description.
4007     peripheralRegisterHandler()->updateRegisterGroups();
4008 }
4009 
handleGdbStartFailed()4010 void GdbEngine::handleGdbStartFailed()
4011 {
4012     if (usesOutputCollector())
4013         m_outputCollector.shutdown();
4014 }
4015 
loadInitScript()4016 void GdbEngine::loadInitScript()
4017 {
4018     const QString script = runParameters().overrideStartScript;
4019     if (!script.isEmpty()) {
4020         if (QFileInfo(script).isReadable()) {
4021             runCommand({"source " + script});
4022         } else {
4023             AsynchronousMessageBox::warning(
4024             tr("Cannot Find Debugger Initialization Script"),
4025             tr("The debugger settings point to a script file at \"%1\", "
4026                "which is not accessible. If a script file is not needed, "
4027                "consider clearing that entry to avoid this warning."
4028               ).arg(script));
4029         }
4030     } else {
4031         const QString commands = nativeStartupCommands().trimmed();
4032         if (!commands.isEmpty())
4033             runCommand({commands});
4034     }
4035 }
4036 
setEnvironmentVariables()4037 void GdbEngine::setEnvironmentVariables()
4038 {
4039     auto isWindowsPath = [this](const QString &str){
4040         return HostOsInfo::isWindowsHost()
4041                 && !isRemoteEngine()
4042                 && str.compare("path", Qt::CaseInsensitive) == 0;
4043     };
4044 
4045     Environment sysEnv = Environment::systemEnvironment();
4046     Environment runEnv = runParameters().inferior.environment;
4047     foreach (const EnvironmentItem &item, sysEnv.diff(runEnv)) {
4048         // imitate the weird windows gdb behavior of setting the case of the path environment
4049         // variable name to an all uppercase PATH
4050         const QString name = isWindowsPath(item.name) ? "PATH" : item.name;
4051         if (item.operation == EnvironmentItem::Unset
4052                 || item.operation == EnvironmentItem::SetDisabled) {
4053             runCommand({"unset environment " + name});
4054         } else {
4055             runCommand({"-gdb-set environment " + name + '=' + item.value});
4056         }
4057     }
4058 }
4059 
reloadDebuggingHelpers()4060 void GdbEngine::reloadDebuggingHelpers()
4061 {
4062     runCommand({"reloadDumpers"});
4063     reloadLocals();
4064 }
4065 
handleGdbError(QProcess::ProcessError error)4066 void GdbEngine::handleGdbError(QProcess::ProcessError error)
4067 {
4068     QString msg = RunWorker::userMessageForProcessError(error, runParameters().debugger.executable);
4069     QString errorString = m_gdbProc.errorString();
4070     if (!errorString.isEmpty())
4071         msg += '\n' + errorString;
4072     showMessage("HANDLE GDB ERROR: " + msg);
4073     // Show a message box for asynchronously reported issues.
4074     switch (error) {
4075     case QProcess::FailedToStart:
4076         // This should be handled by the code trying to start the process.
4077         break;
4078     case QProcess::Crashed:
4079         // At this time, m_gdbProc.state() can still return Running.
4080         // Wait for finished() instead.
4081         break;
4082     case QProcess::ReadError:
4083     case QProcess::WriteError:
4084     case QProcess::Timedout:
4085     default:
4086         //m_gdbProc->kill();
4087         //notifyEngineIll();
4088         AsynchronousMessageBox::critical(tr("GDB I/O Error"), msg);
4089         break;
4090     }
4091 }
4092 
handleGdbFinished()4093 void GdbEngine::handleGdbFinished()
4094 {
4095     if (m_commandTimer.isActive())
4096         m_commandTimer.stop();
4097 
4098     notifyDebuggerProcessFinished(m_gdbProc.exitCode(), m_gdbProc.exitStatus(), "GDB");
4099 }
4100 
abortDebuggerProcess()4101 void GdbEngine::abortDebuggerProcess()
4102 {
4103     m_gdbProc.kill();
4104 }
4105 
resetInferior()4106 void GdbEngine::resetInferior()
4107 {
4108     if (!runParameters().commandsForReset.isEmpty()) {
4109         const QString commands = expand(runParameters().commandsForReset);
4110         foreach (QString command, commands.split('\n')) {
4111             command = command.trimmed();
4112             if (!command.isEmpty())
4113                 runCommand({command, ConsoleCommand | NeedsTemporaryStop | NativeCommand});
4114         }
4115     }
4116     m_rerunPending = true;
4117     requestInterruptInferior();
4118     runEngine();
4119 }
4120 
handleAdapterStartFailed(const QString & msg,Id settingsIdHint)4121 void GdbEngine::handleAdapterStartFailed(const QString &msg, Id settingsIdHint)
4122 {
4123     showMessage("ADAPTER START FAILED");
4124     if (!msg.isEmpty() && !Internal::isTestRun()) {
4125         const QString title = tr("Adapter Start Failed");
4126         ICore::showWarningWithOptions(title, msg, QString(), settingsIdHint);
4127     }
4128     notifyEngineSetupFailed();
4129 }
4130 
prepareForRestart()4131 void GdbEngine::prepareForRestart()
4132 {
4133     m_rerunPending = false;
4134     m_commandForToken.clear();
4135     m_flagsForToken.clear();
4136 }
4137 
handleInferiorPrepared()4138 void GdbEngine::handleInferiorPrepared()
4139 {
4140     CHECK_STATE(EngineSetupRequested);
4141 
4142     notifyEngineSetupOk();
4143     runEngine();
4144 }
4145 
handleDebugInfoLocation(const DebuggerResponse & response)4146 void GdbEngine::handleDebugInfoLocation(const DebuggerResponse &response)
4147 {
4148     if (response.resultClass == ResultDone) {
4149         const QString debugInfoLocation = runParameters().debugInfoLocation;
4150         if (!debugInfoLocation.isEmpty() && QFile::exists(debugInfoLocation)) {
4151             const QString curDebugInfoLocations = response.consoleStreamOutput.split('"').value(1);
4152             QString cmd = "set debug-file-directory " + debugInfoLocation;
4153             if (!curDebugInfoLocations.isEmpty())
4154                 cmd += HostOsInfo::pathListSeparator() + curDebugInfoLocations;
4155             runCommand({cmd});
4156         }
4157     }
4158 }
4159 
notifyInferiorSetupFailedHelper(const QString & msg)4160 void GdbEngine::notifyInferiorSetupFailedHelper(const QString &msg)
4161 {
4162     showStatusMessage(tr("Failed to start application:") + ' ' + msg);
4163     if (state() == EngineSetupFailed) {
4164         showMessage("INFERIOR START FAILED, BUT ADAPTER DIED ALREADY");
4165         return; // Adapter crashed meanwhile, so this notification is meaningless.
4166     }
4167     showMessage("INFERIOR START FAILED");
4168     AsynchronousMessageBox::critical(tr("Failed to Start Application"), msg);
4169     notifyEngineSetupFailed();
4170 }
4171 
createFullBacktrace()4172 void GdbEngine::createFullBacktrace()
4173 {
4174     DebuggerCommand cmd("thread apply all bt full", NeedsTemporaryStop | ConsoleCommand);
4175     cmd.callback = [](const DebuggerResponse &response) {
4176         if (response.resultClass == ResultDone) {
4177             Internal::openTextEditor("Backtrace $",
4178                 response.consoleStreamOutput + response.logStreamOutput);
4179         }
4180     };
4181     runCommand(cmd);
4182 }
4183 
resetCommandQueue()4184 void GdbEngine::resetCommandQueue()
4185 {
4186     m_commandTimer.stop();
4187     if (!m_commandForToken.isEmpty()) {
4188         QString msg;
4189         QTextStream ts(&msg);
4190         ts << "RESETING COMMAND QUEUE. LEFT OVER TOKENS: ";
4191         foreach (const DebuggerCommand &cmd, m_commandForToken)
4192             ts << "CMD:" << cmd.function;
4193         m_commandForToken.clear();
4194         m_flagsForToken.clear();
4195         showMessage(msg);
4196     }
4197 }
4198 
usesExecInterrupt() const4199 bool GdbEngine::usesExecInterrupt() const
4200 {
4201     DebuggerStartMode mode = runParameters().startMode;
4202     return (mode == AttachToRemoteServer || mode == AttachToRemoteProcess)
4203             && usesTargetAsync();
4204 }
4205 
usesTargetAsync() const4206 bool GdbEngine::usesTargetAsync() const
4207 {
4208     return runParameters().useTargetAsync || debuggerSettings()->targetAsync.value();
4209 }
4210 
scheduleTestResponse(int testCase,const QString & response)4211 void GdbEngine::scheduleTestResponse(int testCase, const QString &response)
4212 {
4213     if (!m_testCases.contains(testCase) && runParameters().testCase != testCase)
4214         return;
4215 
4216     int token = currentToken() + 1;
4217     showMessage(QString("SCHEDULING TEST RESPONSE (CASE: %1, TOKEN: %2, RESPONSE: %3)")
4218         .arg(testCase).arg(token).arg(response));
4219     m_scheduledTestResponses[token] = response;
4220 }
4221 
msgGdbStopFailed(const QString & why)4222 QString GdbEngine::msgGdbStopFailed(const QString &why)
4223 {
4224     return tr("The gdb process could not be stopped:\n%1").arg(why);
4225 }
4226 
msgInferiorStopFailed(const QString & why)4227 QString GdbEngine::msgInferiorStopFailed(const QString &why)
4228 {
4229     return tr("Application process could not be stopped:\n%1").arg(why);
4230 }
4231 
msgInferiorSetupOk()4232 QString GdbEngine::msgInferiorSetupOk()
4233 {
4234     return tr("Application started.");
4235 }
4236 
msgInferiorRunOk()4237 QString GdbEngine::msgInferiorRunOk()
4238 {
4239     return tr("Application running.");
4240 }
4241 
msgAttachedToStoppedInferior()4242 QString GdbEngine::msgAttachedToStoppedInferior()
4243 {
4244     return tr("Attached to stopped application.");
4245 }
4246 
msgConnectRemoteServerFailed(const QString & why)4247 QString GdbEngine::msgConnectRemoteServerFailed(const QString &why)
4248 {
4249     return tr("Connecting to remote server failed:\n%1").arg(why);
4250 }
4251 
interruptLocalInferior(qint64 pid)4252 void GdbEngine::interruptLocalInferior(qint64 pid)
4253 {
4254     CHECK_STATE(InferiorStopRequested);
4255     if (pid <= 0) {
4256         showMessage("TRYING TO INTERRUPT INFERIOR BEFORE PID WAS OBTAINED", LogError);
4257         return;
4258     }
4259     QString errorMessage;
4260     if (runParameters().runAsRoot) {
4261         Environment env = Environment::systemEnvironment();
4262         RunControl::provideAskPassEntry(env);
4263         QtcProcess proc;
4264         proc.setCommand(CommandLine{"sudo", {"-A", "kill", "-s", "SIGINT", QString::number(pid)}});
4265         proc.setEnvironment(env);
4266         proc.start();
4267         proc.waitForFinished();
4268     } else if (interruptProcess(pid, GdbEngineType, &errorMessage)) {
4269         showMessage("Interrupted " + QString::number(pid));
4270     } else {
4271         showMessage(errorMessage, LogError);
4272         notifyInferiorStopFailed();
4273     }
4274 }
4275 
debugLastCommand()4276 void GdbEngine::debugLastCommand()
4277 {
4278     runCommand(m_lastDebuggableCommand);
4279 }
4280 
isLocalRunEngine() const4281 bool GdbEngine::isLocalRunEngine() const
4282 {
4283     return !isCoreEngine() && !isLocalAttachEngine() && !isRemoteEngine();
4284 }
4285 
isPlainEngine() const4286 bool GdbEngine::isPlainEngine() const
4287 {
4288     return isLocalRunEngine() && !terminal();
4289 }
4290 
isCoreEngine() const4291 bool GdbEngine::isCoreEngine() const
4292 {
4293     return runParameters().startMode == AttachToCore;
4294 }
4295 
isRemoteEngine() const4296 bool GdbEngine::isRemoteEngine() const
4297 {
4298     DebuggerStartMode startMode = runParameters().startMode;
4299     return startMode == StartRemoteProcess || startMode == AttachToRemoteServer;
4300 }
4301 
isLocalAttachEngine() const4302 bool GdbEngine::isLocalAttachEngine() const
4303 {
4304     return runParameters().startMode == AttachToLocalProcess;
4305 }
4306 
isTermEngine() const4307 bool GdbEngine::isTermEngine() const
4308 {
4309     return isLocalRunEngine() && terminal();
4310 }
4311 
usesOutputCollector() const4312 bool GdbEngine::usesOutputCollector() const
4313 {
4314     return isPlainEngine() && !runParameters().debugger.executable.needsDevice();
4315 }
4316 
claimInitialBreakpoints()4317 void GdbEngine::claimInitialBreakpoints()
4318 {
4319     CHECK_STATE(EngineRunRequested);
4320 
4321     const DebuggerRunParameters &rp = runParameters();
4322     if (rp.startMode != AttachToCore) {
4323         showStatusMessage(tr("Setting breakpoints..."));
4324         showMessage(tr("Setting breakpoints..."));
4325         BreakpointManager::claimBreakpointsForEngine(this);
4326 
4327         const DebuggerSettings &s = *debuggerSettings();
4328         const bool onAbort = s.breakOnAbort.value();
4329         const bool onWarning = s.breakOnWarning.value();
4330         const bool onFatal = s.breakOnFatal.value();
4331         if (onAbort || onWarning || onFatal) {
4332             DebuggerCommand cmd("createSpecialBreakpoints");
4333             cmd.arg("breakonabort", onAbort);
4334             cmd.arg("breakonwarning", onWarning);
4335             cmd.arg("breakonfatal", onFatal);
4336             runCommand(cmd);
4337         }
4338     }
4339 
4340     // It is ok to cut corners here and not wait for createSpecialBreakpoints()'s
4341     // response, as the command is synchronous from Creator's point of view,
4342     // and even if it fails (e.g. due to stripped binaries), continuing with
4343     // the start up is the best we can do.
4344 
4345     if (!rp.commandsAfterConnect.isEmpty()) {
4346         const QString commands = expand(rp.commandsAfterConnect);
4347         for (const QString &command : commands.split('\n'))
4348             runCommand({command, NativeCommand});
4349     }
4350 }
4351 
setupInferior()4352 void GdbEngine::setupInferior()
4353 {
4354     CHECK_STATE(EngineSetupRequested);
4355 
4356     const DebuggerRunParameters &rp = runParameters();
4357 
4358     //runCommand("set follow-exec-mode new");
4359     if (rp.breakOnMain)
4360         runCommand({"tbreak " + mainFunction()});
4361 
4362     if (rp.startMode == AttachToRemoteProcess) {
4363 
4364         handleInferiorPrepared();
4365 
4366     } else if (isLocalAttachEngine()) {
4367         // Task 254674 does not want to remove them
4368         //qq->breakHandler()->removeAllBreakpoints();
4369         handleInferiorPrepared();
4370 
4371     } else if (isRemoteEngine()) {
4372 
4373         setLinuxOsAbi();
4374         QString symbolFile;
4375         if (!rp.symbolFile.isEmpty())
4376             symbolFile = rp.symbolFile.toFileInfo().absoluteFilePath();
4377 
4378         //const QByteArray sysroot = sp.sysroot.toLocal8Bit();
4379         //const QByteArray remoteArch = sp.remoteArchitecture.toLatin1();
4380         const QString args = runParameters().inferior.commandLineArguments;
4381 
4382     //    if (!remoteArch.isEmpty())
4383     //        postCommand("set architecture " + remoteArch);
4384         if (!rp.solibSearchPath.isEmpty()) {
4385             DebuggerCommand cmd("appendSolibSearchPath");
4386             cmd.arg("path", rp.solibSearchPath);
4387             cmd.arg("separator", HostOsInfo::pathListSeparator());
4388             runCommand(cmd);
4389         }
4390 
4391         if (!args.isEmpty())
4392             runCommand({"-exec-arguments " + args});
4393 
4394         setEnvironmentVariables();
4395 
4396         // This has to be issued before 'target remote'. On pre-7.0 the
4397         // command is not present and will result in ' No symbol table is
4398         // loaded.  Use the "file" command.' as gdb tries to set the
4399         // value of a variable with name 'target-async'.
4400         //
4401         // Testing with -list-target-features which was introduced at
4402         // the same time would not work either, as this need an existing
4403         // target.
4404         //
4405         // Using it even without a target and having it fail might still
4406         // be better as:
4407         // Some external comment: '[but] "set target-async on" with a native
4408         // windows gdb will work, but then fail when you actually do
4409         // "run"/"attach", I think..
4410 
4411 
4412         // gdb/mi/mi-main.c:1958: internal-error:
4413         // mi_execute_async_cli_command: Assertion `is_running (inferior_ptid)'
4414         // failed.\nA problem internal to GDB has been detected,[...]
4415         if (usesTargetAsync())
4416             runCommand({"set target-async on", CB(handleSetTargetAsync)});
4417 
4418         if (symbolFile.isEmpty()) {
4419             showMessage(tr("No symbol file given."), StatusBar);
4420             callTargetRemote();
4421         } else {
4422             runCommand({"-file-exec-and-symbols \"" + symbolFile + '"',
4423                         CB(handleFileExecAndSymbols)});
4424         }
4425 
4426     } else if (isCoreEngine()) {
4427 
4428         setLinuxOsAbi();
4429 
4430         FilePath executable = rp.inferior.executable;
4431 
4432         if (executable.isEmpty()) {
4433             CoreInfo cinfo = CoreInfo::readExecutableNameFromCore(rp.debugger, rp.coreFile);
4434 
4435             if (!cinfo.isCore) {
4436                 AsynchronousMessageBox::warning(tr("Error Loading Core File"),
4437                                                 tr("The specified file does not appear to be a core file."));
4438                 notifyEngineSetupFailed();
4439                 return;
4440             }
4441 
4442             executable = FilePath::fromString(cinfo.foundExecutableName);
4443             if (executable.isEmpty()) {
4444                 AsynchronousMessageBox::warning(tr("Error Loading Symbols"),
4445                                                 tr("No executable to load symbols from specified core."));
4446                 notifyEngineSetupFailed();
4447                 return;
4448             }
4449         }
4450 
4451         // Do that first, otherwise no symbols are loaded.
4452         QFileInfo fi = executable.toFileInfo();
4453         QString path = fi.absoluteFilePath();
4454         // This is *not* equivalent to -file-exec-and-symbols. If the file is not executable
4455         // (contains only debugging symbols), this should still work.
4456         runCommand({"-file-exec-file \"" + path + '"'});
4457         runCommand({"-file-symbol-file \"" + path + '"',
4458                     CB(handleFileExecAndSymbols)});
4459 
4460     } else if (isTermEngine()) {
4461 
4462         const qint64 attachedPID = terminal()->applicationPid();
4463         const qint64 attachedMainThreadID = terminal()->applicationMainThreadId();
4464         notifyInferiorPid(ProcessHandle(attachedPID));
4465         const QString msg = (attachedMainThreadID != -1)
4466                 ? QString("Going to attach to %1 (%2)").arg(attachedPID).arg(attachedMainThreadID)
4467                 : QString("Going to attach to %1").arg(attachedPID);
4468         showMessage(msg, LogMisc);
4469         // For some reason, this breaks GDB 9 on Linux. See QTCREATORBUG-26299.
4470         if (HostOsInfo::isWindowsHost() && m_gdbVersion >= 100000) {
4471             // Required for debugging MinGW32 apps with 64-bit GDB. See QTCREATORBUG-26208.
4472             const QString executable = runParameters().inferior.executable.toFileInfo().absoluteFilePath();
4473             runCommand({"-file-exec-and-symbols \"" + executable + '"',
4474                         CB(handleFileExecAndSymbols)});
4475         } else {
4476             handleInferiorPrepared();
4477         }
4478     } else if (isPlainEngine()) {
4479 
4480         setEnvironmentVariables();
4481         if (!rp.inferior.workingDirectory.isEmpty())
4482             runCommand({"cd " + rp.inferior.workingDirectory});
4483         if (!rp.inferior.commandLineArguments.isEmpty()) {
4484             QString args = rp.inferior.commandLineArguments;
4485             runCommand({"-exec-arguments " + args});
4486         }
4487 
4488         QString executable = runParameters().inferior.executable.toFileInfo().absoluteFilePath();
4489         runCommand({"-file-exec-and-symbols \"" + executable + '"',
4490                     CB(handleFileExecAndSymbols)});
4491     }
4492 }
4493 
runEngine()4494 void GdbEngine::runEngine()
4495 {
4496     CHECK_STATE(EngineRunRequested);
4497 
4498     const DebuggerRunParameters &rp = runParameters();
4499 
4500     if (rp.startMode == AttachToRemoteProcess) {
4501 
4502         claimInitialBreakpoints();
4503         notifyEngineRunAndInferiorStopOk();
4504 
4505         QString channel = rp.remoteChannel;
4506         runCommand({"target remote " + channel});
4507 
4508     } else if (isLocalAttachEngine()) {
4509 
4510         const qint64 pid = rp.attachPID.pid();
4511         showStatusMessage(tr("Attaching to process %1.").arg(pid));
4512         runCommand({"attach " + QString::number(pid), [this](const DebuggerResponse &r) {
4513                         handleLocalAttach(r);
4514                     }});
4515         // In some cases we get only output like
4516         //   "Could not attach to process.  If your uid matches the uid of the target\n"
4517         //   "process, check the setting of /proc/sys/kernel/yama/ptrace_scope, or try\n"
4518         //   " again as the root user.  For more details, see /etc/sysctl.d/10-ptrace.conf\n"
4519         //   " ptrace: Operation not permitted.\n"
4520         // but no(!) ^ response. Use a second command to force *some* output
4521         runCommand({"print 24"});
4522 
4523     } else if (isRemoteEngine()) {
4524 
4525         claimInitialBreakpoints();
4526         if (runParameters().useContinueInsteadOfRun) {
4527             notifyEngineRunAndInferiorStopOk();
4528             continueInferiorInternal();
4529         } else {
4530             runCommand({"-exec-run", DebuggerCommand::RunRequest, CB(handleExecRun)});
4531         }
4532 
4533     } else if (isCoreEngine()) {
4534 
4535         claimInitialBreakpoints();
4536         runCommand({"target core " + runParameters().coreFile, CB(handleTargetCore)});
4537 
4538     } else if (isTermEngine()) {
4539 
4540         const qint64 attachedPID = terminal()->applicationPid();
4541         const qint64 mainThreadId = terminal()->applicationMainThreadId();
4542         runCommand({"attach " + QString::number(attachedPID),
4543                     [this, mainThreadId](const DebuggerResponse &r) {
4544                         handleStubAttached(r, mainThreadId);
4545                     }});
4546 
4547     } else if (isPlainEngine()) {
4548 
4549         claimInitialBreakpoints();
4550         if (runParameters().useContinueInsteadOfRun)
4551             runCommand({"-exec-continue", DebuggerCommand::RunRequest, CB(handleExecuteContinue)});
4552         else
4553             runCommand({"-exec-run", DebuggerCommand::RunRequest, CB(handleExecRun)});
4554 
4555     }
4556 }
4557 
handleLocalAttach(const DebuggerResponse & response)4558 void GdbEngine::handleLocalAttach(const DebuggerResponse &response)
4559 {
4560     QTC_ASSERT(state() == EngineRunRequested || state() == InferiorStopOk, qDebug() << state());
4561     switch (response.resultClass) {
4562     case ResultDone:
4563     case ResultRunning:
4564         showMessage("INFERIOR ATTACHED");
4565         if (state() == EngineRunRequested) {
4566             // Happens e.g. for "Attach to unstarted application"
4567             // We will get a '*stopped' later that we'll interpret as 'spontaneous'
4568             // So acknowledge the current state and put a delayed 'continue' in the pipe.
4569             showMessage(tr("Attached to running application."), StatusBar);
4570             claimInitialBreakpoints();
4571             notifyEngineRunAndInferiorRunOk();
4572         } else {
4573             // InferiorStopOk, e.g. for "Attach to running application".
4574             // The *stopped came in between sending the 'attach' and
4575             // receiving its '^done'.
4576             claimInitialBreakpoints();
4577             notifyEngineRunAndInferiorStopOk();
4578             if (runParameters().continueAfterAttach)
4579                 continueInferiorInternal();
4580             else
4581                 updateAll();
4582         }
4583         break;
4584     case ResultError:
4585         if (response.data["msg"].data() == "ptrace: Operation not permitted.") {
4586             QString msg = msgPtraceError(runParameters().startMode);
4587             showStatusMessage(tr("Failed to attach to application: %1").arg(msg));
4588             AsynchronousMessageBox::warning(tr("Debugger Error"), msg);
4589             notifyEngineIll();
4590             break;
4591         }
4592         showStatusMessage(tr("Failed to attach to application: %1")
4593                           .arg(QString(response.data["msg"].data())));
4594         notifyEngineIll();
4595         break;
4596     default:
4597         showStatusMessage(tr("Failed to attach to application: %1")
4598                           .arg(QString(response.data["msg"].data())));
4599         notifyEngineIll();
4600         break;
4601     }
4602 }
4603 
handleRemoteAttach(const DebuggerResponse & response)4604 void GdbEngine::handleRemoteAttach(const DebuggerResponse &response)
4605 {
4606     CHECK_STATE(EngineSetupRequested);
4607     switch (response.resultClass) {
4608     case ResultDone:
4609     case ResultRunning: {
4610         showMessage("INFERIOR ATTACHED");
4611         showMessage(msgAttachedToStoppedInferior(), StatusBar);
4612         handleInferiorPrepared();
4613         break;
4614     }
4615     case ResultError:
4616         if (response.data["msg"].data() == "ptrace: Operation not permitted.") {
4617             notifyInferiorSetupFailedHelper(msgPtraceError(runParameters().startMode));
4618             break;
4619         }
4620         notifyInferiorSetupFailedHelper(response.data["msg"].data());
4621         break;
4622     default:
4623         notifyInferiorSetupFailedHelper(response.data["msg"].data());
4624         break;
4625     }
4626 }
4627 
interruptInferior2()4628 void GdbEngine::interruptInferior2()
4629 {
4630     if (isLocalAttachEngine()) {
4631 
4632         interruptLocalInferior(runParameters().attachPID.pid());
4633 
4634     } else if (isRemoteEngine() || runParameters().startMode == AttachToRemoteProcess) {
4635 
4636         CHECK_STATE(InferiorStopRequested);
4637         if (usesTargetAsync()) {
4638             runCommand({"-exec-interrupt", CB(handleInterruptInferior)});
4639         } else if (m_isQnxGdb && HostOsInfo::isWindowsHost()) {
4640             m_gdbProc.interrupt();
4641         } else {
4642             qint64 pid = m_gdbProc.processId();
4643             bool ok = interruptProcess(pid, GdbEngineType, &m_errorString);
4644             if (!ok) {
4645                 // FIXME: Extra state needed?
4646                 showMessage("NOTE: INFERIOR STOP NOT POSSIBLE");
4647                 showStatusMessage(tr("Interrupting not possible."));
4648                 notifyInferiorRunOk();
4649             }
4650         }
4651 
4652     } else if (isPlainEngine()) {
4653 
4654         interruptLocalInferior(inferiorPid());
4655 
4656     } else if (isTermEngine()) {
4657 
4658         terminal()->interruptProcess();
4659     }
4660 }
4661 
mixedDisasmFlag() const4662 QChar GdbEngine::mixedDisasmFlag() const
4663 {
4664     // /m is deprecated since 7.11, and was replaced by /s which works better with optimizations
4665     return m_gdbVersion >= 71100 ? 's' : 'm';
4666 }
4667 
shutdownEngine()4668 void GdbEngine::shutdownEngine()
4669 {
4670     if (usesOutputCollector()) {
4671         showMessage(QString("PLAIN ADAPTER SHUTDOWN %1").arg(state()));
4672         m_outputCollector.shutdown();
4673     }
4674 
4675     CHECK_STATE(EngineShutdownRequested);
4676     showMessage(QString("INITIATE GDBENGINE SHUTDOWN, PROC STATE: %1").arg(m_gdbProc.state()));
4677 
4678     switch (m_gdbProc.state()) {
4679     case QProcess::Running: {
4680         if (runParameters().closeMode == KillAndExitMonitorAtClose)
4681             runCommand({"monitor exit"});
4682         runCommand({"exitGdb", ExitRequest, CB(handleGdbExit)});
4683         break;
4684     }
4685     case QProcess::NotRunning:
4686         // Cannot find executable.
4687         notifyEngineShutdownFinished();
4688         break;
4689     case QProcess::Starting:
4690         showMessage("GDB NOT REALLY RUNNING; KILLING IT");
4691         m_gdbProc.kill();
4692         notifyEngineShutdownFinished();
4693         break;
4694     }
4695 }
4696 
handleFileExecAndSymbols(const DebuggerResponse & response)4697 void GdbEngine::handleFileExecAndSymbols(const DebuggerResponse &response)
4698 {
4699     CHECK_STATE(EngineSetupRequested);
4700 
4701     if (isRemoteEngine()) {
4702         if (response.resultClass != ResultDone) {
4703             QString msg = response.data["msg"].data();
4704             if (!msg.isEmpty()) {
4705                 showMessage(msg);
4706                 showMessage(msg, StatusBar);
4707             }
4708         }
4709         callTargetRemote(); // Proceed nevertheless.
4710 
4711     } else  if (isCoreEngine()) {
4712 
4713         QString core = runParameters().coreFile;
4714         if (response.resultClass == ResultDone) {
4715             showMessage(tr("Symbols found."), StatusBar);
4716             handleInferiorPrepared();
4717         } else {
4718             QString msg = tr("No symbols found in the core file \"%1\".").arg(core)
4719                     + ' ' + tr("This can be caused by a path length limitation "
4720                                "in the core file.")
4721                     + ' ' + tr("Try to specify the binary in "
4722                                "Debug > Start Debugging > Load Core File.");
4723             notifyInferiorSetupFailedHelper(msg);
4724         }
4725 
4726     } else if (isLocalRunEngine()) {
4727 
4728         if (response.resultClass == ResultDone) {
4729             handleInferiorPrepared();
4730         } else {
4731             QString msg = response.data["msg"].data();
4732             // Extend the message a bit in unknown cases.
4733             if (!msg.endsWith("File format not recognized"))
4734                 msg = tr("Starting executable failed:") + '\n' + msg;
4735             notifyInferiorSetupFailedHelper(msg);
4736         }
4737 
4738     }
4739 }
4740 
handleExecRun(const DebuggerResponse & response)4741 void GdbEngine::handleExecRun(const DebuggerResponse &response)
4742 {
4743     CHECK_STATE(EngineRunRequested);
4744 
4745     if (response.resultClass == ResultRunning) {
4746         notifyEngineRunAndInferiorRunOk();
4747         showMessage("INFERIOR STARTED");
4748         showMessage(msgInferiorSetupOk(), StatusBar);
4749     } else {
4750         showMessage(response.data["msg"].data());
4751         notifyEngineRunFailed();
4752     }
4753 }
4754 
handleSetTargetAsync(const DebuggerResponse & response)4755 void GdbEngine::handleSetTargetAsync(const DebuggerResponse &response)
4756 {
4757     CHECK_STATE(EngineSetupRequested);
4758     if (response.resultClass == ResultError)
4759         qDebug() << "Adapter too old: does not support asynchronous mode.";
4760 }
4761 
callTargetRemote()4762 void GdbEngine::callTargetRemote()
4763 {
4764     CHECK_STATE(EngineSetupRequested);
4765     QString channel = runParameters().remoteChannel;
4766 
4767     // Don't touch channels with explicitly set protocols.
4768     if (!channel.startsWith("tcp:") && !channel.startsWith("udp:")
4769             && !channel.startsWith("file:") && channel.contains(':')
4770             && !channel.startsWith('|'))
4771     {
4772         // "Fix" the IPv6 case with host names without '['...']'
4773         if (!channel.startsWith('[') && channel.count(':') >= 2) {
4774             channel.insert(0, '[');
4775             channel.insert(channel.lastIndexOf(':'), ']');
4776         }
4777         channel = "tcp:" + channel;
4778     }
4779 
4780     if (m_isQnxGdb)
4781         runCommand({"target qnx " + channel, CB(handleTargetQnx)});
4782     else if (runParameters().useExtendedRemote)
4783         runCommand({"target extended-remote " + channel, CB(handleTargetExtendedRemote)});
4784     else
4785         runCommand({"target remote " + channel, CB(handleTargetRemote)});
4786 }
4787 
handleTargetRemote(const DebuggerResponse & response)4788 void GdbEngine::handleTargetRemote(const DebuggerResponse &response)
4789 {
4790     CHECK_STATE(EngineSetupRequested);
4791     if (response.resultClass == ResultDone) {
4792         // gdb server will stop the remote application itself.
4793         showMessage("INFERIOR STARTED");
4794         showMessage(msgAttachedToStoppedInferior(), StatusBar);
4795         QString commands = expand(debuggerSettings()->gdbPostAttachCommands.value());
4796         if (!commands.isEmpty())
4797             runCommand({commands, NativeCommand});
4798         handleInferiorPrepared();
4799     } else {
4800         // 16^error,msg="hd:5555: Connection timed out."
4801         notifyInferiorSetupFailedHelper(msgConnectRemoteServerFailed(response.data["msg"].data()));
4802     }
4803 }
4804 
handleTargetExtendedRemote(const DebuggerResponse & response)4805 void GdbEngine::handleTargetExtendedRemote(const DebuggerResponse &response)
4806 {
4807     CHECK_STATE(EngineSetupRequested);
4808     if (response.resultClass == ResultDone) {
4809         showMessage("ATTACHED TO GDB SERVER STARTED");
4810         showMessage(msgAttachedToStoppedInferior(), StatusBar);
4811         QString commands = expand(debuggerSettings()->gdbPostAttachCommands.value());
4812         if (!commands.isEmpty())
4813             runCommand({commands, NativeCommand});
4814         if (runParameters().attachPID.isValid()) { // attach to pid if valid
4815             // gdb server will stop the remote application itself.
4816             runCommand({"attach " + QString::number(runParameters().attachPID.pid()),
4817                         CB(handleTargetExtendedAttach)});
4818         } else if (!runParameters().inferior.executable.isEmpty()) {
4819             runCommand({"-gdb-set remote exec-file " + runParameters().inferior.executable.toString(),
4820                         CB(handleTargetExtendedAttach)});
4821         } else {
4822             const QString title = tr("No Remote Executable or Process ID Specified");
4823             const QString msg = tr(
4824                 "No remote executable could be determined from your build system files.<p>"
4825                 "In case you use qmake, consider adding<p>"
4826                 "&nbsp;&nbsp;&nbsp;&nbsp;target.path = /tmp/your_executable # path on device<br>"
4827                 "&nbsp;&nbsp;&nbsp;&nbsp;INSTALLS += target</p>"
4828                 "to your .pro file.");
4829             QMessageBox *mb = showMessageBox(QMessageBox::Critical, title, msg,
4830                 QMessageBox::Ok | QMessageBox::Cancel);
4831             mb->button(QMessageBox::Cancel)->setText(tr("Continue Debugging"));
4832             mb->button(QMessageBox::Ok)->setText(tr("Stop Debugging"));
4833             if (mb->exec() == QMessageBox::Ok) {
4834                 showMessage("KILLING DEBUGGER AS REQUESTED BY USER");
4835                 notifyInferiorSetupFailedHelper(title);
4836             } else {
4837                 showMessage("CONTINUE DEBUGGER AS REQUESTED BY USER");
4838                 handleInferiorPrepared(); // This will likely fail.
4839             }
4840         }
4841     } else {
4842         notifyInferiorSetupFailedHelper(msgConnectRemoteServerFailed(response.data["msg"].data()));
4843     }
4844 }
4845 
handleTargetExtendedAttach(const DebuggerResponse & response)4846 void GdbEngine::handleTargetExtendedAttach(const DebuggerResponse &response)
4847 {
4848     CHECK_STATE(EngineSetupRequested);
4849     if (response.resultClass == ResultDone) {
4850         // gdb server will stop the remote application itself.
4851         handleInferiorPrepared();
4852     } else {
4853         notifyInferiorSetupFailedHelper(msgConnectRemoteServerFailed(response.data["msg"].data()));
4854     }
4855 }
4856 
handleTargetQnx(const DebuggerResponse & response)4857 void GdbEngine::handleTargetQnx(const DebuggerResponse &response)
4858 {
4859     CHECK_STATE(EngineSetupRequested);
4860     if (response.resultClass == ResultDone) {
4861         // gdb server will stop the remote application itself.
4862         showMessage("INFERIOR STARTED");
4863         showMessage(msgAttachedToStoppedInferior(), StatusBar);
4864 
4865         const DebuggerRunParameters &rp = runParameters();
4866         if (rp.attachPID.isValid())
4867             runCommand({"attach " + QString::number(rp.attachPID.pid()), CB(handleRemoteAttach)});
4868         else if (!rp.inferior.executable.isEmpty())
4869             runCommand({"set nto-executable " + rp.inferior.executable.toString(),
4870                         CB(handleSetNtoExecutable)});
4871         else
4872             handleInferiorPrepared();
4873     } else {
4874         // 16^error,msg="hd:5555: Connection timed out."
4875         notifyInferiorSetupFailedHelper(response.data["msg"].data());
4876     }
4877 }
4878 
handleSetNtoExecutable(const DebuggerResponse & response)4879 void GdbEngine::handleSetNtoExecutable(const DebuggerResponse &response)
4880 {
4881     CHECK_STATE(EngineSetupRequested);
4882     switch (response.resultClass) {
4883     case ResultDone:
4884     case ResultRunning: {
4885         showMessage("EXECUTABLE SET");
4886         showMessage(msgAttachedToStoppedInferior(), StatusBar);
4887         handleInferiorPrepared();
4888         break;
4889     }
4890     case ResultError:
4891     default:
4892         notifyInferiorSetupFailedHelper(response.data["msg"].data());
4893     }
4894 }
4895 
handleInterruptInferior(const DebuggerResponse & response)4896 void GdbEngine::handleInterruptInferior(const DebuggerResponse &response)
4897 {
4898     if (response.resultClass == ResultDone) {
4899         // The gdb server will trigger extra output that we will pick up
4900         // to do a proper state transition.
4901     } else {
4902         // FIXME: On some gdb versions like git 170ffa5d7dd this produces
4903         // >810^error,msg="mi_cmd_exec_interrupt: Inferior not executing."
4904         notifyInferiorStopOk();
4905     }
4906 }
4907 
handleStubAttached(const DebuggerResponse & response,qint64 mainThreadId)4908 void GdbEngine::handleStubAttached(const DebuggerResponse &response, qint64 mainThreadId)
4909 {
4910     // InferiorStopOk can happen if the "*stopped" in response to the
4911     // 'attach' comes in before its '^done'
4912     QTC_ASSERT(state() == EngineRunRequested || state() == InferiorStopOk, qDebug() << state());
4913 
4914     switch (response.resultClass) {
4915     case ResultDone:
4916     case ResultRunning:
4917         claimInitialBreakpoints();
4918         if (runParameters().toolChainAbi.os() == ProjectExplorer::Abi::WindowsOS) {
4919             QString errorMessage;
4920             // Resume thread that was suspended by console stub process (see stub code).
4921             if (winResumeThread(mainThreadId, &errorMessage)) {
4922                 showMessage(QString("Inferior attached, thread %1 resumed").
4923                             arg(mainThreadId), LogMisc);
4924             } else {
4925                 showMessage(QString("Inferior attached, unable to resume thread %1: %2").
4926                             arg(mainThreadId).arg(errorMessage),
4927                             LogWarning);
4928             }
4929             notifyEngineRunAndInferiorStopOk();
4930             continueInferiorInternal();
4931         } else {
4932             showMessage("INFERIOR ATTACHED");
4933             QTC_ASSERT(terminal(), return);
4934             terminal()->kickoffProcess();
4935             //notifyEngineRunAndInferiorRunOk();
4936             // Wait for the upcoming *stopped and handle it there.
4937         }
4938         break;
4939     case ResultError:
4940         if (response.data["msg"].data() == "ptrace: Operation not permitted.") {
4941             showMessage(msgPtraceError(runParameters().startMode));
4942             notifyEngineRunFailed();
4943             break;
4944         }
4945         showMessage(response.data["msg"].data());
4946         notifyEngineIll();
4947         break;
4948     default:
4949         showMessage(QString("Invalid response %1").arg(response.resultClass));
4950         notifyEngineIll();
4951         break;
4952     }
4953 }
4954 
findExecutableFromName(const QString & fileNameFromCore,const QString & coreFile)4955 static QString findExecutableFromName(const QString &fileNameFromCore, const QString &coreFile)
4956 {
4957     if (fileNameFromCore.isEmpty())
4958         return fileNameFromCore;
4959     QFileInfo fi(fileNameFromCore);
4960     if (fi.isFile())
4961         return fileNameFromCore;
4962 
4963     // turn the filename into an absolute path, using the location of the core as a hint
4964     QString absPath;
4965     if (fi.isAbsolute()) {
4966         absPath = fileNameFromCore;
4967     } else {
4968         QFileInfo coreInfo(coreFile);
4969         FilePath coreDir = FilePath::fromString(coreInfo.dir().absolutePath());
4970         absPath = coreDir.resolvePath(fileNameFromCore).toString();
4971     }
4972     if (QFileInfo(absPath).isFile() || absPath.isEmpty())
4973         return absPath;
4974 
4975     // remove possible trailing arguments
4976     QChar sep(' ');
4977     QStringList pathFragments = absPath.split(sep);
4978     while (pathFragments.size() > 0) {
4979         QString joined_path = pathFragments.join(sep);
4980         if (QFileInfo(joined_path).isFile()) {
4981             return joined_path;
4982         }
4983         pathFragments.pop_back();
4984     }
4985 
4986     return QString();
4987 }
4988 
readExecutableNameFromCore(const Runnable & debugger,const QString & coreFile)4989 CoreInfo CoreInfo::readExecutableNameFromCore(const Runnable &debugger, const QString &coreFile)
4990 {
4991     CoreInfo cinfo;
4992 #if 0
4993     ElfReader reader(coreFile);
4994     cinfo.rawStringFromCore = QString::fromLocal8Bit(reader.readCoreName(&cinfo.isCore));
4995     cinfo.foundExecutableName = findExecutableFromName(cinfo.rawStringFromCore, coreFile);
4996 #else
4997     QStringList args = {"-nx",  "-batch"};
4998     // Multiarch GDB on Windows crashes if osabi is cygwin (the default) when opening a core dump.
4999     if (HostOsInfo::isWindowsHost())
5000         args += {"-ex", "set osabi GNU/Linux"};
5001     args += {"-ex", "core " + coreFile};
5002 
5003     QtcProcess proc;
5004     Environment envLang(Environment::systemEnvironment());
5005     envLang.setupEnglishOutput();
5006     proc.setEnvironment(envLang);
5007     proc.setCommand({debugger.executable, args});
5008     proc.runBlocking();
5009 
5010     if (proc.result() == QtcProcess::FinishedWithSuccess) {
5011         QString output = proc.stdOut();
5012         // Core was generated by `/data/dev/creator-2.6/bin/qtcreator'.
5013         // Program terminated with signal 11, Segmentation fault.
5014         int pos1 = output.indexOf("Core was generated by");
5015         if (pos1 != -1) {
5016             pos1 += 23;
5017             int pos2 = output.indexOf('\'', pos1);
5018             if (pos2 != -1) {
5019                 cinfo.isCore = true;
5020                 cinfo.rawStringFromCore = output.mid(pos1, pos2 - pos1);
5021                 cinfo.foundExecutableName = findExecutableFromName(cinfo.rawStringFromCore, coreFile);
5022             }
5023         }
5024     }
5025 #endif
5026     return cinfo;
5027 }
5028 
handleTargetCore(const DebuggerResponse & response)5029 void GdbEngine::handleTargetCore(const DebuggerResponse &response)
5030 {
5031     CHECK_STATE(EngineRunRequested);
5032     notifyEngineRunOkAndInferiorUnrunnable();
5033     showMessage(tr("Attached to core."), StatusBar);
5034     if (response.resultClass == ResultError) {
5035         // We'll accept any kind of error e.g. &"Cannot access memory at address 0x2abc2a24\n"
5036         // Even without the stack, the user can find interesting stuff by exploring
5037         // the memory, globals etc.
5038         showStatusMessage(tr("Attach to core \"%1\" failed:").arg(runParameters().coreFile)
5039                           + '\n' + response.data["msg"].data()
5040                 + '\n' + tr("Continuing nevertheless."));
5041     }
5042     // Due to the auto-solib-add off setting, we don't have any
5043     // symbols yet. Load them in order of importance.
5044     reloadStack();
5045     reloadModulesInternal();
5046     runCommand({"p 5", CB(handleCoreRoundTrip)});
5047 }
5048 
handleCoreRoundTrip(const DebuggerResponse & response)5049 void GdbEngine::handleCoreRoundTrip(const DebuggerResponse &response)
5050 {
5051     CHECK_STATE(InferiorUnrunnable);
5052     Q_UNUSED(response)
5053     loadSymbolsForStack();
5054     handleStop3();
5055     QTimer::singleShot(1000, this, &GdbEngine::loadAllSymbols);
5056 }
5057 
doUpdateLocals(const UpdateParameters & params)5058 void GdbEngine::doUpdateLocals(const UpdateParameters &params)
5059 {
5060     watchHandler()->notifyUpdateStarted(params);
5061 
5062     DebuggerCommand cmd("fetchVariables", Discardable|InUpdateLocals);
5063     watchHandler()->appendFormatRequests(&cmd);
5064     watchHandler()->appendWatchersAndTooltipRequests(&cmd);
5065 
5066     const static bool alwaysVerbose = qEnvironmentVariableIsSet("QTC_DEBUGGER_PYTHON_VERBOSE");
5067     const DebuggerSettings &s = *debuggerSettings();
5068     cmd.arg("passexceptions", alwaysVerbose);
5069     cmd.arg("fancy", s.useDebuggingHelpers.value());
5070     cmd.arg("autoderef", s.autoDerefPointers.value());
5071     cmd.arg("dyntype", s.useDynamicType.value());
5072     cmd.arg("qobjectnames", s.showQObjectNames.value());
5073     cmd.arg("timestamps", s.logTimeStamps.value());
5074 
5075     StackFrame frame = stackHandler()->currentFrame();
5076     cmd.arg("context", frame.context);
5077     cmd.arg("nativemixed", isNativeMixedActive());
5078 
5079     cmd.arg("stringcutoff", s.maximalStringLength.value());
5080     cmd.arg("displaystringlimit", s.displayStringLimit.value());
5081 
5082     cmd.arg("resultvarname", m_resultVarName);
5083     cmd.arg("partialvar", params.partialVariable);
5084 
5085     m_lastDebuggableCommand = cmd;
5086     m_lastDebuggableCommand.arg("passexceptions", "1");
5087 
5088     cmd.callback = CB(handleFetchVariables);
5089 
5090     runCommand(cmd);
5091 }
5092 
handleFetchVariables(const DebuggerResponse & response)5093 void GdbEngine::handleFetchVariables(const DebuggerResponse &response)
5094 {
5095     m_inUpdateLocals = false;
5096     updateLocalsView(response.data);
5097     watchHandler()->notifyUpdateFinished();
5098     updateToolTips();
5099 }
5100 
msgPtraceError(DebuggerStartMode sm)5101 QString GdbEngine::msgPtraceError(DebuggerStartMode sm)
5102 {
5103     if (sm == StartInternal) {
5104         return QCoreApplication::translate("QtDumperHelper",
5105             "ptrace: Operation not permitted.\n\n"
5106             "Could not attach to the process. "
5107             "Make sure no other debugger traces this process.\n"
5108             "Check the settings of\n"
5109             "/proc/sys/kernel/yama/ptrace_scope\n"
5110             "For more details, see /etc/sysctl.d/10-ptrace.conf\n");
5111     }
5112     return QCoreApplication::translate("QtDumperHelper",
5113         "ptrace: Operation not permitted.\n\n"
5114         "Could not attach to the process. "
5115         "Make sure no other debugger traces this process.\n"
5116         "If your uid matches the uid\n"
5117         "of the target process, check the settings of\n"
5118         "/proc/sys/kernel/yama/ptrace_scope\n"
5119         "For more details, see /etc/sysctl.d/10-ptrace.conf\n");
5120 }
5121 
mainFunction() const5122 QString GdbEngine::mainFunction() const
5123 {
5124     const DebuggerRunParameters &rp = runParameters();
5125     return QLatin1String(rp.toolChainAbi.os() == Abi::WindowsOS && !terminal() ? "qMain" : "main");
5126 }
5127 
5128 //
5129 // Factory
5130 //
5131 
createGdbEngine()5132 DebuggerEngine *createGdbEngine()
5133 {
5134     return new GdbEngine;
5135 }
5136 
5137 } // namespace Internal
5138 } // namespace Debugger
5139 
5140 Q_DECLARE_METATYPE(Debugger::Internal::GdbMi)
5141 Q_DECLARE_METATYPE(Debugger::Internal::TracepointCaptureData)
5142