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 ¤tToken()
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 " target.path = /tmp/your_executable # path on device<br>"
4827 " 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 ¶ms)
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