1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #define QT_NO_CAST_FROM_ASCII
36 #include "gdbengine.h"
38 #include "debuggerstartparameters.h"
39 #include "disassemblerlines.h"
40 #include "attachgdbadapter.h"
41 #include "coregdbadapter.h"
42 #include "localplaingdbadapter.h"
43 #include "termgdbadapter.h"
44 #include "remotegdbserveradapter.h"
45 #include "remoteplaingdbadapter.h"
46 #include "trkgdbadapter.h"
47 #include "codagdbadapter.h"
49 #include "debuggeractions.h"
50 #include "debuggerconstants.h"
51 #include "debuggercore.h"
52 #include "debuggerplugin.h"
53 #include "debuggerrunner.h"
54 #include "debuggerstringutils.h"
55 #include "debuggertooltipmanager.h"
56 #include "disassembleragent.h"
58 #include "gdboptionspage.h"
59 #include "memoryagent.h"
60 #include "watchutils.h"
62 #include "breakhandler.h"
63 #include "moduleshandler.h"
64 #include "registerhandler.h"
65 #include "snapshothandler.h"
66 #include "sourcefileshandler.h"
67 #include "stackhandler.h"
68 #include "threadshandler.h"
69 #include "watchhandler.h"
70 #include "debuggersourcepathmappingwidget.h"
73 # include "dbgwinutils.h"
75 #include "logwindow.h"
77 #include <coreplugin/icore.h>
78 #include <coreplugin/ifile.h>
79 #include <projectexplorer/abi.h>
80 #include <projectexplorer/projectexplorerconstants.h>
81 #include <texteditor/itexteditor.h>
82 #include <utils/qtcassert.h>
84 #include <QtCore/QCoreApplication>
85 #include <QtCore/QDebug>
86 #include <QtCore/QDir>
87 #include <QtCore/QFileInfo>
88 #include <QtCore/QMetaObject>
89 #include <QtCore/QTime>
90 #include <QtCore/QTimer>
91 #include <QtCore/QTemporaryFile>
92 #include <QtCore/QTextStream>
94 #include <QtGui/QAction>
95 #include <QtGui/QDialogButtonBox>
96 #include <QtGui/QLabel>
97 #include <QtGui/QMainWindow>
98 #include <QtGui/QMessageBox>
99 #include <QtGui/QPushButton>
107 using namespace ProjectExplorer;
112 class GdbToolTipContext : public DebuggerToolTipContext
115 GdbToolTipContext(const DebuggerToolTipContext &c) :
116 DebuggerToolTipContext(c), editor(0) {}
118 QPoint mousePosition;
120 Core::IEditor *editor;
123 static const char winPythonVersionC[] = "python2.5";
125 enum { debugPending = 0 };
127 #define PENDING_DEBUG(s) do { if (debugPending) qDebug() << s; } while (0)
129 #define CB(callback) &GdbEngine::callback, STRINGIFY(callback)
131 QByteArray GdbEngine::tooltipIName(const QString &exp)
133 return "tooltip." + exp.toLatin1().toHex();
136 static bool stateAcceptsGdbCommands(DebuggerState state)
139 case EngineSetupRequested:
141 case EngineSetupFailed:
142 case InferiorUnrunnable:
143 case InferiorSetupRequested:
144 case InferiorSetupFailed:
145 case EngineRunRequested:
146 case InferiorRunRequested:
148 case InferiorStopRequested:
150 case InferiorShutdownRequested:
151 case EngineShutdownRequested:
152 case InferiorShutdownOk:
153 case InferiorShutdownFailed:
155 case DebuggerNotReady:
156 case InferiorStopFailed:
157 case InferiorSetupOk:
158 case EngineRunFailed:
160 case InferiorRunFailed:
161 case EngineShutdownOk:
162 case EngineShutdownFailed:
163 case DebuggerFinished:
169 static int ¤tToken()
171 static int token = 0;
175 static QByteArray parsePlainConsoleStream(const GdbResponse &response)
177 GdbMi output = response.data.findChild("consolestreamoutput");
178 QByteArray out = output.data();
179 // FIXME: proper decoding needed
180 if (out.endsWith("\\n"))
182 while (out.endsWith('\n') || out.endsWith(' '))
184 int pos = out.indexOf(" = ");
185 return out.mid(pos + 3);
188 ///////////////////////////////////////////////////////////////////////
192 ///////////////////////////////////////////////////////////////////////
194 GdbEngine::GdbEngine(const DebuggerStartParameters &startParameters,
195 DebuggerEngine *masterEngine)
196 : DebuggerEngine(startParameters, masterEngine)
198 setObjectName(_("GdbEngine"));
202 m_debuggingHelperState = DebuggingHelperUninitialized;
204 m_gdbBuildVersion = -1;
207 m_registerNamesListed = false;
208 m_hasInferiorThreadList = false;
209 m_sourcesListUpdating = false;
210 m_oldestAcceptableToken = -1;
211 m_outputCodec = QTextCodec::codecForLocale();
212 m_pendingWatchRequests = 0;
213 m_pendingBreakpointRequests = 0;
214 m_commandsDoneCallback = 0;
215 m_stackNeeded = false;
216 m_preparedForQmlBreak = false;
217 m_disassembleUsesComma = false;
218 m_qFatalBreakpointNumber = 0;
220 invalidateSourcesList();
222 m_gdbAdapter = createAdapter();
224 m_commandTimer.setSingleShot(true);
225 connect(&m_commandTimer, SIGNAL(timeout()), SLOT(commandTimeout()));
227 connect(debuggerCore()->action(AutoDerefPointers), SIGNAL(valueChanged(QVariant)),
228 SLOT(reloadLocals()));
229 connect(debuggerCore()->action(SortStructMembers), SIGNAL(valueChanged(QVariant)),
230 SLOT(reloadLocals()));
231 connect(debuggerCore()->action(ShowStdNamespace), SIGNAL(valueChanged(QVariant)),
232 SLOT(reloadLocals()));
233 connect(debuggerCore()->action(ShowQtNamespace), SIGNAL(valueChanged(QVariant)),
234 SLOT(reloadLocals()));
235 connect(debuggerCore()->action(CreateFullBacktrace), SIGNAL(triggered()),
236 SLOT(createFullBacktrace()));
239 DebuggerStartMode GdbEngine::startMode() const
241 return startParameters().startMode;
244 AbstractGdbProcess *GdbEngine::gdbProc() const
246 return m_gdbAdapter->gdbProc();
249 GdbEngine::~GdbEngine()
251 // Prevent sending error messages afterwards.
253 disconnect(gdbProc(), 0, this, 0);
258 QString GdbEngine::errorMessage(QProcess::ProcessError error)
261 case QProcess::FailedToStart:
262 return tr("The gdb process failed to start. Either the "
263 "invoked program '%1' is missing, or you may have insufficient "
264 "permissions to invoke the program.\n%2")
265 .arg(m_gdb, gdbProc()->errorString());
266 case QProcess::Crashed:
267 return tr("The gdb process crashed some time after starting "
269 case QProcess::Timedout:
270 return tr("The last waitFor...() function timed out. "
271 "The state of QProcess is unchanged, and you can try calling "
272 "waitFor...() again.");
273 case QProcess::WriteError:
274 return tr("An error occurred when attempting to write "
275 "to the gdb process. For example, the process may not be running, "
276 "or it may have closed its input channel.");
277 case QProcess::ReadError:
278 return tr("An error occurred when attempting to read from "
279 "the gdb process. For example, the process may not be running.");
281 return tr("An unknown error in the gdb process occurred. ");
286 static void dump(const char *first, const char *middle, const QString & to)
288 QByteArray ba(first, middle - first);
290 // note that qDebug cuts off output after a certain size... (bug?)
291 qDebug("\n>>>>> %s\n%s\n====\n%s\n<<<<<\n",
292 qPrintable(currentTime()),
293 qPrintable(QString(ba).trimmed()),
294 qPrintable(to.trimmed()));
296 //qDebug() << qPrintable(currentTime())
297 // << " Reading response: " << QString(ba).trimmed() << "\n";
301 // Parse "~:gdb: unknown target exception 0xc0000139 at 0x77bef04e\n"
302 // and return an exception message
303 static inline QString msgWinException(const QByteArray &data)
305 const int exCodePos = data.indexOf("0x");
306 const int blankPos = exCodePos != -1 ? data.indexOf(' ', exCodePos + 1) : -1;
307 const int addressPos = blankPos != -1 ? data.indexOf("0x", blankPos + 1) : -1;
309 return GdbEngine::tr("An exception was triggered.");
310 const unsigned exCode = data.mid(exCodePos, blankPos - exCodePos).toUInt(0, 0);
311 const quint64 address = data.mid(addressPos).trimmed().toULongLong(0, 0);
313 QTextStream str(&rc);
314 str << GdbEngine::tr("An exception was triggered: ");
316 formatWindowsException(exCode, address, 0, 0, 0, str);
325 void GdbEngine::readDebugeeOutput(const QByteArray &data)
327 QString msg = m_outputCodec->toUnicode(data.constData(), data.length(),
328 &m_outputCodecState);
329 showMessage(msg, AppOutput);
332 void GdbEngine::handleResponse(const QByteArray &buff)
334 showMessage(QString::fromLocal8Bit(buff, buff.length()), LogOutput);
337 static QTime lastTime;
338 qDebug() // << "#### start response handling #### "
339 << lastTime.msecsTo(QTime::currentTime()) << "ms,"
340 << "buf:" << buff.left(1500) << "..."
342 << "size:" << buff.size();
343 lastTime = QTime::currentTime();
345 //qDebug() << "buf:" << buff;
347 if (buff.isEmpty() || buff == "(gdb) ")
350 const char *from = buff.constData();
351 const char *to = from + buff.size();
355 // token is a sequence of numbers
356 for (inner = from; inner != to; ++inner)
357 if (*inner < '0' || *inner > '9')
360 token = QByteArray(from, inner - from).toInt();
362 //qDebug() << "found token" << token;
365 // next char decides kind of response
366 const char c = *from++;
367 //qDebug() << "CODE:" << c;
372 QByteArray asyncClass;
373 for (; from != to; ++from) {
374 const char c = *from;
379 //qDebug() << "ASYNCCLASS" << asyncClass;
385 // happens on archer where we get
386 // 23^running <NL> *running,thread-id="all" <NL> (gdb)
387 result.m_type = GdbMi::Tuple;
391 data.parseResultOrValue(from, to);
392 if (data.isValid()) {
393 //qDebug() << "parsed result:" << data.toString();
394 result.m_children += data;
395 result.m_type = GdbMi::Tuple;
398 if (asyncClass == "stopped") {
399 handleStopResponse(result);
400 m_pendingLogStreamOutput.clear();
401 m_pendingConsoleStreamOutput.clear();
402 } else if (asyncClass == "running") {
403 // Archer has 'thread-id="all"' here
404 } else if (asyncClass == "library-loaded") {
405 // Archer has 'id="/usr/lib/libdrm.so.2",
406 // target-name="/usr/lib/libdrm.so.2",
407 // host-name="/usr/lib/libdrm.so.2",
408 // symbols-loaded="0"
409 QByteArray id = result.findChild("id").data();
411 showStatusMessage(tr("Library %1 loaded").arg(_(id)), 1000);
413 invalidateSourcesList();
414 } else if (asyncClass == "library-unloaded") {
415 // Archer has 'id="/usr/lib/libdrm.so.2",
416 // target-name="/usr/lib/libdrm.so.2",
417 // host-name="/usr/lib/libdrm.so.2"
418 QByteArray id = result.findChild("id").data();
420 showStatusMessage(tr("Library %1 unloaded").arg(_(id)), 1000);
421 invalidateSourcesList();
422 } else if (asyncClass == "thread-group-added") {
423 // 7.1-symbianelf has "{id="i1"}"
424 } else if (asyncClass == "thread-group-created"
425 || asyncClass == "thread-group-started") {
426 // Archer had only "{id="28902"}" at some point of 6.8.x.
427 // *-started seems to be standard in 7.1, but in early
428 // 7.0.x, there was a *-created instead.
430 // 7.1.50 has thread-group-started,id="i1",pid="3529"
431 QByteArray id = result.findChild("id").data();
432 showStatusMessage(tr("Thread group %1 created").arg(_(id)), 1000);
433 int pid = id.toInt();
435 id = result.findChild("pid").data();
439 notifyInferiorPid(pid);
440 } else if (asyncClass == "thread-created") {
441 //"{id="1",group-id="28902"}"
442 QByteArray id = result.findChild("id").data();
443 showStatusMessage(tr("Thread %1 created").arg(_(id)), 1000);
444 } else if (asyncClass == "thread-group-exited") {
445 // Archer has "{id="28902"}"
446 QByteArray id = result.findChild("id").data();
447 showStatusMessage(tr("Thread group %1 exited").arg(_(id)), 1000);
448 } else if (asyncClass == "thread-exited") {
449 //"{id="1",group-id="28902"}"
450 QByteArray id = result.findChild("id").data();
451 QByteArray groupid = result.findChild("group-id").data();
452 showStatusMessage(tr("Thread %1 in group %2 exited")
453 .arg(_(id)).arg(_(groupid)), 1000);
454 } else if (asyncClass == "thread-selected") {
455 QByteArray id = result.findChild("id").data();
456 showStatusMessage(tr("Thread %1 selected").arg(_(id)), 1000);
458 } else if (m_isMacGdb && asyncClass == "shlibs-updated") {
459 // Apple's gdb announces updated libs.
460 invalidateSourcesList();
461 } else if (m_isMacGdb && asyncClass == "shlibs-added") {
462 // Apple's gdb announces added libs.
463 // {shlib-info={num="2", name="libmathCommon.A_debug.dylib",
464 // kind="-", dyld-addr="0x7f000", reason="dyld", requested-state="Y",
465 // state="Y", path="/usr/lib/system/libmathCommon.A_debug.dylib",
466 // description="/usr/lib/system/libmathCommon.A_debug.dylib",
467 // loaded_addr="0x7f000", slide="0x7f000", prefix=""}}
468 invalidateSourcesList();
469 } else if (m_isMacGdb && asyncClass == "resolve-pending-breakpoint") {
470 // Apple's gdb announces resolved breakpoints.
471 // new_bp="1",pended_bp="1",new_expr="\"gdbengine.cpp\":1584",
472 // bkpt={number="1",type="breakpoint",disp="keep",enabled="y",
473 // addr="0x0000000115cc3ddf",func="foo()",file="../foo.cpp",
474 // line="1584",shlib="/../libFoo_debug.dylib",times="0"}
475 const GdbMi bkpt = result.findChild("bkpt");
476 const int number = bkpt.findChild("number").data().toInt();
477 if (!isQmlStepBreakpoint1(number) && isQmlStepBreakpoint2(number)) {
478 BreakpointId id = breakHandler()->findBreakpointByNumber(number);
479 updateBreakpointDataFromOutput(id, bkpt);
480 BreakpointResponse response = breakHandler()->response(id);
481 if (response.correctedLineNumber == 0)
482 attemptAdjustBreakpointLocation(id);
485 qDebug() << "IGNORED ASYNC OUTPUT"
486 << asyncClass << result.toString();
492 QByteArray data = GdbMi::parseCString(from, to);
493 m_pendingConsoleStreamOutput += data;
495 // Parse pid from noise.
496 if (!inferiorPid()) {
497 // Linux/Mac gdb: [New [Tt]hread 0x545 (LWP 4554)]
498 static QRegExp re1(_("New .hread 0x[0-9a-f]+ \\(LWP ([0-9]*)\\)"));
499 // MinGW 6.8: [New thread 2437.0x435345]
500 static QRegExp re2(_("New .hread ([0-9]+)\\.0x[0-9a-f]*"));
501 // Mac: [Switching to process 9294 local thread 0x2e03] or
502 // [Switching to process 31773]
503 static QRegExp re3(_("Switching to process ([0-9]+)"));
504 QTC_ASSERT(re1.isValid() && re2.isValid(), return);
505 if (re1.indexIn(_(data)) != -1)
506 maybeHandleInferiorPidChanged(re1.cap(1));
507 else if (re2.indexIn(_(data)) != -1)
508 maybeHandleInferiorPidChanged(re2.cap(1));
509 else if (re3.indexIn(_(data)) != -1)
510 maybeHandleInferiorPidChanged(re3.cap(1));
513 // Show some messages to give the impression something happens.
514 if (data.startsWith("Reading symbols from ")) {
515 showStatusMessage(tr("Reading %1...").arg(_(data.mid(21))), 1000);
517 invalidateSourcesList();
518 } else if (data.startsWith("[New ") || data.startsWith("[Thread ")) {
519 if (data.endsWith('\n'))
522 showStatusMessage(_(data), 1000);
523 } else if (data.startsWith("gdb: unknown target exception 0x")) {
524 // [Windows, most likely some DLL/Entry point not found]:
525 // "gdb: unknown target exception 0xc0000139 at 0x77bef04e"
526 // This may be fatal and cause the target to exit later
527 m_lastWinException = msgWinException(data);
528 showMessage(m_lastWinException, LogMisc);
531 if (data.startsWith("QMLBP:")) {
533 int pos2 = data.indexOf(' ', pos1);
534 m_qmlBreakpointNumbers[2] = data.mid(pos1, pos2 - pos1).toInt();
535 //qDebug() << "FOUND QMLBP: " << m_qmlBreakpointNumbers[2];
536 //qDebug() << "CURRENT: " << m_qmlBreakpointNumbers;
543 readDebugeeOutput(GdbMi::parseCString(from, to));
548 QByteArray data = GdbMi::parseCString(from, to);
549 m_pendingLogStreamOutput += data;
550 // On Windows, the contents seem to depend on the debugger
551 // version and/or OS version used.
552 if (data.startsWith("warning:"))
553 showMessage(_(data.mid(9)), AppStuff); // Cut "warning: "
558 GdbResponse response;
560 response.token = token;
562 for (inner = from; inner != to; ++inner)
563 if (*inner < 'a' || *inner > 'z')
566 QByteArray resultClass = QByteArray::fromRawData(from, inner - from);
567 if (resultClass == "done") {
568 response.resultClass = GdbResultDone;
569 } else if (resultClass == "running") {
570 response.resultClass = GdbResultRunning;
571 } else if (resultClass == "connected") {
572 response.resultClass = GdbResultConnected;
573 } else if (resultClass == "error") {
574 response.resultClass = GdbResultError;
575 } else if (resultClass == "exit") {
576 response.resultClass = GdbResultExit;
578 response.resultClass = GdbResultUnknown;
585 response.data.parseTuple_helper(from, to);
586 response.data.m_type = GdbMi::Tuple;
587 response.data.m_name = "data";
590 response.data.m_type = GdbMi::Tuple;
591 response.data.m_name = "data";
595 //qDebug() << "\nLOG STREAM:" + m_pendingLogStreamOutput;
596 //qDebug() << "\nCONSOLE STREAM:" + m_pendingConsoleStreamOutput;
597 response.data.setStreamOutput("logstreamoutput",
598 m_pendingLogStreamOutput);
599 response.data.setStreamOutput("consolestreamoutput",
600 m_pendingConsoleStreamOutput);
601 m_pendingLogStreamOutput.clear();
602 m_pendingConsoleStreamOutput.clear();
604 handleResultRecord(&response);
608 qDebug() << "UNKNOWN RESPONSE TYPE" << c;
614 void GdbEngine::readGdbStandardError()
616 QByteArray err = gdbProc()->readAllStandardError();
617 showMessage(_("UNEXPECTED GDB STDERR: " + err));
618 if (err == "Undefined command: \"bb\". Try \"help\".\n")
620 if (err.startsWith("BFD: reopening"))
622 qWarning() << "Unexpected GDB stderr:" << err;
625 void GdbEngine::readGdbStandardOutput()
627 m_commandTimer.start(); // Restart timer.
630 int scan = m_inbuffer.size();
632 m_inbuffer.append(gdbProc()->readAllStandardOutput());
634 // This can trigger when a dialog starts a nested event loop.
638 while (newstart < m_inbuffer.size()) {
639 int start = newstart;
640 int end = m_inbuffer.indexOf('\n', scan);
642 m_inbuffer.remove(0, start);
649 # if defined(Q_OS_WIN)
650 if (m_inbuffer.at(end - 1) == '\r') {
657 handleResponse(QByteArray::fromRawData(m_inbuffer.constData() + start, end - start));
663 void GdbEngine::interruptInferior()
665 QTC_ASSERT(state() == InferiorStopRequested, qDebug() << state(); return);
667 if (debuggerCore()->boolSetting(TargetAsync)) {
668 postCommand("-exec-interrupt");
670 showStatusMessage(tr("Stop requested..."), 5000);
671 showMessage(_("TRYING TO INTERRUPT INFERIOR"));
672 m_gdbAdapter->interruptInferior();
676 void GdbEngine::interruptInferiorTemporarily()
678 foreach (const GdbCommand &cmd, m_commandsToRunOnTemporaryBreak) {
679 if (cmd.flags & LosesChild) {
684 requestInterruptInferior();
687 void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
689 const qint64 pid = pid0.toLongLong();
691 showMessage(_("Cannot parse PID from %1").arg(pid0));
694 if (pid == inferiorPid())
697 showMessage(_("FOUND PID %1").arg(pid));
698 notifyInferiorPid(pid);
701 void GdbEngine::postCommand(const QByteArray &command, AdapterCallback callback,
702 const char *callbackName, const QVariant &cookie)
704 postCommand(command, NoFlags, callback, callbackName, cookie);
707 void GdbEngine::postCommand(const QByteArray &command, GdbCommandFlags flags,
708 AdapterCallback callback,
709 const char *callbackName, const QVariant &cookie)
712 cmd.command = command;
714 cmd.adapterCallback = callback;
715 cmd.callbackName = callbackName;
717 postCommandHelper(cmd);
720 void GdbEngine::postCommand(const QByteArray &command, GdbCommandCallback callback,
721 const char *callbackName, const QVariant &cookie)
723 postCommand(command, NoFlags, callback, callbackName, cookie);
726 void GdbEngine::postCommand(const QByteArray &command, GdbCommandFlags flags,
727 GdbCommandCallback callback, const char *callbackName,
728 const QVariant &cookie)
731 cmd.command = command;
733 cmd.callback = callback;
734 cmd.callbackName = callbackName;
736 postCommandHelper(cmd);
739 void GdbEngine::postCommandHelper(const GdbCommand &cmd)
741 if (!stateAcceptsGdbCommands(state())) {
742 PENDING_DEBUG(_("NO GDB PROCESS RUNNING, CMD IGNORED: " + cmd.command));
743 showMessage(_("NO GDB PROCESS RUNNING, CMD IGNORED: %1 %2")
744 .arg(_(cmd.command)).arg(state()));
748 if (cmd.flags & RebuildWatchModel) {
749 ++m_pendingWatchRequests;
750 PENDING_DEBUG(" WATCH MODEL:" << cmd.command << "=>" << cmd.callbackName
751 << "INCREMENTS PENDING TO" << m_pendingWatchRequests);
752 } else if (cmd.flags & RebuildBreakpointModel) {
753 ++m_pendingBreakpointRequests;
754 PENDING_DEBUG(" BRWAKPOINT MODEL:" << cmd.command << "=>" << cmd.callbackName
755 << "INCREMENTS PENDING TO" << m_pendingBreakpointRequests);
757 PENDING_DEBUG(" OTHER (IN):" << cmd.command << "=>" << cmd.callbackName
758 << "LEAVES PENDING WATCH AT" << m_pendingWatchRequests
759 << "LEAVES PENDING BREAKPOINT AT" << m_pendingBreakpointRequests);
762 // FIXME: clean up logic below
763 if (cmd.flags & Immediate) {
764 // This should always be sent.
766 } else if ((cmd.flags & NeedsStop)
767 || !m_commandsToRunOnTemporaryBreak.isEmpty()) {
768 if (state() == InferiorStopOk || state() == InferiorUnrunnable
769 || state() == InferiorSetupRequested || state() == EngineSetupOk
770 || state() == InferiorShutdownRequested) {
771 // Can be safely sent now.
774 // Queue the commands that we cannot send at once.
775 showMessage(_("QUEUING COMMAND " + cmd.command));
776 m_commandsToRunOnTemporaryBreak.append(cmd);
777 if (state() == InferiorStopRequested) {
778 if (cmd.flags & LosesChild) {
781 showMessage(_("CHILD ALREADY BEING INTERRUPTED. STILL HOPING."));
782 // Calling shutdown() here breaks all situations where two
783 // NeedsStop commands are issued in quick succession.
784 } else if (state() == InferiorRunOk) {
785 showStatusMessage(tr("Stopping temporarily"), 1000);
786 interruptInferiorTemporarily();
788 qDebug() << "ATTEMPTING TO QUEUE COMMAND "
789 << cmd.command << "IN INAPPROPRIATE STATE" << state();
792 } else if (!cmd.command.isEmpty()) {
797 void GdbEngine::flushQueuedCommands()
799 showStatusMessage(tr("Processing queued commands"), 1000);
800 while (!m_commandsToRunOnTemporaryBreak.isEmpty()) {
801 GdbCommand cmd = m_commandsToRunOnTemporaryBreak.takeFirst();
802 showMessage(_("RUNNING QUEUED COMMAND " + cmd.command + ' '
803 + cmd.callbackName));
808 void GdbEngine::flushCommand(const GdbCommand &cmd0)
810 if (!stateAcceptsGdbCommands(state())) {
811 showMessage(_(cmd0.command), LogInput);
812 showMessage(_("GDB PROCESS ACCEPTS NO CMD IN STATE %1 ").arg(state()));
816 QTC_ASSERT(gdbProc()->state() == QProcess::Running, return;)
819 GdbCommand cmd = cmd0;
820 cmd.postTime = QTime::currentTime();
821 m_cookieForToken[currentToken()] = cmd;
822 if (cmd.flags & ConsoleCommand)
823 cmd.command = "-interpreter-exec console \"" + cmd.command + '"';
824 cmd.command = QByteArray::number(currentToken()) + cmd.command;
825 showMessage(_(cmd.command), LogInput);
827 m_gdbAdapter->write(cmd.command + "\r\n");
830 if (m_commandTimer.interval() <= 20000)
831 m_commandTimer.setInterval(commandTimeoutTime());
832 // The process can die for external reason between the "-gdb-exit" was
833 // sent and a response could be retrieved. We don't want the watchdog
834 // to bark in that case since the only possible outcome is a dead
836 if (!cmd.command.endsWith("-gdb-exit"))
837 m_commandTimer.start();
839 //if (cmd.flags & LosesChild)
840 // notifyInferiorIll();
843 int GdbEngine::commandTimeoutTime() const
845 int time = debuggerCore()->action(GdbWatchdogTimeout)->value().toInt();
846 return 1000 * qMax(40, time);
849 void GdbEngine::commandTimeout()
851 QList<int> keys = m_cookieForToken.keys();
854 foreach (int key, keys) {
855 const GdbCommand &cmd = m_cookieForToken.value(key);
856 if (!(cmd.flags & NonCriticalResponse))
858 QByteArray msg = QByteArray::number(key);
859 msg += ": " + cmd.command + " => ";
860 msg += cmd.callbackName ? cmd.callbackName : "<unnamed callback>";
864 showMessage(_("TIMED OUT WAITING FOR GDB REPLY. COMMANDS STILL IN PROGRESS:"));
865 int timeOut = m_commandTimer.interval();
866 //m_commandTimer.stop();
867 const QString msg = tr("The gdb process has not responded "
868 "to a command within %1 seconds. This could mean it is stuck "
869 "in an endless loop or taking longer than expected to perform "
870 "the operation.\nYou can choose between waiting "
871 "longer or abort debugging.").arg(timeOut / 1000);
872 QMessageBox *mb = showMessageBox(QMessageBox::Critical,
873 tr("GDB not responding"), msg,
874 QMessageBox::Ok | QMessageBox::Cancel);
875 mb->button(QMessageBox::Cancel)->setText(tr("Give GDB more time"));
876 mb->button(QMessageBox::Ok)->setText(tr("Stop debugging"));
877 if (mb->exec() == QMessageBox::Ok) {
878 showMessage(_("KILLING DEBUGGER AS REQUESTED BY USER"));
879 // This is an undefined state, so we just pull the emergency brake.
880 watchHandler()->endCycle();
883 showMessage(_("CONTINUE DEBUGGER AS REQUESTED BY USER"));
886 showMessage(_("\nNON-CRITICAL TIMEOUT\n"));
890 void GdbEngine::handleResultRecord(GdbResponse *response)
892 //qDebug() << "TOKEN:" << response.token
893 // << " ACCEPTABLE:" << m_oldestAcceptableToken;
894 //qDebug() << "\nRESULT" << response.token << response.toString();
896 int token = response->token;
900 if (!m_cookieForToken.contains(token)) {
901 // In theory this should not happen (rather the error should be
902 // reported in the "first" response to the command) in practice it
903 // does. We try to handle a few situations we are aware of gracefully.
904 // Ideally, this code should not be present at all.
905 showMessage(_("COOKIE FOR TOKEN %1 ALREADY EATEN (%2). "
906 "TWO RESPONSES FOR ONE COMMAND?").arg(token).
907 arg(QString::fromAscii(stateName(state()))));
908 if (response->resultClass == GdbResultError) {
909 QByteArray msg = response->data.findChild("msg").data();
910 if (msg == "Cannot find new threads: generic error") {
911 // Handle a case known to occur on Linux/gdb 6.8 when debugging moc
912 // with helpers enabled. In this case we get a second response with
913 // msg="Cannot find new threads: generic error"
914 showMessage(_("APPLYING WORKAROUND #1"));
915 showMessageBox(QMessageBox::Critical,
916 tr("Executable failed"), QString::fromLocal8Bit(msg));
917 showStatusMessage(tr("Process failed to start"));
920 } else if (msg == "\"finish\" not meaningful in the outermost frame.") {
921 // Handle a case known to appear on GDB 6.4 symbianelf when
922 // the stack is cut due to access to protected memory.
923 //showMessage(_("APPLYING WORKAROUND #2"));
924 notifyInferiorStopOk();
925 } else if (msg.startsWith("Cannot find bounds of current function")) {
926 // Happens when running "-exec-next" in a function for which
927 // there is no debug information. Divert to "-exec-next-step"
928 showMessage(_("APPLYING WORKAROUND #3"));
929 notifyInferiorStopOk();
931 } else if (msg.startsWith("Couldn't get registers: No such process.")) {
932 // Happens on archer-tromey-python 6.8.50.20090910-cvs
933 // There might to be a race between a process shutting down
934 // and library load messages.
935 showMessage(_("APPLYING WORKAROUND #4"));
936 notifyInferiorStopOk();
937 //notifyInferiorIll();
938 //showStatusMessage(tr("Executable failed: %1")
939 // .arg(QString::fromLocal8Bit(msg)));
941 //showMessageBox(QMessageBox::Critical,
942 // tr("Executable failed"), QString::fromLocal8Bit(msg));
943 } else if (msg.contains("Cannot insert breakpoint")) {
944 // For breakpoints set by address to non-existent addresses we
945 // might get something like "6^error,msg="Warning:\nCannot insert
946 // breakpoint 3.\nError accessing memory address 0x34592327:
947 // Input/output error.\nCannot insert breakpoint 4.\nError
948 // accessing memory address 0x34592335: Input/output error.\n".
949 // This should not stop us from proceeding.
950 // Most notably, that happens after a "6^running" and "*running"
951 // We are probably sitting at _start and can't proceed as
952 // long as the breakpoints are enabled.
953 // FIXME: Should we silently disable the offending breakpoints?
954 showMessage(_("APPLYING WORKAROUND #5"));
955 showMessageBox(QMessageBox::Critical,
956 tr("Setting breakpoints failed"), QString::fromLocal8Bit(msg));
957 QTC_ASSERT(state() == InferiorRunOk, /**/);
958 notifyInferiorSpontaneousStop();
961 // Windows: Some DLL or some function not found. Report
962 // the exception now in a box.
963 if (msg.startsWith("During startup program exited with"))
964 notifyInferiorExited();
966 if (!m_lastWinException.isEmpty())
967 logMsg = m_lastWinException + QLatin1Char('\n');
968 logMsg += QString::fromLocal8Bit(msg);
969 showMessageBox(QMessageBox::Critical, tr("Executable Failed"), logMsg);
970 showStatusMessage(tr("Executable failed: %1").arg(logMsg));
976 GdbCommand cmd = m_cookieForToken.take(token);
977 if (debuggerCore()->boolSetting(LogTimeStamps)) {
978 showMessage(_("Response time: %1: %2 s")
980 .arg(cmd.postTime.msecsTo(QTime::currentTime()) / 1000.),
984 if (response->token < m_oldestAcceptableToken && (cmd.flags & Discardable)) {
985 //showMessage(_("### SKIPPING OLD RESULT") + response.toString());
989 response->cookie = cmd.cookie;
991 bool isExpectedResult =
992 (response->resultClass == GdbResultError) // Can always happen.
993 || (response->resultClass == GdbResultRunning && (cmd.flags & RunRequest))
994 || (response->resultClass == GdbResultExit && (cmd.flags & ExitRequest))
995 || (response->resultClass == GdbResultDone);
996 // GdbResultDone can almost "always" happen. Known examples are:
997 // (response->resultClass == GdbResultDone && cmd.command == "continue")
998 // Happens with some incarnations of gdb 6.8 for "jump to line"
999 // (response->resultClass == GdbResultDone && cmd.command.startsWith("jump"))
1000 // (response->resultClass == GdbResultDone && cmd.command.startsWith("detach"))
1001 // Happens when stepping finishes very quickly and issues *stopped/^done
1002 // instead of ^running/*stopped
1003 // (response->resultClass == GdbResultDone && (cmd.flags & RunRequest));
1005 if (!isExpectedResult) {
1007 // Ignore spurious 'running' responses to 'attach'
1008 const bool warning = !((startParameters().startMode == AttachExternal
1009 || startParameters().useTerminal)
1010 && cmd.command.startsWith("attach"));
1012 const bool warning = true;
1015 QByteArray rsp = GdbResponse::stringFromResultClass(response->resultClass);
1016 rsp = "UNEXPECTED RESPONSE '" + rsp + "' TO COMMAND '" + cmd.command + "'";
1017 qWarning() << rsp << " AT " __FILE__ ":" STRINGIFY(__LINE__);
1018 showMessage(_(rsp));
1023 (this->*cmd.callback)(*response);
1024 else if (cmd.adapterCallback)
1025 (m_gdbAdapter->*cmd.adapterCallback)(*response);
1027 if (cmd.flags & RebuildWatchModel) {
1028 --m_pendingWatchRequests;
1029 PENDING_DEBUG(" WATCH" << cmd.command << "=>" << cmd.callbackName
1030 << "DECREMENTS PENDING WATCH TO" << m_pendingWatchRequests);
1031 if (m_pendingWatchRequests <= 0) {
1032 PENDING_DEBUG("\n\n ... AND TRIGGERS WATCH MODEL UPDATE\n");
1033 rebuildWatchModel();
1035 } else if (cmd.flags & RebuildBreakpointModel) {
1036 --m_pendingBreakpointRequests;
1037 PENDING_DEBUG(" BREAKPOINT" << cmd.command << "=>" << cmd.callbackName
1038 << "DECREMENTS PENDING TO" << m_pendingWatchRequests);
1039 if (m_pendingBreakpointRequests <= 0) {
1040 PENDING_DEBUG("\n\n ... AND TRIGGERS BREAKPOINT MODEL UPDATE\n");
1041 attemptBreakpointSynchronization();
1044 PENDING_DEBUG(" OTHER (OUT):" << cmd.command << "=>" << cmd.callbackName
1045 << "LEAVES PENDING WATCH AT" << m_pendingWatchRequests
1046 << "LEAVES PENDING BREAKPOINT AT" << m_pendingBreakpointRequests);
1049 // Commands were queued, but we were in RunningRequested state, so the interrupt
1051 // This is done after the command callbacks so the running-requesting commands
1052 // can assert on the right state.
1053 if (state() == InferiorRunOk && !m_commandsToRunOnTemporaryBreak.isEmpty())
1054 interruptInferiorTemporarily();
1056 // Continue only if there are no commands wire anymore, so this will
1057 // be fully synchronous.
1058 // This is somewhat inefficient, as it makes the last command synchronous.
1059 // An optimization would be requesting the continue immediately when the
1060 // event loop is entered, and let individual commands have a flag to suppress
1062 if (m_commandsDoneCallback && m_cookieForToken.isEmpty()) {
1063 showMessage(_("ALL COMMANDS DONE; INVOKING CALLBACK"));
1064 CommandsDoneCallback cont = m_commandsDoneCallback;
1065 m_commandsDoneCallback = 0;
1068 PENDING_DEBUG("MISSING TOKENS: " << m_cookieForToken.keys());
1071 if (m_cookieForToken.isEmpty())
1072 m_commandTimer.stop();
1075 bool GdbEngine::acceptsDebuggerCommands() const
1077 return state() == InferiorStopOk
1078 || state() == InferiorUnrunnable;
1081 void GdbEngine::executeDebuggerCommand(const QString &command)
1083 QTC_ASSERT(acceptsDebuggerCommands(), /**/);
1084 m_gdbAdapter->write(command.toLatin1() + "\r\n");
1087 // This is called from CoreAdapter and AttachAdapter.
1088 void GdbEngine::updateAll()
1096 void GdbEngine::handleQuerySources(const GdbResponse &response)
1098 m_sourcesListUpdating = false;
1099 m_sourcesListOutdated = false;
1100 if (response.resultClass == GdbResultDone) {
1101 QMap<QString, QString> oldShortToFull = m_shortToFullName;
1102 m_shortToFullName.clear();
1103 m_fullToShortName.clear();
1104 // "^done,files=[{file="../../../../bin/gdbmacros/gdbmacros.cpp",
1105 // fullname="/data5/dev/ide/main/bin/gdbmacros/gdbmacros.cpp"},
1106 GdbMi files = response.data.findChild("files");
1107 foreach (const GdbMi &item, files.children()) {
1108 GdbMi fullName = item.findChild("fullname");
1109 GdbMi fileName = item.findChild("file");
1110 QString file = QString::fromLocal8Bit(fileName.data());
1111 if (fullName.isValid()) {
1112 QString full = cleanupFullName(QString::fromLocal8Bit(fullName.data()));
1113 m_shortToFullName[file] = full;
1114 m_fullToShortName[full] = file;
1115 } else if (fileName.isValid()) {
1116 m_shortToFullName[file] = tr("<unknown>");
1119 if (m_shortToFullName != oldShortToFull)
1120 sourceFilesHandler()->setSourceFiles(m_shortToFullName);
1124 void GdbEngine::handleExecuteJumpToLine(const GdbResponse &response)
1126 if (response.resultClass == GdbResultRunning) {
1127 doNotifyInferiorRunOk();
1128 // All is fine. Waiting for the temporary breakpoint to be hit.
1129 } else if (response.resultClass == GdbResultDone) {
1130 // This happens on old gdb. Trigger the effect of a '*stopped'.
1131 showStatusMessage(tr("Jumped. Stopped"));
1132 notifyInferiorSpontaneousStop();
1133 handleStop1(response);
1137 void GdbEngine::handleExecuteRunToLine(const GdbResponse &response)
1139 if (response.resultClass == GdbResultRunning) {
1140 doNotifyInferiorRunOk();
1141 // All is fine. Waiting for the temporary breakpoint to be hit.
1142 } else if (response.resultClass == GdbResultDone) {
1143 // This happens on old gdb. Trigger the effect of a '*stopped'.
1145 // >~"Continuing.\n"
1146 //>~"testArray () at ../simple/app.cpp:241\n"
1147 //>~"241\t s[1] = \"b\";\n"
1149 gotoLocation(m_targetFrame);
1150 showStatusMessage(tr("Target line hit. Stopped"));
1151 notifyInferiorSpontaneousStop();
1152 handleStop1(response);
1156 static bool isExitedReason(const QByteArray &reason)
1158 return reason == "exited-normally" // inferior exited normally
1159 || reason == "exited-signalled" // inferior exited because of a signal
1160 //|| reason == "signal-received" // inferior received signal
1161 || reason == "exited"; // inferior exited
1165 void GdbEngine::handleAqcuiredInferior()
1167 // Reverse debugging. FIXME: Should only be used when available.
1168 //if (debuggerCore()->boolSetting(EnableReverseDebugging))
1169 // postCommand("target response");
1171 tryLoadDebuggingHelpers();
1174 // intentionally after tryLoadDebuggingHelpers(),
1175 // otherwise we'd interrupt solib loading.
1176 if (debuggerCore()->boolSetting(AllPluginBreakpoints)) {
1177 postCommand("set auto-solib-add on");
1178 postCommand("set stop-on-solib-events 0");
1179 postCommand("sharedlibrary .*");
1180 } else if (debuggerCore()->boolSetting(SelectedPluginBreakpoints)) {
1181 postCommand("set auto-solib-add on");
1182 postCommand("set stop-on-solib-events 1");
1183 postCommand("sharedlibrary "
1184 + theDebuggerStringSetting(SelectedPluginBreakpointsPattern));
1185 } else if (debuggerCore()->boolSetting(NoPluginBreakpoints)) {
1186 // should be like that already
1187 postCommand("set auto-solib-add off");
1188 postCommand("set stop-on-solib-events 0");
1192 // It's nicer to see a bit of the world we live in.
1193 reloadModulesInternal();
1198 # define STOP_SIGNAL "SIGINT"
1199 # define CROSS_STOP_SIGNAL "SIGTRAP"
1201 # define STOP_SIGNAL "SIGTRAP"
1202 # define CROSS_STOP_SIGNAL "SIGINT"
1205 void GdbEngine::handleStopResponse(const GdbMi &data)
1207 // This is gdb 7+'s initial *stopped in response to attach.
1208 // For consistency, we just discard it.
1209 if (state() == InferiorSetupRequested)
1213 notifyInferiorStopOk();
1217 const QByteArray reason = data.findChild("reason").data();
1219 if (isExitedReason(reason)) {
1220 // // The user triggered a stop, but meanwhile the app simply exited ...
1221 // QTC_ASSERT(state() == InferiorStopRequested
1222 // /*|| state() == InferiorStopRequested_Kill*/,
1223 // qDebug() << state());
1225 if (reason == "exited") {
1226 msg = tr("Application exited with exit code %1")
1227 .arg(_(data.findChild("exit-code").toString()));
1228 } else if (reason == "exited-signalled" || reason == "signal-received") {
1229 msg = tr("Application exited after receiving signal %1")
1230 .arg(_(data.findChild("signal-name").toString()));
1232 msg = tr("Application exited normally");
1234 showStatusMessage(msg);
1235 notifyInferiorExited();
1239 const int bkptno = data.findChild("bkptno").data().toInt();
1240 const GdbMi frame = data.findChild("frame");
1242 const int lineNumber = frame.findChild("line").data().toInt();
1243 QString fullName = QString::fromUtf8(frame.findChild("fullname").data());
1245 if (fullName.isEmpty())
1246 fullName = QString::fromUtf8(frame.findChild("file").data());
1248 if (bkptno && frame.isValid()
1249 && !isQmlStepBreakpoint1(bkptno)
1250 && !isQmlStepBreakpoint2(bkptno)
1251 && !isQFatalBreakpoint(bkptno)) {
1252 // Use opportunity to update the breakpoint marker position.
1253 BreakHandler *handler = breakHandler();
1254 //qDebug() << " PROBLEM: " << m_qmlBreakpointNumbers << bkptno
1255 // << isQmlStepBreakpoint1(bkptno)
1256 // << isQmlStepBreakpoint2(bkptno)
1257 BreakpointId id = handler->findBreakpointByNumber(bkptno);
1258 const BreakpointResponse &response = handler->response(id);
1259 QString fileName = response.fileName;
1260 if (fileName.isEmpty())
1261 fileName = handler->fileName(id);
1262 if (fileName.isEmpty())
1263 fileName = fullName;
1264 if (!fileName.isEmpty())
1265 handler->setMarkerFileAndLine(id, fileName, lineNumber);
1268 //qDebug() << "BP " << bkptno << data.toString();
1269 // Quickly set the location marker.
1270 if (lineNumber && !debuggerCore()->boolSetting(OperateByInstruction)
1271 && QFileInfo(fullName).exists()
1272 && !isQmlStepBreakpoint1(bkptno)
1273 && !isQmlStepBreakpoint2(bkptno)
1274 && !isQFatalBreakpoint(bkptno))
1275 gotoLocation(Location(fullName, lineNumber));
1277 if (!m_commandsToRunOnTemporaryBreak.isEmpty()) {
1278 QTC_ASSERT(state() == InferiorStopRequested, qDebug() << state())
1279 notifyInferiorStopOk();
1280 flushQueuedCommands();
1281 if (state() == InferiorStopOk) {
1282 QTC_ASSERT(m_commandsDoneCallback == 0, /**/);
1283 m_commandsDoneCallback = &GdbEngine::autoContinueInferior;
1285 QTC_ASSERT(state() == InferiorShutdownRequested, qDebug() << state())
1290 if (state() == InferiorRunOk) {
1291 // Stop triggered by a breakpoint or otherwise not directly
1292 // initiated by the user.
1293 notifyInferiorSpontaneousStop();
1294 } else if (state() == InferiorRunRequested) {
1295 // Stop triggered by something like "-exec-step\n"
1296 // "&"Cannot access memory at address 0xbfffedd4\n"
1297 // In this case a proper response 94^error,msg="" will follow and
1298 // be handled in the result handler.
1300 // *stopped arriving earlier than ^done response to an -exec-step
1301 doNotifyInferiorRunOk();
1302 notifyInferiorSpontaneousStop();
1303 } else if (state() == InferiorStopOk && isQmlStepBreakpoint2(bkptno)) {
1306 QTC_ASSERT(state() == InferiorStopRequested, qDebug() << state());
1307 notifyInferiorStopOk();
1310 // FIXME: Replace the #ifdef by the "target" architecture.
1312 if (!m_entryPoint.isEmpty()) {
1313 // This is needed as long as we support stock gdb 6.8.
1314 if (frame.findChild("addr").data() == m_entryPoint) {
1315 // There are two expected reasons for getting here:
1316 // 1) For some reason, attaching to a stopped process causes *two*
1318 // when trying to continue (kernel i386 2.6.24-23-ubuntu, gdb 6.8).
1319 // Interestingly enough, on MacOSX no signal is delivered at all.
1320 // 2) The explicit tbreak at the entry point we set to query the PID.
1321 // Gdb <= 6.8 reports a frame but no reason, 6.8.50+ reports
1323 // The case of the user really setting a breakpoint at _start is simply
1325 if (!inferiorPid()) // For programs without -pthread under gdb <= 6.8.
1326 postCommand("info proc", CB(handleInfoProc));
1327 continueInferiorInternal();
1330 // We are past the initial stop(s). No need to waste time on further checks.
1331 m_entryPoint.clear();
1335 //qDebug() << "STOP: " << m_qmlBreakpointNumbers;
1337 if (isQmlStepBreakpoint1(bkptno))
1343 void GdbEngine::handleStop0(const GdbMi &data)
1345 #if 0 // See http://vladimir_prus.blogspot.com/2007/12/debugger-stories-pending-breakpoints.html
1346 // Due to LD_PRELOADing the dumpers, these events can occur even before
1347 // reaching the entry point. So handle it before the entry point hacks below.
1348 if (reason.isEmpty() && m_gdbVersion < 70000 && !m_isMacGdb) {
1349 // On Linux it reports "Stopped due to shared library event\n", but
1350 // on Windows it simply forgets about it. Thus, we identify the response
1351 // based on it having no frame information.
1352 if (!data.findChild("frame").isValid()) {
1353 invalidateSourcesList();
1354 // Each stop causes a roundtrip and button flicker, so prevent
1355 // a flood of useless stops. Will be automatically re-enabled.
1356 postCommand("set stop-on-solib-events 0");
1358 // The related code (handleAqcuiredInferior()) is disabled as well.
1359 if (debuggerCore()->boolSetting(SelectedPluginBreakpoints)) {
1360 QString dataStr = _(data.toString());
1361 showMessage(_("SHARED LIBRARY EVENT: ") + dataStr);
1362 QString pat = theDebuggerStringSetting(SelectedPluginBreakpointsPattern);
1363 showMessage(_("PATTERN: ") + pat);
1364 postCommand("sharedlibrary " + pat.toLocal8Bit());
1365 showStatusMessage(tr("Loading %1...").arg(dataStr));
1368 continueInferiorInternal();
1374 const GdbMi frame = data.findChild("frame");
1375 const QByteArray reason = data.findChild("reason").data();
1377 // This was seen on XP after removing a breakpoint while running
1378 // >945*stopped,reason="signal-received",signal-name="SIGTRAP",
1379 // signal-meaning="Trace/breakpoint trap",thread-id="2",
1380 // frame={addr="0x7c91120f",func="ntdll!DbgUiConnectToDbg",
1381 // args=[],from="C:\\WINDOWS\\system32\\ntdll.dll"}
1382 // also seen on gdb 6.8-symbianelf without qXfer:libraries:read+;
1383 // FIXME: remote.c parses "loaded" reply. It should be turning
1384 // that into a TARGET_WAITKIND_LOADED. Does it?
1385 // The bandaid here has the problem that it breaks for 'next' over a
1386 // statement that indirectly loads shared libraries
1387 // 6.1.2010: Breaks interrupting inferiors, disabled:
1388 // if (reason == "signal-received"
1389 // && data.findChild("signal-name").data() == "SIGTRAP") {
1390 // continueInferiorInternal();
1394 // Jump over well-known frames.
1395 static int stepCounter = 0;
1396 if (debuggerCore()->boolSetting(SkipKnownFrames)) {
1397 if (reason == "end-stepping-range" || reason == "function-finished") {
1398 //showMessage(frame.toString());
1399 QString funcName = _(frame.findChild("func").data());
1400 QString fileName = QString::fromLocal8Bit(frame.findChild("file").data());
1401 if (isLeavableFunction(funcName, fileName)) {
1402 //showMessage(_("LEAVING ") + funcName);
1407 if (isSkippableFunction(funcName, fileName)) {
1408 //showMessage(_("SKIPPING ") + funcName);
1414 // qDebug() << "STEPCOUNTER:" << stepCounter;
1419 // Show return value if possible, usually with reason "function-finished".
1420 // *stopped,reason="function-finished",frame={addr="0x080556da",
1421 // func="testReturnValue",args=[],file="/../app.cpp",
1422 // fullname="/../app.cpp",line="1611"},gdb-result-var="$1",
1423 // return-value="{d = 0x808d998}",thread-id="1",stopped-threads="all",
1425 GdbMi resultVar = data.findChild("gdb-result-var");
1426 if (resultVar.isValid())
1427 m_resultVarName = resultVar.data();
1429 m_resultVarName.clear();
1431 bool initHelpers = m_debuggingHelperState == DebuggingHelperUninitialized
1432 || m_debuggingHelperState == DebuggingHelperLoadTried;
1433 // Don't load helpers on stops triggered by signals unless it's
1434 // an intentional trap.
1436 && m_gdbAdapter->dumperHandling() != AbstractGdbAdapter::DumperLoadedByGdbPreload
1437 && reason == "signal-received") {
1438 QByteArray name = data.findChild("signal-name").data();
1439 if (name != STOP_SIGNAL
1440 && (startParameters().startMode != AttachToRemote
1441 || name != CROSS_STOP_SIGNAL))
1442 initHelpers = false;
1444 if (isSynchronous())
1445 initHelpers = false;
1447 tryLoadDebuggingHelpersClassic();
1448 QVariant var = QVariant::fromValue<GdbMi>(data);
1449 postCommand("p 4", CB(handleStop1), var); // dummy
1453 // Dumper loading is sequenced, as otherwise the display functions
1454 // will start requesting data without knowing that dumpers are available.
1457 void GdbEngine::handleStop1(const GdbResponse &response)
1459 handleStop1(response.cookie.value<GdbMi>());
1462 void GdbEngine::handleStop1(const GdbMi &data)
1465 qDebug() << "HANDLING STOP WHILE DYING";
1466 notifyInferiorStopOk();
1470 QByteArray reason = data.findChild("reason").data();
1473 if (startParameters().useTerminal && reason == "signal-received"
1474 && data.findChild("signal-name").data() == "SIGTRAP") {
1475 // Command line start up trap
1476 showMessage(_("INTERNAL CONTINUE"), LogMisc);
1477 continueInferiorInternal();
1482 // This is for display only.
1483 if (m_modulesListOutdated)
1484 reloadModulesInternal();
1486 if (m_breakListOutdated) {
1487 reloadBreakListInternal();
1489 // Older gdb versions do not produce "library loaded" messages
1490 // so the breakpoint update is not triggered.
1491 if (m_gdbVersion < 70000 && !m_isMacGdb && breakHandler()->size() > 0)
1492 reloadBreakListInternal();
1495 if (reason == "watchpoint-trigger") {
1496 // *stopped,reason="watchpoint-trigger",wpt={number="2",exp="*0xbfffed40"},
1497 // value={old="1",new="0"},frame={addr="0x00451e1b",
1498 // func="QScopedPointer",args=[{name="this",value="0xbfffed40"},
1499 // {name="p",value="0x0"}],file="x.h",fullname="/home/.../x.h",line="95"},
1500 // thread-id="1",stopped-threads="all",core="2"
1501 GdbMi wpt = data.findChild("wpt");
1502 const int bpNumber = wpt.findChild("number").data().toInt();
1503 const BreakpointId id = breakHandler()->findBreakpointByNumber(bpNumber);
1504 const quint64 bpAddress = wpt.findChild("exp").data().mid(1).toULongLong(0, 0);
1505 QString msg = msgWatchpointTriggered(id, bpNumber, bpAddress);
1506 GdbMi value = data.findChild("value");
1507 GdbMi oldValue = value.findChild("old");
1508 GdbMi newValue = value.findChild("new");
1509 if (oldValue.isValid() && newValue.isValid()) {
1510 msg += QLatin1Char(' ');
1511 msg += tr("Value changed from %1 to %2.")
1512 .arg(_(oldValue.data())).arg(_(newValue.data()));
1514 showStatusMessage(msg);
1515 } else if (reason == "breakpoint-hit") {
1516 GdbMi gNumber = data.findChild("bkptno"); // 'number' or 'bkptno'?
1517 if (!gNumber.isValid())
1518 gNumber = data.findChild("number");
1519 const int bpNumber = gNumber.data().toInt();
1520 const QByteArray threadId = data.findChild("thread-id").data();
1521 const BreakpointId id = breakHandler()->findBreakpointByNumber(bpNumber);
1522 showStatusMessage(msgBreakpointTriggered(id, bpNumber, _(threadId)));
1523 m_currentThread = threadId;
1525 QString reasontr = msgStopped(_(reason));
1526 if (reason == "signal-received") {
1527 QByteArray name = data.findChild("signal-name").data();
1528 QByteArray meaning = data.findChild("signal-meaning").data();
1529 // Ignore these as they are showing up regularly when
1530 // stopping debugging.
1531 if (name == STOP_SIGNAL) {
1532 showMessage(_(STOP_SIGNAL " CONSIDERED HARMLESS. CONTINUING."));
1533 } else if (startParameters().startMode == AttachToRemote
1534 && name == CROSS_STOP_SIGNAL) {
1535 showMessage(_(CROSS_STOP_SIGNAL " CONSIDERED HARMLESS. CONTINUING."));
1537 showMessage(_("HANDLING SIGNAL" + name));
1538 if (debuggerCore()->boolSetting(UseMessageBoxForSignals))
1539 showStoppedBySignalMessageBox(_(meaning), _(name));
1540 if (!name.isEmpty() && !meaning.isEmpty())
1541 reasontr = msgStoppedBySignal(_(meaning), _(name));
1545 if (reason.isEmpty())
1546 showStatusMessage(msgStopped());
1548 showStatusMessage(reasontr);
1551 // Let the event loop run before deciding whether to update the stack.
1552 m_stackNeeded = true; // setTokenBarrier() might reset this.
1553 m_currentThreadId = data.findChild("thread-id").data().toInt();
1554 QTimer::singleShot(0, this, SLOT(handleStop2()));
1557 void GdbEngine::handleStop2()
1559 // We are already continuing.
1563 reloadStack(false); // Will trigger register reload.
1565 if (supportsThreads()) {
1566 if (m_gdbAdapter->isTrkAdapter()) {
1567 m_gdbAdapter->trkReloadThreads();
1568 } else if (m_isMacGdb) {
1569 postCommand("-thread-list-ids", Discardable,
1570 CB(handleThreadListIds), m_currentThreadId);
1572 // This is only available in gdb 7.1+.
1573 postCommand("-thread-info", Discardable,
1574 CB(handleThreadInfo), m_currentThreadId);
1580 void GdbEngine::handleInfoProc(const GdbResponse &response)
1582 if (response.resultClass == GdbResultDone) {
1583 static QRegExp re(_("\\bprocess ([0-9]+)\n"));
1584 QTC_ASSERT(re.isValid(), return);
1585 if (re.indexIn(_(response.data.findChild("consolestreamoutput").data())) != -1)
1586 maybeHandleInferiorPidChanged(re.cap(1));
1590 void GdbEngine::handleShowVersion(const GdbResponse &response)
1592 showMessage(_("PARSING VERSION: " + response.toString()));
1593 if (response.resultClass == GdbResultDone) {
1595 m_gdbBuildVersion = -1;
1597 GdbMi version = response.data.findChild("consolestreamoutput");
1598 QString msg = QString::fromLocal8Bit(version.data());
1599 extractGdbVersion(msg,
1600 &m_gdbVersion, &m_gdbBuildVersion, &m_isMacGdb);
1601 if (m_gdbVersion > 60500 && m_gdbVersion < 200000)
1602 showMessage(_("SUPPORTED GDB VERSION ") + msg);
1604 showMessage(_("UNSUPPORTED GDB VERSION ") + msg);
1606 showMessage(_("USING GDB VERSION: %1, BUILD: %2%3").arg(m_gdbVersion)
1607 .arg(m_gdbBuildVersion).arg(_(m_isMacGdb ? " (APPLE)" : "")));
1611 void GdbEngine::handleHasPython(const GdbResponse &response)
1613 if (response.resultClass == GdbResultDone) {
1615 GdbMi contents = response.data.findChild("consolestreamoutput");
1617 data.fromStringMultiple(contents.data());
1618 const GdbMi dumpers = data.findChild("dumpers");
1619 foreach (const GdbMi &dumper, dumpers.children()) {
1620 QByteArray type = dumper.findChild("type").data();
1621 QStringList formats(tr("Raw structure"));
1622 foreach (const QByteArray &format,
1623 dumper.findChild("formats").data().split(',')) {
1624 if (format == "Normal")
1625 formats.append(tr("Normal"));
1626 else if (format == "Displayed")
1627 formats.append(tr("Displayed"));
1628 else if (!format.isEmpty())
1629 formats.append(_(format));
1631 watchHandler()->addTypeFormats(type, formats);
1633 const GdbMi hasInferiorThreadList = data.findChild("hasInferiorThreadList");
1634 m_hasInferiorThreadList = (hasInferiorThreadList.data().toInt() != 0);
1636 pythonDumpersFailed();
1640 void GdbEngine::pythonDumpersFailed()
1642 m_hasPython = false;
1643 if (m_gdbAdapter->dumperHandling()
1644 == AbstractGdbAdapter::DumperLoadedByGdbPreload
1645 && checkDebuggingHelpersClassic()) {
1647 const char * const LD_PRELOAD_ENV_VAR = "DYLD_INSERT_LIBRARIES";
1649 const char * const LD_PRELOAD_ENV_VAR = "LD_PRELOAD";
1651 QByteArray cmd = "set environment ";
1652 cmd += LD_PRELOAD_ENV_VAR;
1654 cmd += startParameters().startMode == StartRemoteGdb
1655 ? startParameters().remoteDumperLib
1656 : qtDumperLibraryName().toLocal8Bit();
1658 m_debuggingHelperState = DebuggingHelperLoadTried;
1662 void GdbEngine::showExecutionError(const QString &message)
1664 showMessageBox(QMessageBox::Critical, tr("Execution Error"),
1665 tr("Cannot continue debugged process:\n") + message);
1668 void GdbEngine::handleExecuteContinue(const GdbResponse &response)
1670 QTC_ASSERT(state() == InferiorRunRequested, qDebug() << state());
1671 if (response.resultClass == GdbResultRunning) {
1672 doNotifyInferiorRunOk();
1675 QByteArray msg = response.data.findChild("msg").data();
1676 if (msg.startsWith("Cannot find bounds of current function")) {
1677 notifyInferiorRunFailed();
1680 if (!m_commandsToRunOnTemporaryBreak.isEmpty())
1681 flushQueuedCommands();
1682 QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
1683 showStatusMessage(tr("Stopped."), 5000);
1685 } else if (msg.startsWith("\"finish\" not meaningful in the outermost frame")) {
1686 notifyInferiorRunFailed();
1689 QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
1690 // FIXME: Fix translation in master.
1691 showStatusMessage(QString::fromLocal8Bit(msg), 5000);
1692 gotoLocation(stackHandler()->currentFrame());
1694 showExecutionError(QString::fromLocal8Bit(msg));
1695 notifyInferiorIll();
1699 QString GdbEngine::fullName(const QString &fileName)
1701 if (fileName.isEmpty())
1703 //QTC_ASSERT(!m_sourcesListOutdated, /* */)
1704 QTC_ASSERT(!m_sourcesListUpdating, /* */)
1705 return m_shortToFullName.value(fileName, QString());
1708 QString GdbEngine::cleanupFullName(const QString &fileName)
1710 QString cleanFilePath = fileName;
1712 QTC_ASSERT(!fileName.isEmpty(), return QString())
1713 // Gdb on windows often delivers "fullnames" which
1714 // (a) have no drive letter and (b) are not normalized.
1715 QFileInfo fi(fileName);
1716 if (fi.isReadable())
1717 cleanFilePath = QDir::cleanPath(fi.absoluteFilePath());
1719 if (startMode() == StartRemoteGdb) {
1720 cleanFilePath.replace(0, startParameters().remoteMountPoint.length(),
1721 startParameters().localMountDir);
1723 return cleanFilePath;
1726 void GdbEngine::shutdownInferior()
1728 QTC_ASSERT(state() == InferiorShutdownRequested, qDebug() << state());
1729 m_commandsToRunOnTemporaryBreak.clear();
1730 m_gdbAdapter->shutdownInferior();
1733 void GdbEngine::defaultInferiorShutdown(const char *cmd)
1735 QTC_ASSERT(state() == InferiorShutdownRequested, qDebug() << state());
1736 postCommand(cmd, NeedsStop | LosesChild, CB(handleInferiorShutdown));
1739 void GdbEngine::handleInferiorShutdown(const GdbResponse &response)
1741 QTC_ASSERT(state() == InferiorShutdownRequested, qDebug() << state());
1742 if (response.resultClass == GdbResultDone) {
1743 notifyInferiorShutdownOk();
1746 QByteArray ba = response.data.findChild("msg").data();
1747 if (ba.contains(": No such file or directory.")) {
1748 // This happens when someone removed the binary behind our back.
1749 // It is not really an error from a user's point of view.
1750 showMessage(_("NOTE: " + ba));
1751 notifyInferiorShutdownOk();
1754 showMessageBox(QMessageBox::Critical,
1755 tr("Failed to shut down application"),
1756 AbstractGdbAdapter::msgInferiorStopFailed(QString::fromLocal8Bit(ba)));
1757 notifyInferiorShutdownFailed();
1760 void GdbEngine::shutdownEngine()
1762 m_gdbAdapter->shutdownAdapter();
1765 void GdbEngine::notifyAdapterShutdownFailed()
1767 showMessage(_("ADAPTER SHUTDOWN FAILED"));
1768 QTC_ASSERT(state() == EngineShutdownRequested, qDebug() << state());
1769 notifyEngineShutdownFailed();
1772 void GdbEngine::notifyAdapterShutdownOk()
1774 QTC_ASSERT(state() == EngineShutdownRequested, qDebug() << state());
1775 showMessage(_("INITIATE GDBENGINE SHUTDOWN IN STATE %1, PROC: %2")
1776 .arg(lastGoodState()).arg(gdbProc()->state()));
1777 m_commandsDoneCallback = 0;
1778 switch (gdbProc()->state()) {
1779 case QProcess::Running:
1780 postCommand("-gdb-exit", GdbEngine::ExitRequest, CB(handleGdbExit));
1782 case QProcess::NotRunning:
1783 // Cannot find executable.
1784 notifyEngineShutdownOk();
1786 case QProcess::Starting:
1787 showMessage(_("GDB NOT REALLY RUNNING; KILLING IT"));
1789 notifyEngineShutdownFailed();
1794 void GdbEngine::handleGdbExit(const GdbResponse &response)
1796 if (response.resultClass == GdbResultExit) {
1797 showMessage(_("GDB CLAIMS EXIT; WAITING"));
1798 // Don't set state here, this will be handled in handleGdbFinished()
1799 //notifyEngineShutdownOk();
1801 QString msg = m_gdbAdapter->msgGdbStopFailed(
1802 QString::fromLocal8Bit(response.data.findChild("msg").data()));
1803 qDebug() << (_("GDB WON'T EXIT (%1); KILLING IT").arg(msg));
1804 showMessage(_("GDB WON'T EXIT (%1); KILLING IT").arg(msg));
1809 void GdbEngine::detachDebugger()
1811 QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
1812 QTC_ASSERT(startMode() != AttachCore, qDebug() << startMode());
1813 postCommand("detach", GdbEngine::ExitRequest, CB(handleDetach));
1816 void GdbEngine::handleDetach(const GdbResponse &response)
1819 QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
1820 notifyInferiorExited();
1823 int GdbEngine::currentFrame() const
1825 return stackHandler()->currentIndex();
1828 QString msgNoGdbBinaryForToolChain(const ProjectExplorer::Abi &tc)
1830 return GdbEngine::tr("There is no gdb binary available for binaries in format '%1'")
1831 .arg(tc.toString());
1834 AbstractGdbAdapter *GdbEngine::createAdapter()
1836 const DebuggerStartParameters &sp = startParameters();
1837 if (sp.toolChainAbi.os() == ProjectExplorer::Abi::SymbianOS) {
1838 // FIXME: 1 of 3 testing hacks.
1839 if (sp.debugClient == DebuggerStartParameters::DebugClientCoda)
1840 return new CodaGdbAdapter(this);
1842 return new TrkGdbAdapter(this);
1845 switch (sp.startMode) {
1847 return new CoreGdbAdapter(this);
1848 case AttachToRemote:
1849 return new RemoteGdbServerAdapter(this, sp.toolChainAbi);
1850 case StartRemoteGdb:
1851 return new RemotePlainGdbAdapter(this);
1852 case AttachExternal:
1853 return new AttachGdbAdapter(this);
1856 return new TermGdbAdapter(this);
1857 return new LocalPlainGdbAdapter(this);
1861 void GdbEngine::setupEngine()
1863 QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state());
1864 QTC_ASSERT(m_debuggingHelperState == DebuggingHelperUninitialized, /**/);
1866 if (m_gdbAdapter->dumperHandling() != AbstractGdbAdapter::DumperNotAvailable) {
1867 connect(debuggerCore()->action(UseDebuggingHelpers),
1868 SIGNAL(valueChanged(QVariant)),
1869 SLOT(setUseDebuggingHelpers(QVariant)));
1872 QTC_ASSERT(state() == EngineSetupRequested, /**/);
1873 m_gdbAdapter->startAdapter();
1876 unsigned GdbEngine::debuggerCapabilities() const
1878 unsigned caps = ReverseSteppingCapability
1879 | AutoDerefPointersCapability
1880 | DisassemblerCapability
1881 | RegisterCapability
1882 | ShowMemoryCapability
1883 | JumpToLineCapability
1884 | ReloadModuleCapability
1885 | ReloadModuleSymbolsCapability
1886 | BreakOnThrowAndCatchCapability
1887 | BreakConditionCapability
1888 | TracePointCapability
1889 | ReturnFromFunctionCapability
1890 | CreateFullBacktraceCapability
1891 | WatchpointCapability
1892 | AddWatcherCapability
1893 | ShowModuleSymbolsCapability
1896 if (startParameters().startMode == AttachCore)
1899 return caps | SnapshotCapability;
1902 bool GdbEngine::canWatchWidgets() const
1907 void GdbEngine::continueInferiorInternal()
1909 QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
1910 notifyInferiorRunRequested();
1911 showStatusMessage(tr("Running requested..."), 5000);
1912 QTC_ASSERT(state() == InferiorRunRequested, qDebug() << state());
1913 postCommand("-exec-continue", RunRequest, CB(handleExecuteContinue));
1916 void GdbEngine::doNotifyInferiorRunOk()
1919 notifyInferiorRunOk();
1922 void GdbEngine::autoContinueInferior()
1925 continueInferiorInternal();
1926 showStatusMessage(tr("Continuing after temporary stop..."), 1000);
1929 void GdbEngine::continueInferior()
1931 QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
1933 continueInferiorInternal();
1936 void GdbEngine::executeStep()
1938 QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
1940 notifyInferiorRunRequested();
1941 showStatusMessage(tr("Step requested..."), 5000);
1942 if (m_gdbAdapter->isTrkAdapter() && stackHandler()->stackSize() > 0)
1943 postCommand("sal step,0x" + QByteArray::number(stackHandler()->topAddress(), 16));
1944 if (isReverseDebugging())
1945 postCommand("reverse-step", RunRequest, CB(handleExecuteStep));
1947 postCommand("-exec-step", RunRequest, CB(handleExecuteStep));
1950 void GdbEngine::handleExecuteStep(const GdbResponse &response)
1952 if (response.resultClass == GdbResultDone) {
1953 // Step was finishing too quick, and a '*stopped' messages should
1954 // have preceded it, so just ignore this result.
1955 QTC_ASSERT(state() == InferiorStopOk, /**/);
1958 QTC_ASSERT(state() == InferiorRunRequested, qDebug() << state());
1959 if (response.resultClass == GdbResultRunning) {
1960 doNotifyInferiorRunOk();
1963 QByteArray msg = response.data.findChild("msg").data();
1964 if (msg.startsWith("Cannot find bounds of current function")) {
1965 notifyInferiorRunFailed();
1968 if (!m_commandsToRunOnTemporaryBreak.isEmpty())
1969 flushQueuedCommands();
1970 executeStepI(); // Fall back to instruction-wise stepping.
1972 showExecutionError(QString::fromLocal8Bit(msg));
1973 notifyInferiorIll();
1977 void GdbEngine::executeStepI()
1979 QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
1981 notifyInferiorRunRequested();
1982 showStatusMessage(tr("Step by instruction requested..."), 5000);
1983 if (isReverseDebugging())
1984 postCommand("reverse-stepi", RunRequest, CB(handleExecuteContinue));
1986 postCommand("-exec-step-instruction", RunRequest, CB(handleExecuteContinue));
1989 void GdbEngine::executeStepOut()
1991 QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
1992 postCommand("-stack-select-frame 0");
1994 notifyInferiorRunRequested();
1995 showStatusMessage(tr("Finish function requested..."), 5000);
1996 postCommand("-exec-finish", RunRequest, CB(handleExecuteContinue));
1999 void GdbEngine::executeNext()
2001 QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
2003 notifyInferiorRunRequested();
2004 showStatusMessage(tr("Step next requested..."), 5000);
2005 if (m_gdbAdapter->isTrkAdapter() && stackHandler()->stackSize() > 0)
2006 postCommand("sal next,0x" + QByteArray::number(stackHandler()->topAddress(), 16));
2007 if (isReverseDebugging())
2008 postCommand("reverse-next", RunRequest, CB(handleExecuteNext));
2010 postCommand("-exec-next", RunRequest, CB(handleExecuteNext));
2013 void GdbEngine::handleExecuteNext(const GdbResponse &response)
2015 if (response.resultClass == GdbResultDone) {
2016 // Step was finishing too quick, and a '*stopped' messages should
2017 // have preceded it, so just ignore this result.
2018 QTC_ASSERT(state() == InferiorStopOk, /**/);
2021 QTC_ASSERT(state() == InferiorRunRequested, qDebug() << state());
2022 if (response.resultClass == GdbResultRunning) {
2023 doNotifyInferiorRunOk();
2026 QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
2027 QByteArray msg = response.data.findChild("msg").data();
2028 if (msg.startsWith("Cannot find bounds of current function")) {
2029 if (!m_commandsToRunOnTemporaryBreak.isEmpty())
2030 flushQueuedCommands();
2031 notifyInferiorRunFailed();
2033 executeNextI(); // Fall back to instruction-wise stepping.
2035 showMessageBox(QMessageBox::Critical, tr("Execution Error"),
2036 tr("Cannot continue debugged process:\n") + QString::fromLocal8Bit(msg));
2037 notifyInferiorIll();
2041 void GdbEngine::executeNextI()
2043 QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
2045 notifyInferiorRunRequested();
2046 showStatusMessage(tr("Step next instruction requested..."), 5000);
2047 if (isReverseDebugging())
2048 postCommand("reverse-nexti", RunRequest, CB(handleExecuteContinue));
2050 postCommand("-exec-next-instruction", RunRequest, CB(handleExecuteContinue));
2053 static QByteArray addressSpec(quint64 address)
2055 return "*0x" + QByteArray::number(address, 16);
2058 void GdbEngine::executeRunToLine(const ContextData &data)
2060 QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
2062 notifyInferiorRunRequested();
2063 showStatusMessage(tr("Run to line %1 requested...").arg(data.lineNumber), 5000);
2065 m_targetFrame.file = data.fileName;
2066 m_targetFrame.line = data.lineNumber;
2069 loc = addressSpec(data.address);
2071 loc = '"' + breakLocation(data.fileName).toLocal8Bit() + '"' + ':'
2072 + QByteArray::number(data.lineNumber);
2073 postCommand("tbreak " + loc);
2074 postCommand("continue", RunRequest, CB(handleExecuteRunToLine));
2076 // Seems to jump to unpredicatable places. Observed in the manual
2077 // tests in the Foo::Foo() constructor with both gdb 6.8 and 7.1.
2078 QByteArray args = '"' + breakLocation(fileName).toLocal8Bit() + '"' + ':'
2079 + QByteArray::number(lineNumber);
2080 postCommand("-exec-until " + args, RunRequest, CB(handleExecuteContinue));
2084 void GdbEngine::executeRunToFunction(const QString &functionName)
2086 QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
2088 postCommand("-break-insert -t " + functionName.toLatin1());
2089 showStatusMessage(tr("Run to function %1 requested...").arg(functionName), 5000);
2090 continueInferiorInternal();
2093 void GdbEngine::executeJumpToLine(const ContextData &data)
2095 QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
2098 loc = addressSpec(data.address);
2100 loc = '"' + breakLocation(data.fileName).toLocal8Bit() + '"' + ':'
2101 + QByteArray::number(data.lineNumber);
2102 postCommand("tbreak " + loc);
2103 notifyInferiorRunRequested();
2104 postCommand("jump " + loc, RunRequest, CB(handleExecuteJumpToLine));
2105 // will produce something like
2106 // &"jump \"/home/apoenitz/dev/work/test1/test1.cpp\":242"
2107 // ~"Continuing at 0x4058f3."
2108 // ~"run1 (argc=1, argv=0x7fffbf1f5538) at test1.cpp:242"
2113 void GdbEngine::executeReturn()
2115 QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
2117 notifyInferiorRunRequested();
2118 showStatusMessage(tr("Immediate return from function requested..."), 5000);
2119 postCommand("-exec-finish", RunRequest, CB(handleExecuteReturn));
2122 void GdbEngine::handleExecuteReturn(const GdbResponse &response)
2124 if (response.resultClass == GdbResultDone) {
2125 notifyInferiorStopOk();
2129 notifyInferiorRunFailed();
2133 \fn void Debugger::Internal::GdbEngine::setTokenBarrier()
2134 \brief Discard the results of all pending watch-updating commands.
2136 This method is called at the beginning of all step/next/finish etc.
2138 If non-watch-updating commands with call-backs are still in the pipe,
2142 void GdbEngine::setTokenBarrier()
2144 foreach (const GdbCommand &cookie, m_cookieForToken) {
2145 QTC_ASSERT(!cookie.callback || (cookie.flags & Discardable),
2146 qDebug() << "CMD:" << cookie.command
2147 << " FLAGS:" << cookie.flags
2148 << " CALLBACK:" << cookie.callbackName;
2152 PENDING_DEBUG("\n--- token barrier ---\n");
2153 showMessage(_("--- token barrier ---"), LogMiscInput);
2154 if (debuggerCore()->boolSetting(LogTimeStamps))
2155 showMessage(LogWindow::logTimeStamp(), LogMiscInput);
2156 m_oldestAcceptableToken = currentToken();
2157 m_stackNeeded = false;
2161 //////////////////////////////////////////////////////////////////////
2163 // Breakpoint specific stuff
2165 //////////////////////////////////////////////////////////////////////
2167 void GdbEngine::updateBreakpointDataFromOutput(BreakpointId id, const GdbMi &bkpt)
2169 QTC_ASSERT(bkpt.isValid(), return);
2171 BreakpointResponse response = breakHandler()->response(id);
2173 response.multiple = false;
2174 response.enabled = true;
2175 response.pending = false;
2176 response.condition.clear();
2177 QByteArray file, fullName;
2178 foreach (const GdbMi &child, bkpt.children()) {
2179 if (child.hasName("number")) {
2180 response.number = child.data().toInt();
2181 } else if (child.hasName("func")) {
2182 response.functionName = _(child.data());
2183 } else if (child.hasName("addr")) {
2184 // <MULTIPLE> happens in constructors, inline functions, and
2185 // at other places like 'foreach' lines. In this case there are
2186 // fields named "addr" in the response and/or the address
2187 // is called <MULTIPLE>.
2188 //qDebug() << "ADDR: " << child.data() << (child.data() == "<MULTIPLE>");
2189 if (child.data().startsWith("0x")) {
2190 response.address = child.data().mid(2).toULongLong(0, 16);
2192 response.extra = child.data();
2193 if (child.data() == "<MULTIPLE>")
2194 response.multiple = true;
2196 } else if (child.hasName("file")) {
2197 file = child.data();
2198 } else if (child.hasName("fullname")) {
2199 fullName = child.data();
2200 } else if (child.hasName("line")) {
2201 // The line numbers here are the uncorrected ones. So don't
2202 // change it if we know better already.
2203 if (response.correctedLineNumber == 0)
2204 response.lineNumber = child.data().toInt();
2205 } else if (child.hasName("cond")) {
2206 // gdb 6.3 likes to "rewrite" conditions. Just accept that fact.
2207 response.condition = child.data();
2208 } else if (child.hasName("enabled")) {
2209 response.enabled = (child.data() == "y");
2210 } else if (child.hasName("pending")) {
2211 // Any content here would be interesting only if we did accept
2212 // spontaneously appearing breakpoints (user using gdb commands).
2213 response.pending = true;
2214 } else if (child.hasName("at")) {
2215 // Happens with gdb 6.4 symbianelf.
2216 QByteArray ba = child.data();
2217 if (ba.startsWith('<') && ba.endsWith('>'))
2218 ba = ba.mid(1, ba.size() - 2);
2219 response.functionName = _(ba);
2220 } else if (child.hasName("thread")) {
2221 response.threadSpec = child.data().toInt();
2222 } else if (child.hasName("type")) {
2223 // "breakpoint", "hw breakpoint", "tracepoint"
2224 if (child.data().contains("tracepoint"))
2225 response.tracepoint = true;
2226 else if (!child.data().contains("reakpoint"))
2227 response.type = Watchpoint;
2229 // This field is not present. Contents needs to be parsed from
2230 // the plain "ignore" response.
2231 //else if (child.hasName("ignore"))
2232 // response.ignoreCount = child.data();
2236 if (!fullName.isEmpty()) {
2237 name = cleanupFullName(QFile::decodeName(fullName));
2238 response.fileName = name;
2239 //if (data->markerFileName().isEmpty())
2240 // data->setMarkerFileName(name);
2242 name = QFile::decodeName(file);
2243 // Use fullName() once we have a mapping which is more complete than
2244 // gdb's own. No point in assigning markerFileName for now.
2246 if (!name.isEmpty())
2247 response.fileName = name;
2249 breakHandler()->setResponse(id, response);
2252 QString GdbEngine::breakLocation(const QString &file) const
2254 //QTC_ASSERT(!m_breakListOutdated, /* */)
2255 QString where = m_fullToShortName.value(file);
2256 if (where.isEmpty())
2257 return QFileInfo(file).fileName();
2261 QByteArray GdbEngine::breakpointLocation(BreakpointId id)
2263 BreakHandler *handler = breakHandler();
2264 const BreakpointParameters &data = handler->breakpointData(id);
2265 QTC_ASSERT(data.type != UnknownType, return QByteArray());
2266 // FIXME: Non-GCC-runtime
2267 if (data.type == BreakpointAtThrow)
2268 return "__cxa_throw";
2269 if (data.type == BreakpointAtCatch)
2270 return "__cxa_begin_catch";
2271 if (data.type == BreakpointAtMain)
2273 // FIXME: Should be target specific.
2278 if (data.type == BreakpointByFunction)
2279 return data.functionName.toUtf8();
2280 if (data.type == BreakpointByAddress)
2281 return addressSpec(data.address);
2283 const QString fileName = data.pathUsage == BreakpointUseFullPath
2284 ? data.fileName : breakLocation(data.fileName);
2285 // The argument is simply a C-quoted version of the argument to the
2286 // non-MI "break" command, including the "original" quoting it wants.
2287 return "\"\\\"" + GdbMi::escapeCString(fileName).toLocal8Bit() + "\\\":"
2288 + QByteArray::number(data.lineNumber) + '"';
2291 QByteArray GdbEngine::breakpointLocation2(BreakpointId id)
2293 BreakHandler *handler = breakHandler();
2294 const BreakpointParameters &data = handler->breakpointData(id);
2295 const QString fileName = data.pathUsage == BreakpointUseFullPath
2296 ? data.fileName : breakLocation(data.fileName);
2297 return GdbMi::escapeCString(fileName).toLocal8Bit() + ':'
2298 + QByteArray::number(data.lineNumber);
2301 void GdbEngine::handleWatchInsert(const GdbResponse &response)
2303 const int id = response.cookie.toInt();
2304 if (response.resultClass == GdbResultDone) {
2305 // "Hardware watchpoint 2: *0xbfffed40\n"
2306 QByteArray ba = response.data.findChild("consolestreamoutput").data();
2307 if (ba.startsWith("Hardware watchpoint ")
2308 || ba.startsWith("Watchpoint ")) {
2309 const int end = ba.indexOf(':');
2310 const int begin = ba.lastIndexOf(' ', end) + 1;
2311 const QByteArray address = ba.mid(end + 3).trimmed();
2312 BreakHandler *handler = breakHandler();
2313 BreakpointResponse response = handler->response(id);
2314 response.number = ba.mid(begin, end - begin).toInt();
2315 response.address = address.toULongLong(0, 0);
2316 handler->setResponse(id, response);
2317 QTC_ASSERT(!handler->needsChange(id), /**/);
2318 handler->notifyBreakpointInsertOk(id);
2320 showMessage(_("CANNOT PARSE WATCHPOINT FROM " + ba));
2325 void GdbEngine::attemptAdjustBreakpointLocation(BreakpointId id)
2327 if (!debuggerCore()->boolSetting(AdjustBreakpointLocations))
2329 BreakpointResponse response = breakHandler()->response(id);
2330 if (response.address == 0 || response.correctedLineNumber != 0)
2332 // Prevent endless loop.
2333 response.correctedLineNumber = -1;
2334 breakHandler()->setResponse(id, response);
2335 postCommand("info line *0x" + QByteArray::number(response.address, 16),
2336 NeedsStop | RebuildBreakpointModel,
2337 CB(handleInfoLine), id);
2340 void GdbEngine::handleCatchInsert(const GdbResponse &response)
2342 BreakHandler *handler = breakHandler();
2343 BreakpointId id(response.cookie.toInt());
2344 if (response.resultClass == GdbResultDone) {
2345 handler->notifyBreakpointInsertOk(id);
2346 attemptAdjustBreakpointLocation(id);
2350 void GdbEngine::handleBreakInsert1(const GdbResponse &response)
2352 BreakHandler *handler = breakHandler();
2353 BreakpointId id(response.cookie.toInt());
2354 if (response.resultClass == GdbResultDone) {
2355 // Interesting only on Mac?
2356 GdbMi bkpt = response.data.findChild("bkpt");
2357 updateBreakpointDataFromOutput(id, bkpt);
2358 if (handler->needsChange(id)) {
2359 handler->notifyBreakpointChangeAfterInsertNeeded(id);
2360 changeBreakpoint(id);
2362 handler->notifyBreakpointInsertOk(id);
2363 attemptAdjustBreakpointLocation(id);
2365 } else if (response.data.findChild("msg").data().contains("Unknown option")) {
2366 // Older version of gdb don't know the -a option to set tracepoints
2367 // ^error,msg="mi_cmd_break_insert: Unknown option ``a''"
2368 const QString fileName = handler->fileName(id);
2369 const int lineNumber = handler->lineNumber(id);
2370 QByteArray cmd = "trace "
2371 "\"" + GdbMi::escapeCString(fileName).toLocal8Bit() + "\":"
2372 + QByteArray::number(lineNumber);
2373 postCommand(cmd, NeedsStop | RebuildBreakpointModel,
2374 CB(handleTraceInsert2), id);
2376 // Some versions of gdb like "GNU gdb (GDB) SUSE (6.8.91.20090930-2.4)"
2377 // know how to do pending breakpoints using CLI but not MI. So try
2379 QByteArray cmd = "break " + breakpointLocation2(id);
2380 postCommand(cmd, NeedsStop | RebuildBreakpointModel,
2381 CB(handleBreakInsert2), id);
2385 void GdbEngine::handleBreakInsert2(const GdbResponse &response)
2387 if (response.resultClass == GdbResultDone) {
2388 BreakpointId id(response.cookie.toInt());
2389 attemptAdjustBreakpointLocation(id);
2390 breakHandler()->notifyBreakpointInsertOk(id);
2392 // Note: gdb < 60800 doesn't "do" pending breakpoints.
2393 // Not much we can do about it except implementing the
2394 // logic on top of shared library events, and that's not
2395 // worth the effort.
2399 void GdbEngine::handleTraceInsert2(const GdbResponse &response)
2401 if (response.resultClass == GdbResultDone)
2402 reloadBreakListInternal();
2405 void GdbEngine::reloadBreakListInternal()
2407 postCommand("-break-list",
2408 NeedsStop | RebuildBreakpointModel,
2409 CB(handleBreakList));
2412 void GdbEngine::handleBreakList(const GdbResponse &response)
2414 // 45^done,BreakpointTable={nr_rows="2",nr_cols="6",hdr=[
2415 // {width="3",alignment="-1",col_name="number",colhdr="Num"}, ...
2416 // body=[bkpt={number="1",type="breakpoint",disp="keep",enabled="y",
2417 // addr="0x000000000040109e",func="main",file="app.cpp",
2418 // fullname="/home/apoenitz/dev/work/plugintest/app/app.cpp",
2419 // line="11",times="1"},
2420 // bkpt={number="2",type="breakpoint",disp="keep",enabled="y",
2421 // addr="<PENDING>",pending="plugin.cpp:7",times="0"}] ... }
2423 if (response.resultClass == GdbResultDone) {
2424 GdbMi table = response.data.findChild("BreakpointTable");
2425 if (table.isValid())
2426 handleBreakList(table);
2430 void GdbEngine::handleBreakList(const GdbMi &table)
2432 const GdbMi body = table.findChild("body");
2434 if (body.isValid()) {
2436 bkpts = body.children();
2439 bkpts = table.children();
2440 // Remove the 'hdr' and artificial items.
2441 for (int i = bkpts.size(); --i >= 0; ) {
2442 int num = bkpts.at(i).findChild("number").data().toInt();
2448 foreach (const GdbMi &bkpt, bkpts) {
2449 BreakpointResponse needle;
2450 needle.number = bkpt.findChild("number").data().toInt();
2451 if (isQmlStepBreakpoint2(needle.number))
2453 if (isQFatalBreakpoint(needle.number))
2455 BreakpointId id = breakHandler()->findSimilarBreakpoint(needle);
2456 if (id != BreakpointId(-1)) {
2457 updateBreakpointDataFromOutput(id, bkpt);
2458 BreakpointResponse response = breakHandler()->response(id);
2459 if (response.correctedLineNumber == 0)
2460 attemptAdjustBreakpointLocation(id);
2461 if (response.multiple && response.addresses.isEmpty())
2462 postCommand("info break " + QByteArray::number(response.number),
2463 NeedsStop, CB(handleBreakListMultiple), QVariant(id));
2465 qDebug() << " NOTHING SUITABLE FOUND";
2466 showMessage(_("CANNOT FIND BP: " + bkpt.toString()));
2470 m_breakListOutdated = false;
2473 void GdbEngine::handleBreakListMultiple(const GdbResponse &response)
2475 QTC_ASSERT(response.resultClass == GdbResultDone, /**/)
2476 const BreakpointId id = response.cookie.toInt();
2477 const QString str = QString::fromLocal8Bit(
2478 response.data.findChild("consolestreamoutput").data());
2479 extractDataFromInfoBreak(str, id);
2482 void GdbEngine::handleBreakDisable(const GdbResponse &response)
2484 QTC_ASSERT(response.resultClass == GdbResultDone, /**/)
2485 const BreakpointId id = response.cookie.toInt();
2486 BreakHandler *handler = breakHandler();
2487 // This should only be the requested state.
2488 QTC_ASSERT(!handler->isEnabled(id), /* Prevent later recursion */);
2489 BreakpointResponse br = handler->response(id);
2491 handler->setResponse(id, br);
2492 changeBreakpoint(id); // Maybe there's more to do.
2495 void GdbEngine::handleBreakEnable(const GdbResponse &response)
2497 QTC_ASSERT(response.resultClass == GdbResultDone, /**/)
2498 const BreakpointId id = response.cookie.toInt();
2499 BreakHandler *handler = breakHandler();
2500 // This should only be the requested state.
2501 QTC_ASSERT(handler->isEnabled(id), /* Prevent later recursion */);
2502 BreakpointResponse br = handler->response(id);
2504 handler->setResponse(id, br);
2505 changeBreakpoint(id); // Maybe there's more to do.
2508 void GdbEngine::handleBreakThreadSpec(const GdbResponse &response)
2510 QTC_ASSERT(response.resultClass == GdbResultDone, /**/)
2511 const BreakpointId id = response.cookie.toInt();
2512 BreakHandler *handler = breakHandler();
2513 BreakpointResponse br = handler->response(id);
2514 br.threadSpec = handler->threadSpec(id);
2515 handler->setResponse(id, br);
2516 handler->notifyBreakpointNeedsReinsertion(id);
2517 insertBreakpoint(id);
2520 void GdbEngine::handleBreakIgnore(const GdbResponse &response)
2524 // ~"Will stop next time breakpoint 2 is reached.\n"
2528 // ~"Will ignore next 12 crossings of breakpoint 2.\n"
2531 // gdb 6.3 does not produce any console output
2532 QTC_ASSERT(response.resultClass == GdbResultDone, /**/)
2533 QString msg = _(response.data.findChild("consolestreamoutput").data());
2534 BreakpointId id = response.cookie.toInt();
2535 BreakHandler *handler = breakHandler();
2536 BreakpointResponse br = handler->response(id);
2537 //if (msg.contains(__("Will stop next time breakpoint")))
2538 // response.ignoreCount = _("0");
2539 //else if (msg.contains(__("Will ignore next")))
2540 // response.ignoreCount = data->ignoreCount;
2541 // FIXME: this assumes it is doing the right thing...
2542 const BreakpointParameters ¶meters = handler->breakpointData(id);
2543 br.ignoreCount = parameters.ignoreCount;
2544 br.command = parameters.command;
2545 handler->setResponse(id, br);
2546 changeBreakpoint(id); // Maybe there's more to do.
2549 void GdbEngine::handleBreakCondition(const GdbResponse &response)
2551 // Can happen at invalid condition strings.
2552 //QTC_ASSERT(response.resultClass == GdbResultDone, /**/)
2553 const BreakpointId id = response.cookie.toInt();
2554 BreakHandler *handler = breakHandler();
2555 // We just assume it was successful. Otherwise we had to parse
2556 // the output stream data.
2557 // The following happens on Mac:
2558 // QByteArray msg = response.data.findChild("msg").data();
2559 // if (1 || msg.startsWith("Error parsing breakpoint condition. "
2560 // " Will try again when we hit the breakpoint.")) {
2561 BreakpointResponse br = handler->response(id);
2562 br.condition = handler->condition(id);
2563 handler->setResponse(id, br);
2564 changeBreakpoint(id); // Maybe there's more to do.
2567 void GdbEngine::extractDataFromInfoBreak(const QString &output, BreakpointId id)
2569 //qDebug() << output;
2570 if (output.isEmpty())
2572 // "Num Type Disp Enb Address What
2573 // 4 breakpoint keep y <MULTIPLE> 0x00000000004066ad
2574 // 4.1 y 0x00000000004066ad in CTorTester
2575 // at /data5/dev/ide/main/tests/manual/gdbdebugger/simple/app.cpp:124
2577 // everything on a single line on Windows for constructors of classes
2578 // within namespaces.
2579 // Sometimes the path is relative too.
2581 // 2 breakpoint keep y <MULTIPLE> 0x0040168e
2582 // 2.1 y 0x0040168e in MainWindow::MainWindow(QWidget*) at mainwindow.cpp:7
2583 // 2.2 y 0x00401792 in MainWindow::MainWindow(QWidget*) at mainwindow.cpp:7
2585 // tested in ../../../tests/auto/debugger/
2586 QRegExp re(_("MULTIPLE.*(0x[0-9a-f]+) in (.*)\\s+at (.*):([\\d]+)([^\\d]|$)"));
2587 re.setMinimal(true);
2589 BreakpointResponse response;
2590 response.fileName = _("<MULTIPLE>");
2592 QString requestedFileName = breakHandler()->fileName(id);
2594 if (re.indexIn(output) != -1) {
2595 response.address = re.cap(1).toULongLong(0, 16);
2596 response.functionName = re.cap(2).trimmed();
2597 response.lineNumber = re.cap(4).toInt();
2598 QString full = fullName(re.cap(3));
2599 if (full.isEmpty()) {
2600 // FIXME: This happens without UsePreciseBreakpoints regularly.
2601 // We need to revive that "fill full name mapping bit by bit"
2602 // approach of 1.2.x
2603 //qDebug() << "NO FULL NAME KNOWN FOR" << re.cap(3);
2604 full = cleanupFullName(re.cap(3));
2605 if (full.isEmpty()) {
2606 qDebug() << "FILE IS NOT RESOLVABLE" << re.cap(3);
2607 full = re.cap(3); // FIXME: wrong, but prevents recursion
2610 // The variable full could still contain, say "foo.cpp" when we asked
2611 // for "/full/path/to/foo.cpp". In this case, using the requested
2612 // instead of the acknowledged name makes sense as it will allow setting
2613 // the marker in more cases.
2614 if (requestedFileName.endsWith(full))
2615 full = requestedFileName;
2616 response.fileName = full;
2618 qDebug() << "COULD NOT MATCH " << re.pattern() << " AND " << output;
2619 response.number = -1; // <unavailable>
2621 breakHandler()->setResponse(id, response);
2624 void GdbEngine::handleBreakInfo(const GdbResponse &response)
2626 if (response.resultClass == GdbResultDone) {
2627 // Old-style output for multiple breakpoints, presumably in a
2629 const BreakpointId id(response.cookie.toInt());
2630 const QString str = QString::fromLocal8Bit(
2631 response.data.findChild("consolestreamoutput").data());
2632 extractDataFromInfoBreak(str, id);
2636 void GdbEngine::handleInfoLine(const GdbResponse &response)
2638 if (response.resultClass == GdbResultDone) {
2639 // Old-style output: "Line 1102 of \"simple/app.cpp\" starts
2640 // at address 0x80526aa <_Z10...+131> and ends at 0x80526b5
2641 // <_Z10testQStackv+142>.\n"
2642 QByteArray ba = response.data.findChild("consolestreamoutput").data();
2643 const BreakpointId id = response.cookie.toInt();
2644 const int pos = ba.indexOf(' ', 5);
2645 if (ba.startsWith("Line ") && pos != -1) {
2646 const int line = ba.mid(5, pos - 5).toInt();
2647 BreakpointResponse br = breakHandler()->response(id);
2648 br.lineNumber = line;
2649 br.correctedLineNumber = line;
2650 breakHandler()->setResponse(id, br);
2655 bool GdbEngine::stateAcceptsBreakpointChanges() const
2658 case InferiorSetupRequested:
2659 case InferiorRunRequested:
2661 case InferiorStopRequested:
2662 case InferiorStopOk:
2669 bool GdbEngine::acceptsBreakpoint(BreakpointId id) const
2671 return DebuggerEngine::isCppBreakpoint(breakHandler()->breakpointData(id));
2674 void GdbEngine::insertBreakpoint(BreakpointId id)
2676 // Set up fallback in case of pending breakpoints which aren't handled
2677 // by the MI interface.
2678 BreakHandler *handler = breakHandler();
2679 QTC_ASSERT(handler->state(id) == BreakpointInsertRequested, /**/);
2680 handler->notifyBreakpointInsertProceeding(id);
2681 BreakpointType type = handler->type(id);
2682 if (type == Watchpoint) {
2683 postCommand("watch " + addressSpec(handler->address(id)),
2684 NeedsStop | RebuildBreakpointModel,
2685 CB(handleWatchInsert), id);
2688 if (type == BreakpointAtFork) {
2689 postCommand("catch fork", NeedsStop | RebuildBreakpointModel,
2690 CB(handleCatchInsert), id);
2691 postCommand("catch vfork", NeedsStop | RebuildBreakpointModel,
2692 CB(handleCatchInsert), id);
2695 //if (type == BreakpointAtVFork) {
2696 // postCommand("catch vfork", NeedsStop | RebuildBreakpointModel,
2697 // CB(handleCatchInsert), id);
2700 if (type == BreakpointAtExec) {
2701 postCommand("catch exec", NeedsStop | RebuildBreakpointModel,
2702 CB(handleCatchInsert), id);
2705 if (type == BreakpointAtSysCall) {
2706 postCommand("catch syscall", NeedsStop | RebuildBreakpointModel,
2707 CB(handleCatchInsert), id);
2711 QByteArray cmd = "xxx";
2712 if (handler->isTracepoint(id)) {
2713 cmd = "-break-insert -a -f ";
2714 } else if (m_isMacGdb) {
2715 cmd = "-break-insert -l -1 -f ";
2716 } else if (m_gdbAdapter->isTrkAdapter()) {
2717 cmd = "-break-insert -h -f ";
2718 } else if (m_gdbVersion >= 70000) {
2719 int spec = handler->threadSpec(id);
2720 cmd = "-break-insert ";
2722 cmd += "-p " + QByteArray::number(spec);
2724 } else if (m_gdbVersion >= 60800) {
2725 // Probably some earlier version would work as well.
2726 cmd = "-break-insert -f ";
2728 cmd = "-break-insert ";
2730 //if (!data->condition.isEmpty())
2731 // cmd += "-c " + data->condition + ' ';
2732 cmd += breakpointLocation(id);
2733 postCommand(cmd, NeedsStop | RebuildBreakpointModel,
2734 CB(handleBreakInsert1), id);
2737 void GdbEngine::changeBreakpoint(BreakpointId id)
2739 BreakHandler *handler = breakHandler();
2740 const BreakpointParameters &data = handler->breakpointData(id);
2741 QTC_ASSERT(data.type != UnknownType, return);
2742 const BreakpointResponse &response = handler->response(id);
2743 QTC_ASSERT(response.number > 0, return);
2744 const QByteArray bpnr = QByteArray::number(response.number);
2745 QTC_ASSERT(response.number > 0, return);
2746 const BreakpointState state = handler->state(id);
2747 if (state == BreakpointChangeRequested)
2748 handler->notifyBreakpointChangeProceeding(id);
2749 const BreakpointState state2 = handler->state(id);
2750 QTC_ASSERT(state2 == BreakpointChangeProceeding, qDebug() << state2);
2752 if (data.threadSpec != response.threadSpec) {
2753 // The only way to change this seems to be to re-set the bp completely.
2754 postCommand("-break-delete " + bpnr,
2755 NeedsStop | RebuildBreakpointModel,
2756 CB(handleBreakThreadSpec), id);
2759 if (data.command != response.command) {
2760 QByteArray breakCommand = "-break-commands " + bpnr;
2761 foreach (const QString &command, data.command.split(QLatin1String("\\n"))) {
2762 if (!command.isEmpty()) {
2763 breakCommand.append(" \"");
2764 breakCommand.append(command.toLatin1());
2765 breakCommand.append('"');
2768 postCommand(breakCommand, NeedsStop | RebuildBreakpointModel,
2769 CB(handleBreakIgnore), id);
2772 if (!data.conditionsMatch(response.condition)) {
2773 postCommand("condition " + bpnr + ' ' + data.condition,
2774 NeedsStop | RebuildBreakpointModel,
2775 CB(handleBreakCondition), id);
2778 if (data.ignoreCount != response.ignoreCount) {
2779 postCommand("ignore " + bpnr + ' ' + QByteArray::number(data.ignoreCount),
2780 NeedsStop | RebuildBreakpointModel,
2781 CB(handleBreakIgnore), id);
2784 if (!data.enabled && response.enabled) {
2785 postCommand("-break-disable " + bpnr,
2786 NeedsStop | RebuildBreakpointModel,
2787 CB(handleBreakDisable), id);
2790 if (data.enabled && !response.enabled) {
2791 postCommand("-break-enable " + bpnr,
2792 NeedsStop | RebuildBreakpointModel,
2793 CB(handleBreakEnable), id);
2796 handler->notifyBreakpointChangeOk(id);
2797 attemptAdjustBreakpointLocation(id);
2800 void GdbEngine::removeBreakpoint(BreakpointId id)
2802 BreakHandler *handler = breakHandler();
2803 QTC_ASSERT(handler->state(id) == BreakpointRemoveRequested, /**/);
2804 handler->notifyBreakpointRemoveProceeding(id);
2805 BreakpointResponse br = handler->response(id);
2806 showMessage(_("DELETING BP %1 IN %2").arg(br.number)
2807 .arg(handler->fileName(id)));
2808 postCommand("-break-delete " + QByteArray::number(br.number),
2809 NeedsStop | RebuildBreakpointModel);
2810 // Pretend it succeeds without waiting for response. Feels better.
2812 handler->notifyBreakpointRemoveOk(id);
2816 //////////////////////////////////////////////////////////////////////
2818 // Modules specific stuff
2820 //////////////////////////////////////////////////////////////////////
2822 void GdbEngine::loadSymbols(const QString &moduleName)
2824 // FIXME: gdb does not understand quoted names here (tested with 6.8)
2825 postCommand("sharedlibrary " + dotEscape(moduleName.toLocal8Bit()));
2826 reloadModulesInternal();
2827 reloadBreakListInternal();
2832 void GdbEngine::loadAllSymbols()
2834 postCommand("sharedlibrary .*");
2835 reloadModulesInternal();
2836 reloadBreakListInternal();
2841 void GdbEngine::loadSymbolsForStack()
2843 bool needUpdate = false;
2844 const Modules &modules = modulesHandler()->modules();
2845 foreach (const StackFrame &frame, stackHandler()->frames()) {
2846 if (frame.function == _("??")) {
2847 //qDebug() << "LOAD FOR " << frame.address;
2848 foreach (const Module &module, modules) {
2849 if (module.startAddress <= frame.address
2850 && frame.address < module.endAddress) {
2851 postCommand("sharedlibrary "
2852 + dotEscape(module.moduleName.toLocal8Bit()));
2859 reloadModulesInternal();
2860 reloadBreakListInternal();
2866 void GdbEngine::requestModuleSymbols(const QString &moduleName)
2868 QTemporaryFile tf(QDir::tempPath() + _("/gdbsymbols"));
2871 QString fileName = tf.fileName();
2873 postCommand("maint print msymbols " + fileName.toLocal8Bit()
2874 + ' ' + moduleName.toLocal8Bit(),
2875 NeedsStop, CB(handleShowModuleSymbols),
2876 QVariant(moduleName + QLatin1Char('@') + fileName));
2879 void GdbEngine::handleShowModuleSymbols(const GdbResponse &response)
2881 const QString cookie = response.cookie.toString();
2882 const QString moduleName = cookie.section(QLatin1Char('@'), 0, 0);
2883 const QString fileName = cookie.section(QLatin1Char('@'), 1, 1);
2884 if (response.resultClass == GdbResultDone) {
2886 QFile file(fileName);
2887 file.open(QIODevice::ReadOnly);
2888 // Object file /opt/dev/qt/lib/libQtNetworkMyns.so.4:
2889 // [ 0] A 0x16bd64 _DYNAMIC moc_qudpsocket.cpp
2890 // [12] S 0xe94680 _ZN4myns5QFileC1Ev section .plt myns::QFile::QFile()
2891 foreach (const QByteArray &line, file.readAll().split('\n')) {
2894 if (line.at(0) != '[')
2896 int posCode = line.indexOf(']') + 2;
2897 int posAddress = line.indexOf("0x", posCode);
2898 if (posAddress == -1)
2900 int posName = line.indexOf(" ", posAddress);
2901 int lenAddress = posName - posAddress - 1;
2902 int posSection = line.indexOf(" section ");
2905 int posDemangled = 0;
2906 if (posSection == -1) {
2907 lenName = line.size() - posName;
2908 posDemangled = posName;
2910 lenName = posSection - posName;
2912 posDemangled = line.indexOf(' ', posSection + 1);
2913 if (posDemangled == -1) {
2914 lenSection = line.size() - posSection;
2916 lenSection = posDemangled - posSection;
2920 int lenDemangled = 0;
2921 if (posDemangled != -1)
2922 lenDemangled = line.size() - posDemangled;
2924 symbol.state = _(line.mid(posCode, 1));
2925 symbol.address = _(line.mid(posAddress, lenAddress));
2926 symbol.name = _(line.mid(posName, lenName));
2927 symbol.section = _(line.mid(posSection, lenSection));
2928 symbol.demangled = _(line.mid(posDemangled, lenDemangled));
2929 rc.push_back(symbol);
2933 debuggerCore()->showModuleSymbols(moduleName, rc);
2935 showMessageBox(QMessageBox::Critical, tr("Cannot Read Symbols"),
2936 tr("Cannot read symbols for module \"%1\".").arg(fileName));
2940 void GdbEngine::reloadModules()
2942 if (state() == InferiorRunOk || state() == InferiorStopOk)
2943 reloadModulesInternal();
2946 void GdbEngine::reloadModulesInternal()
2948 m_modulesListOutdated = false;
2949 postCommand("info shared", NeedsStop, CB(handleModulesList));
2952 void GdbEngine::handleModulesList(const GdbResponse &response)
2955 if (response.resultClass == GdbResultDone) {
2956 // That's console-based output, likely Linux or Windows,
2957 // but we can avoid the #ifdef here.
2958 QString data = QString::fromLocal8Bit(
2959 response.data.findChild("consolestreamoutput").data());
2960 QTextStream ts(&data, QIODevice::ReadOnly);
2961 while (!ts.atEnd()) {
2962 QString line = ts.readLine();
2964 QString symbolsRead;
2965 QTextStream ts(&line, QIODevice::ReadOnly);
2966 if (line.startsWith(__("0x"))) {
2967 ts >> module.startAddress >> module.endAddress >> symbolsRead;
2968 module.moduleName = ts.readLine().trimmed();
2969 module.symbolsRead =
2970 (symbolsRead == __("Yes") ? Module::ReadOk : Module::ReadFailed);
2971 modules.append(module);
2972 } else if (line.trimmed().startsWith(__("No"))) {
2973 // gdb 6.4 symbianelf
2975 QTC_ASSERT(symbolsRead == __("No"), continue);
2976 module.startAddress = 0;
2977 module.endAddress = 0;
2978 module.moduleName = ts.readLine().trimmed();
2979 modules.append(module);
2982 if (modules.isEmpty()) {
2983 // Mac has^done,shlib-info={num="1",name="dyld",kind="-",
2984 // dyld-addr="0x8fe00000",reason="dyld",requested-state="Y",
2985 // state="Y",path="/usr/lib/dyld",description="/usr/lib/dyld",
2986 // loaded_addr="0x8fe00000",slide="0x0",prefix="__dyld_"},
2987 // shlib-info={...}...
2988 foreach (const GdbMi &item, response.data.children()) {
2991 QString::fromLocal8Bit(item.findChild("path").data());
2992 module.symbolsRead = (item.findChild("state").data() == "Y")
2993 ? Module::ReadOk : Module::ReadFailed;
2994 module.startAddress =
2995 item.findChild("loaded_addr").data().toULongLong(0, 0);
2996 module.endAddress = 0; // FIXME: End address not easily available.
2997 modules.append(module);
3001 modulesHandler()->setModules(modules);
3005 void GdbEngine::examineModules()
3007 foreach (Module module, modulesHandler()->modules()) {
3008 if (module.symbolsType == Module::UnknownType) {
3010 qDebug() << _("objdump -h \"%1\"").arg(module.moduleName);
3011 proc.start(_("objdump -h \"%1\"").arg(module.moduleName));
3012 if (!proc.waitForStarted())
3014 if (!proc.waitForFinished())
3016 QByteArray ba = proc.readAllStandardOutput();
3017 if (ba.contains(".gdb_index"))
3018 module.symbolsType = Module::FastSymbols;
3020 module.symbolsType = Module::PlainSymbols;
3021 modulesHandler()->updateModule(module.moduleName, module);
3026 //////////////////////////////////////////////////////////////////////
3028 // Source files specific stuff
3030 //////////////////////////////////////////////////////////////////////
3032 void GdbEngine::invalidateSourcesList()
3034 m_modulesListOutdated = true;
3035 m_sourcesListOutdated = true;
3036 m_breakListOutdated = true;
3039 void GdbEngine::reloadSourceFiles()
3041 if ((state() == InferiorRunOk || state() == InferiorStopOk)
3042 && !m_sourcesListUpdating)
3043 reloadSourceFilesInternal();
3046 void GdbEngine::reloadSourceFilesInternal()
3048 QTC_ASSERT(!m_sourcesListUpdating, /**/);
3049 m_sourcesListUpdating = true;
3050 postCommand("-file-list-exec-source-files", NeedsStop, CB(handleQuerySources));
3052 if (m_gdbVersion < 70000 && !m_isMacGdb)
3053 postCommand("set stop-on-solib-events 1");
3058 //////////////////////////////////////////////////////////////////////
3060 // Stack specific stuff
3062 //////////////////////////////////////////////////////////////////////
3064 void GdbEngine::selectThread(int index)
3066 threadsHandler()->setCurrentThread(index);
3067 Threads threads = threadsHandler()->threads();
3068 QTC_ASSERT(index < threads.size(), return);
3069 const int id = threads.at(index).id;
3070 showStatusMessage(tr("Retrieving data for stack view thread 0x%1...")
3071 .arg(id, 0, 16), 10000);
3072 postCommand("-thread-select " + QByteArray::number(id), Discardable,
3073 CB(handleStackSelectThread));
3076 void GdbEngine::handleStackSelectThread(const GdbResponse &)
3078 QTC_ASSERT(state() == InferiorUnrunnable || state() == InferiorStopOk, /**/);
3079 showStatusMessage(tr("Retrieving data for stack view..."), 3000);
3080 reloadStack(true); // Will reload registers.
3084 void GdbEngine::reloadFullStack()
3086 PENDING_DEBUG("RELOAD FULL STACK");
3087 postCommand("-stack-list-frames", Discardable, CB(handleStackListFrames),
3088 QVariant::fromValue<StackCookie>(StackCookie(true, true)));
3091 void GdbEngine::reloadStack(bool forceGotoLocation)
3093 PENDING_DEBUG("RELOAD STACK");
3094 QByteArray cmd = "-stack-list-frames";
3095 int stackDepth = debuggerCore()->action(MaximalStackDepth)->value().toInt();
3096 if (stackDepth && !m_gdbAdapter->isTrkAdapter())
3097 cmd += " 0 " + QByteArray::number(stackDepth);
3098 // FIXME: gdb 6.4 symbianelf likes to be asked twice. The first time it
3099 // returns with "^error,msg="Previous frame identical to this frame
3100 // (corrupt stack?)". Might be related to the fact that we can't
3101 // access the memory belonging to the lower frames. But as we know
3102 // this sometimes happens, ask the second time immediately instead
3103 // of waiting for the first request to fail.
3104 // FIXME: Seems to work with 6.8.
3105 if (m_gdbAdapter->isTrkAdapter() && m_gdbVersion < 6.8)
3107 postCommand(cmd, Discardable, CB(handleStackListFrames),
3108 QVariant::fromValue<StackCookie>(StackCookie(false, forceGotoLocation)));
3111 StackFrame GdbEngine::parseStackFrame(const GdbMi &frameMi, int level)
3113 //qDebug() << "HANDLING FRAME:" << frameMi.toString();
3115 frame.level = level;
3116 GdbMi fullName = frameMi.findChild("fullname");
3117 if (fullName.isValid())
3118 frame.file = cleanupFullName(QFile::decodeName(fullName.data()));
3120 frame.file = QFile::decodeName(frameMi.findChild("file").data());
3121 frame.function = _(frameMi.findChild("func").data());
3122 frame.from = _(frameMi.findChild("from").data());
3123 frame.line = frameMi.findChild("line").data().toInt();
3124 frame.address = frameMi.findChild("addr").data().toULongLong(0, 16);
3125 frame.usable = QFileInfo(frame.file).isReadable();
3129 void GdbEngine::handleStackListFrames(const GdbResponse &response)
3131 bool handleIt = (m_isMacGdb || response.resultClass == GdbResultDone);
3133 // That always happens on symbian gdb with
3134 // ^error,data={msg="Previous frame identical to this frame (corrupt stack?)"
3135 // logstreamoutput="Previous frame identical to this frame (corrupt stack?)\n"
3136 //qDebug() << "LISTING STACK FAILED: " << response.toString();
3141 StackCookie cookie = response.cookie.value<StackCookie>();
3142 QList<StackFrame> stackFrames;
3144 GdbMi stack = response.data.findChild("stack");
3145 if (!stack.isValid()) {
3146 qDebug() << "FIXME: stack:" << stack.toString();
3150 int targetFrame = -1;
3152 int n = stack.childCount();
3153 for (int i = 0; i != n; ++i) {
3154 stackFrames.append(parseStackFrame(stack.childAt(i), i));
3155 const StackFrame &frame = stackFrames.back();
3157 # if defined(Q_OS_WIN)
3158 const bool isBogus =
3159 // Assume this is wrong and points to some strange stl_algobase
3160 // implementation. Happens on Karsten's XP system with Gdb 5.50
3161 (frame.file.endsWith(__("/bits/stl_algobase.h")) && frame.line == 150)
3162 // Also wrong. Happens on Vista with Gdb 5.50
3163 || (frame.function == __("operator new") && frame.line == 151);
3165 // Immediately leave bogus frames.
3166 if (targetFrame == -1 && isBogus) {
3168 notifyInferiorRunRequested();
3169 postCommand("-exec-finish", RunRequest, CB(handleExecuteContinue));
3170 showStatusMessage(tr("Jumping out of bogus frame..."), 1000);
3175 // Initialize top frame to the first valid frame.
3176 const bool isValid = frame.isUsable() && !frame.function.isEmpty();
3177 if (isValid && targetFrame == -1)
3181 bool canExpand = !cookie.isFull
3182 && (n >= debuggerCore()->action(MaximalStackDepth)->value().toInt());
3183 debuggerCore()->action(ExpandStack)->setEnabled(canExpand);
3184 stackHandler()->setFrames(stackFrames, canExpand);
3186 // We can't jump to any file if we don't have any frames.
3187 if (stackFrames.isEmpty())
3190 // targetFrame contains the top most frame for which we have source
3191 // information. That's typically the frame we'd like to jump to, with
3192 // a few exceptions:
3194 // Always jump to frame #0 when stepping by instruction.
3195 if (debuggerCore()->boolSetting(OperateByInstruction))
3198 // If there is no frame with source, jump to frame #0.
3199 if (targetFrame == -1)
3202 stackHandler()->setCurrentIndex(targetFrame);
3203 activateFrame(targetFrame);
3206 void GdbEngine::activateFrame(int frameIndex)
3208 if (state() != InferiorStopOk && state() != InferiorUnrunnable)
3211 StackHandler *handler = stackHandler();
3213 if (frameIndex == handler->stackSize()) {
3218 QTC_ASSERT(frameIndex < handler->stackSize(), return);
3220 // Assuming the command always succeeds this saves a roundtrip.
3221 // Otherwise the lines below would need to get triggered
3222 // after a response to this -stack-select-frame here.
3223 handler->setCurrentIndex(frameIndex);
3224 QByteArray cmd = "-stack-select-frame";
3225 //if (!m_currentThread.isEmpty())
3226 // cmd += " --thread " + m_currentThread;
3228 cmd += QByteArray::number(frameIndex);
3229 postCommand(cmd, Discardable, CB(handleStackSelectFrame));
3230 gotoLocation(stackHandler()->currentFrame());
3235 void GdbEngine::handleStackSelectFrame(const GdbResponse &response)
3240 void GdbEngine::handleThreadInfo(const GdbResponse &response)
3242 const int id = response.cookie.toInt();
3243 if (response.resultClass == GdbResultDone) {
3244 int currentThreadId;
3245 const Threads threads =
3246 ThreadsHandler::parseGdbmiThreads(response.data, ¤tThreadId);
3247 threadsHandler()->setThreads(threads);
3248 threadsHandler()->setCurrentThreadId(currentThreadId);
3249 updateViews(); // Adjust Threads combobox.
3250 if (m_hasInferiorThreadList && debuggerCore()->boolSetting(ShowThreadNames)) {
3251 postCommand("threadnames " +
3252 debuggerCore()->action(MaximalStackDepth)->value().toByteArray(),
3253 Discardable, CB(handleThreadNames), id);
3256 // Fall back for older versions: Try to get at least a list
3257 // of running threads.
3258 postCommand("-thread-list-ids", Discardable, CB(handleThreadListIds), id);
3262 void GdbEngine::handleThreadListIds(const GdbResponse &response)
3264 const int id = response.cookie.toInt();
3265 // "72^done,{thread-ids={thread-id="2",thread-id="1"},number-of-threads="2"}
3266 // In gdb 7.1+ additionally: current-thread-id="1"
3267 const QList<GdbMi> items = response.data.findChild("thread-ids").children();
3269 for (int index = 0, n = items.size(); index != n; ++index) {
3271 thread.id = items.at(index).data().toInt();
3272 threads.append(thread);
3274 threadsHandler()->setThreads(threads);
3275 threadsHandler()->setCurrentThreadId(id);
3278 void GdbEngine::handleThreadNames(const GdbResponse &response)
3280 if (response.resultClass == GdbResultDone) {
3281 GdbMi contents = response.data.findChild("consolestreamoutput");
3283 names.fromString(contents.data());
3285 Threads threads = threadsHandler()->threads();
3287 foreach (const GdbMi &name, names.children()) {
3288 int id = name.findChild("id").data().toInt();
3289 for (int index = 0, n = threads.size(); index != n; ++index) {
3290 ThreadData & thread = threads[index];
3291 if (thread.id == (quint64)id) {
3292 thread.name = decodeData(name.findChild("value").data(),
3293 name.findChild("valueencoded").data().toInt());
3298 threadsHandler()->setThreads(threads);
3304 //////////////////////////////////////////////////////////////////////
3306 // Snapshot specific stuff
3308 //////////////////////////////////////////////////////////////////////
3310 void GdbEngine::createSnapshot()
3313 QTemporaryFile tf(QDir::tempPath() + _("/gdbsnapshot"));
3315 fileName = tf.fileName();
3317 postCommand("gcore " + fileName.toLocal8Bit(),
3318 NeedsStop, CB(handleMakeSnapshot), fileName);
3320 showMessageBox(QMessageBox::Critical, tr("Snapshot Creation Error"),
3321 tr("Cannot create snapshot file."));
3325 void GdbEngine::handleMakeSnapshot(const GdbResponse &response)
3327 if (response.resultClass == GdbResultDone) {
3328 DebuggerStartParameters sp = startParameters();
3329 sp.startMode = AttachCore;
3330 sp.coreFile = response.cookie.toString();
3331 //snapshot.setDate(QDateTime::currentDateTime());
3332 StackFrames frames = stackHandler()->frames();
3333 QString function = _("<unknown>");
3334 if (!frames.isEmpty()) {
3335 const StackFrame &frame = frames.at(0);
3336 function = frame.function + _(":") + QString::number(frame.line);
3338 sp.displayName = function + _(": ") + QDateTime::currentDateTime().toString();
3339 sp.isSnapshot = true;
3340 DebuggerRunControl *rc = DebuggerPlugin::createDebugger(sp);
3341 DebuggerPlugin::startDebugger(rc);
3343 QByteArray msg = response.data.findChild("msg").data();
3344 showMessageBox(QMessageBox::Critical, tr("Snapshot Creation Error"),
3345 tr("Cannot create snapshot:\n") + QString::fromLocal8Bit(msg));
3350 //////////////////////////////////////////////////////////////////////
3352 // Register specific stuff
3354 //////////////////////////////////////////////////////////////////////
3356 void GdbEngine::reloadRegisters()
3358 if (!debuggerCore()->isDockVisible(_(Constants::DOCKWIDGET_REGISTER)))
3361 if (state() != InferiorStopOk && state() != InferiorUnrunnable)
3363 if (!m_registerNamesListed) {
3364 postCommand("-data-list-register-names", CB(handleRegisterListNames));
3365 m_registerNamesListed = true;
3366 // FIXME: Maybe better completely re-do this logic in TRK adapter.
3367 if (m_gdbAdapter->isTrkAdapter())
3371 if (m_gdbAdapter->isTrkAdapter()) {
3372 m_gdbAdapter->trkReloadRegisters();
3374 postCommand("-data-list-register-values x",
3375 Discardable, CB(handleRegisterListValues));
3379 void GdbEngine::setRegisterValue(int nr, const QString &value)
3381 Register reg = registerHandler()->registers().at(nr);
3382 //qDebug() << "NOT IMPLEMENTED: CHANGE REGISTER " << nr << reg.name << ":"
3384 postCommand("-var-delete \"R@\"");
3385 postCommand("-var-create \"R@\" * $" + reg.name);
3386 postCommand("-var-assign \"R@\" " + value.toLatin1());
3387 postCommand("-var-delete \"R@\"");
3388 //postCommand("-data-list-register-values d",
3389 // Discardable, CB(handleRegisterListValues));
3393 void GdbEngine::handleRegisterListNames(const GdbResponse &response)
3395 if (response.resultClass != GdbResultDone) {
3396 m_registerNamesListed = false;
3400 Registers registers;
3401 foreach (const GdbMi &item, response.data.findChild("register-names").children())
3402 registers.append(Register(item.data()));
3404 registerHandler()->setRegisters(registers);
3406 if (m_gdbAdapter->isTrkAdapter())
3407 m_gdbAdapter->trkReloadRegisters();
3410 void GdbEngine::handleRegisterListValues(const GdbResponse &response)
3412 if (response.resultClass != GdbResultDone)
3415 Registers registers = registerHandler()->registers();
3417 // 24^done,register-values=[{number="0",value="0xf423f"},...]
3418 const GdbMi values = response.data.findChild("register-values");
3419 foreach (const GdbMi &item, values.children()) {
3420 const int index = item.findChild("number").data().toInt();
3421 if (index < registers.size()) {
3422 Register ® = registers[index];
3423 GdbMi val = item.findChild("value");
3425 bool handled = false;
3426 if (val.data().startsWith('{')) {
3427 int pos1 = val.data().indexOf("v2_int32");
3429 pos1 = val.data().indexOf("v4_int32");
3431 // FIXME: This block wastes cycles.
3432 pos1 = val.data().indexOf('{', pos1 + 1) + 1;
3433 int pos2 = val.data().indexOf('}', pos1);
3434 QByteArray ba2 = val.data().mid(pos1, pos2 - pos1);
3435 foreach (QByteArray ba3, ba2.split(',')) {
3436 ba3 = ba3.trimmed();
3437 QTC_ASSERT(ba3.size() >= 3, continue);
3438 QTC_ASSERT(ba3.size() <= 10, continue);
3439 ba.prepend(QByteArray(10 - ba3.size(), '0'));
3440 ba.prepend(ba3.mid(2));
3446 reg.value = _(handled ? ba : val.data());
3449 registerHandler()->setAndMarkRegisters(registers);
3453 //////////////////////////////////////////////////////////////////////
3455 // Thread specific stuff
3457 //////////////////////////////////////////////////////////////////////
3459 bool GdbEngine::supportsThreads() const
3461 // FSF gdb 6.3 crashes happily on -thread-list-ids. So don't use it.
3462 // The test below is a semi-random pick, 6.8 works fine
3463 return m_isMacGdb || m_gdbVersion > 60500;
3467 //////////////////////////////////////////////////////////////////////
3469 // Tooltip specific stuff
3471 //////////////////////////////////////////////////////////////////////
3473 bool GdbEngine::showToolTip()
3475 if (m_toolTipContext.isNull())
3477 const QString expression = m_toolTipContext->expression;
3478 const QByteArray iname = tooltipIName(m_toolTipContext->expression);
3479 if (DebuggerToolTipManager::debug())
3480 qDebug() << "GdbEngine::showToolTip " << expression << iname << (*m_toolTipContext);
3482 if (!debuggerCore()->boolSetting(UseToolTipsInMainEditor)) {
3483 watchHandler()->removeData(iname);
3487 const QModelIndex index = watchHandler()->itemIndex(iname);
3488 if (!index.isValid()) {
3489 watchHandler()->removeData(iname);
3492 DebuggerTreeViewToolTipWidget *tw = new DebuggerTreeViewToolTipWidget;
3493 tw->setDebuggerModel(TooltipsWatch);
3494 tw->setExpression(expression);
3495 tw->setContext(*m_toolTipContext);
3496 tw->acquireEngine(this);
3497 DebuggerToolTipManager::instance()->showToolTip(m_toolTipContext->mousePosition,
3498 m_toolTipContext->editor, tw);
3502 QString GdbEngine::tooltipExpression() const
3504 return m_toolTipContext.isNull() ? QString() : m_toolTipContext->expression;
3507 void GdbEngine::clearToolTip()
3509 m_toolTipContext.reset();
3512 bool GdbEngine::setToolTipExpression(const QPoint &mousePos,
3513 TextEditor::ITextEditor *editor, const DebuggerToolTipContext &contextIn)
3515 if (state() != InferiorStopOk || !isCppEditor(editor)) {
3516 //qDebug() << "SUPPRESSING DEBUGGER TOOLTIP, INFERIOR NOT STOPPED "
3517 // " OR NOT A CPPEDITOR";
3521 DebuggerToolTipContext context = contextIn;
3523 QString exp = cppExpressionAt(editor, context.position, &line, &column, &context.function);
3524 if (DebuggerToolTipManager::debug())
3525 qDebug() << "GdbEngine::setToolTipExpression1 " << exp << context;
3529 // Extract the first identifier, everything else is considered
3531 int pos1 = 0, pos2 = exp.size();
3533 for (int i = 0; i != exp.size(); ++i) {
3534 const QChar c = exp.at(i);
3535 const bool isIdChar = c.isLetterOrNumber() || c.unicode() == '_';
3536 if (inId && !isIdChar) {
3540 if (!inId && isIdChar) {
3545 exp = exp.mid(pos1, pos2 - pos1);
3547 if (exp.isEmpty() || exp.startsWith(_c('#')) || !hasLetterOrNumber(exp) || isKeyWord(exp))
3550 if (exp.startsWith(_c('"')) && exp.endsWith(_c('"')))
3553 if (exp.startsWith(__("++")) || exp.startsWith(__("--")))
3556 if (exp.endsWith(__("++")) || exp.endsWith(__("--")))
3559 if (exp.startsWith(_c('<')) || exp.startsWith(_c('[')))
3562 if (hasSideEffects(exp) || exp.isEmpty())
3565 if (!m_toolTipContext.isNull() && m_toolTipContext->expression == exp) {
3570 m_toolTipContext.reset(new GdbToolTipContext(context));
3571 m_toolTipContext->mousePosition = mousePos;
3572 m_toolTipContext->expression = exp;
3573 m_toolTipContext->editor = editor;
3574 if (DebuggerToolTipManager::debug())
3575 qDebug() << "GdbEngine::setToolTipExpression2 " << exp << (*m_toolTipContext);
3577 if (isSynchronous()) {
3578 updateLocals(QVariant());
3583 toolTip.exp = exp.toLatin1();
3585 toolTip.iname = tooltipIName(exp);
3586 watchHandler()->removeData(toolTip.iname);
3587 watchHandler()->insertData(toolTip);
3592 //////////////////////////////////////////////////////////////////////
3594 // Watch specific stuff
3596 //////////////////////////////////////////////////////////////////////
3598 void GdbEngine::reloadLocals()
3604 bool GdbEngine::hasDebuggingHelperForType(const QByteArray &type) const
3606 if (!debuggerCore()->boolSetting(UseDebuggingHelpers))
3609 if (m_gdbAdapter->dumperHandling() == AbstractGdbAdapter::DumperNotAvailable) {
3610 // Inferior calls are not possible in gdb when looking at core files.
3611 return type == "QString" || type.endsWith("::QString")
3612 || type == "QStringList" || type.endsWith("::QStringList");
3615 if (m_debuggingHelperState != DebuggingHelperAvailable)
3619 return m_dumperHelper.type(type) != QtDumperHelper::UnknownType;
3623 void GdbEngine::updateWatchData(const WatchData &data, const WatchUpdateFlags &flags)
3625 if (isSynchronous()) {
3626 // This should only be called for fresh expanded items, not for
3627 // items that had their children retrieved earlier.
3628 //qDebug() << "\nUPDATE WATCH DATA: " << data.toString() << "\n";
3630 WatchData data1 = data;
3631 data1.setAllUnneeded();
3635 if (data.iname.endsWith("."))
3638 // Avoid endless loops created by faulty dumpers.
3639 QByteArray processedName = "1-" + data.iname;
3640 //qDebug() << "PROCESSED NAMES: " << processedName << m_processedNames;
3641 if (m_processedNames.contains(processedName)) {
3642 WatchData data1 = data;
3643 showMessage(_("<Breaking endless loop for " + data.iname + '>'),
3645 data1.setAllUnneeded();
3646 data1.setValue(_("<unavailable>"));
3647 data1.setHasChildren(false);
3651 m_processedNames.insert(processedName);
3653 // FIXME: Is this sufficient when "external" changes are
3654 // triggered e.g. by manually entered command in the gdb console?
3655 //qDebug() << "TRY PARTIAL: " << flags.tryIncremental
3657 // << (m_pendingWatchRequests == 0)
3658 // << (m_pendingBreakpointRequests == 0);
3660 bool tryPartial = flags.tryIncremental
3662 && m_pendingWatchRequests == 0
3663 && m_pendingBreakpointRequests == 0;
3666 updateLocalsPython(true, data.iname);
3671 // Bump requests to avoid model rebuilding during the nested
3672 // updateWatchModel runs.
3673 ++m_pendingWatchRequests;
3674 PENDING_DEBUG("UPDATE WATCH BUMPS PENDING UP TO " << m_pendingWatchRequests);
3676 QMetaObject::invokeMethod(this, "updateWatchDataHelper",
3677 Qt::QueuedConnection, Q_ARG(WatchData, data));
3679 updateWatchDataHelper(data);
3684 void GdbEngine::updateWatchDataHelper(const WatchData &data)
3686 //m_pendingRequests = 0;
3687 PENDING_DEBUG("UPDATE WATCH DATA");
3689 //qDebug() << "##############################################";
3690 qDebug() << "UPDATE MODEL, FOUND INCOMPLETE:";
3691 //qDebug() << data.toString();
3694 updateSubItemClassic(data);
3695 //PENDING_DEBUG("INTERNAL TRIGGERING UPDATE WATCH MODEL");
3696 --m_pendingWatchRequests;
3697 PENDING_DEBUG("UPDATE WATCH DONE BUMPS PENDING DOWN TO " << m_pendingWatchRequests);
3698 if (m_pendingWatchRequests <= 0)
3699 rebuildWatchModel();
3702 void GdbEngine::rebuildWatchModel()
3704 static int count = 0;
3706 if (!isSynchronous())
3707 m_processedNames.clear();
3708 PENDING_DEBUG("REBUILDING MODEL" << count);
3709 if (debuggerCore()->boolSetting(LogTimeStamps))
3710 showMessage(LogWindow::logTimeStamp(), LogMiscInput);
3711 showMessage(_("<Rebuild Watchmodel %1>").arg(count), LogMiscInput);
3712 showStatusMessage(tr("Finished retrieving data"), 400);
3713 watchHandler()->endCycle();
3717 static QByteArray arrayFillCommand(const char *array, const QByteArray ¶ms)
3720 buf.sprintf("set {char[%d]} &%s = {", params.size(), array);
3722 encoded.append(buf.toLocal8Bit());
3723 const int size = params.size();
3724 for (int i = 0; i != size; ++i) {
3725 buf.sprintf("%d,", int(params[i]));
3726 encoded.append(buf.toLocal8Bit());
3728 encoded[encoded.size() - 1] = '}';
3732 void GdbEngine::sendWatchParameters(const QByteArray ¶ms0)
3734 QByteArray params = params0;
3735 params.append('\0');
3736 const QByteArray inBufferCmd = arrayFillCommand("qDumpInBuffer", params);
3738 params.replace('\0','!');
3739 showMessage(QString::fromUtf8(params), LogMiscInput);
3742 params.append('\0');
3743 const QByteArray outBufferCmd = arrayFillCommand("qDumpOutBuffer", params);
3745 postCommand(inBufferCmd);
3746 postCommand(outBufferCmd);
3749 void GdbEngine::handleVarAssign(const GdbResponse &)
3751 // Everything might have changed, force re-evaluation.
3756 void GdbEngine::handleVarCreate(const GdbResponse &response)
3758 WatchData data = response.cookie.value<WatchData>();
3759 // Happens e.g. when we already issued a var-evaluate command.
3760 if (!data.isValid())
3762 //qDebug() << "HANDLE VARIABLE CREATION:" << data.toString();
3763 if (response.resultClass == GdbResultDone) {
3764 data.variable = data.iname;
3765 setWatchDataType(data, response.data.findChild("type"));
3766 if (watchHandler()->isExpandedIName(data.iname)
3767 && !response.data.findChild("children").isValid())
3768 data.setChildrenNeeded();
3770 data.setChildrenUnneeded();
3771 setWatchDataChildCount(data, response.data.findChild("numchild"));
3774 data.setError(QString::fromLocal8Bit(response.data.findChild("msg").data()));
3775 if (data.isWatcher()) {
3776 data.value = WatchData::msgNotInScope();
3778 data.setAllUnneeded();
3779 data.setHasChildren(false);
3780 data.valueEnabled = false;
3781 data.valueEditable = false;
3787 void GdbEngine::handleDebuggingHelperSetup(const GdbResponse &response)
3789 if (response.resultClass == GdbResultDone) {
3791 QString msg = QString::fromLocal8Bit(response.data.findChild("msg").data());
3792 showStatusMessage(tr("Custom dumper setup: %1").arg(msg), 10000);
3796 void GdbEngine::updateLocals(const QVariant &cookie)
3798 m_pendingWatchRequests = 0;
3799 m_pendingBreakpointRequests = 0;
3801 updateLocalsPython(false, QByteArray());
3803 updateLocalsClassic(cookie);
3804 updateMemoryViews();
3808 // Parse a local variable from GdbMi.
3809 WatchData GdbEngine::localVariable(const GdbMi &item,
3810 const QStringList &uninitializedVariables,
3811 QMap<QByteArray, int> *seen)
3813 // Local variables of inlined code are reported as
3814 // 26^done,locals={varobj={exp="this",value="",name="var4",exp="this",
3815 // numchild="1",type="const QtSharedPointer::Basic<CPlusPlus::..."}}
3816 // We do not want these at all. Current hypotheses is that those
3817 // "spurious" locals have _two_ "exp" field. Try to filter them:
3821 foreach (const GdbMi &child, item.children())
3822 numExps += int(child.name() == "exp");
3825 name = item.findChild("exp").data();
3827 name = item.findChild("name").data();
3829 const QMap<QByteArray, int>::iterator it = seen->find(name);
3830 if (it != seen->end()) {
3831 const int n = it.value();
3834 QString nam = _(name);
3835 data.iname = "local." + name + QByteArray::number(n + 1);
3836 data.name = WatchData::shadowedName(nam, n);
3837 if (uninitializedVariables.contains(data.name)) {
3838 data.setError(WatchData::msgNotInScope());
3841 setWatchDataValue(data, item);
3842 //: Type of local variable or parameter shadowed by another
3843 //: variable of the same name in a nested block.
3844 data.setType(GdbEngine::tr("<shadowed>").toUtf8());
3845 data.setHasChildren(false);
3848 seen->insert(name, 1);
3850 QString nam = _(name);
3851 data.iname = "local." + name;
3854 setWatchDataType(data, item.findChild("type"));
3855 if (uninitializedVariables.contains(data.name)) {
3856 data.setError(WatchData::msgNotInScope());
3859 if (isSynchronous()) {
3860 setWatchDataValue(data, item);
3861 // We know that the complete list of children is
3862 // somewhere in the response.
3863 data.setChildrenUnneeded();
3865 // Set value only directly if it is simple enough, otherwise
3866 // pass through the insertData() machinery.
3867 if (isIntOrFloatType(data.type) || isPointerType(data.type))
3868 setWatchDataValue(data, item);
3869 if (isSymbianIntType(data.type)) {
3870 setWatchDataValue(data, item);
3871 data.setHasChildren(false);
3875 if (!watchHandler()->isExpandedIName(data.iname))
3876 data.setChildrenUnneeded();
3878 GdbMi t = item.findChild("numchild");
3880 data.setHasChildren(t.data().toInt() > 0);
3881 else if (isPointerType(data.type) || data.name == __("this"))
3882 data.setHasChildren(true);
3886 void GdbEngine::insertData(const WatchData &data0)
3888 PENDING_DEBUG("INSERT DATA" << data0.toString());
3889 WatchData data = data0;
3890 if (data.value.startsWith(__("mi_cmd_var_create:"))) {
3891 qDebug() << "BOGUS VALUE:" << data.toString();
3894 watchHandler()->insertData(data);
3897 void GdbEngine::assignValueInDebugger(const WatchData *,
3898 const QString &expression, const QVariant &value)
3900 postCommand("-var-delete assign");
3901 postCommand("-var-create assign * " + expression.toLatin1());
3902 postCommand("-var-assign assign " + GdbMi::escapeCString(value.toString().toLatin1()),
3903 Discardable, CB(handleVarAssign));
3906 void GdbEngine::watchPoint(const QPoint &pnt)
3908 QByteArray x = QByteArray::number(pnt.x());
3909 QByteArray y = QByteArray::number(pnt.y());
3910 postCommand("print '" + qtNamespace() + "QApplication::widgetAt'("
3911 + x + ',' + y + ')',
3912 NeedsStop, CB(handleWatchPoint));
3915 void GdbEngine::handleWatchPoint(const GdbResponse &response)
3917 if (response.resultClass == GdbResultDone) {
3918 // "$5 = (void *) 0xbfa7ebfc\n"
3919 const QByteArray ba = parsePlainConsoleStream(response);
3920 //qDebug() << "BA: " << ba;
3921 const int posWidget = ba.indexOf("QWidget");
3922 const int pos0x = ba.indexOf("0x", posWidget + 7);
3923 if (posWidget == -1 || pos0x == -1) {
3924 showStatusMessage(tr("Cannot read widget data: %1").arg(_(ba)));
3926 const QByteArray addr = ba.mid(pos0x);
3927 if (addr.toULongLong(0, 0)) { // Non-null pointer
3928 const QByteArray ns = qtNamespace();
3929 const QByteArray type = ns.isEmpty() ? "QWidget*" : QByteArray("'" + ns + "QWidget'*");
3930 const QString exp = _("(*(struct %1)%2)").arg(_(type)).arg(_(addr));
3931 // qDebug() << posNs << posWidget << pos0x << addr << ns << type;
3932 watchHandler()->watchExpression(exp);
3934 showStatusMessage(tr("Could not find a widget."));
3941 struct MemoryAgentCookie
3943 MemoryAgentCookie() : agent(0), token(0), address(0) {}
3944 MemoryAgentCookie(MemoryAgent *agent_, QObject *token_, quint64 address_)
3945 : agent(agent_), token(token_), address(address_)
3947 QPointer<MemoryAgent> agent;
3948 QPointer<QObject> token;
3952 void GdbEngine::changeMemory(MemoryAgent *agent, QObject *token,
3953 quint64 addr, const QByteArray &data)
3955 QByteArray cmd = "-data-write-memory " + QByteArray::number(addr) + " d 1";
3956 foreach (char c, data) {
3958 cmd.append(QByteArray::number(uint(c)));
3960 postCommand(cmd, NeedsStop, CB(handleChangeMemory),
3961 QVariant::fromValue(MemoryAgentCookie(agent, token, addr)));
3964 void GdbEngine::handleChangeMemory(const GdbResponse &response)
3969 void GdbEngine::fetchMemory(MemoryAgent *agent, QObject *token, quint64 addr,
3972 postCommand("-data-read-memory " + QByteArray::number(addr) + " x 1 1 "
3973 + QByteArray::number(length),
3974 NeedsStop, CB(handleFetchMemory),
3975 QVariant::fromValue(MemoryAgentCookie(agent, token, addr)));
3978 void GdbEngine::handleFetchMemory(const GdbResponse &response)
3980 // ^done,addr="0x08910c88",nr-bytes="16",total-bytes="16",
3981 // next-row="0x08910c98",prev-row="0x08910c78",next-page="0x08910c98",
3982 // prev-page="0x08910c78",memory=[{addr="0x08910c88",
3983 // data=["1","0","0","0","5","0","0","0","0","0","0","0","0","0","0","0"]}]
3984 MemoryAgentCookie ac = response.cookie.value<MemoryAgentCookie>();
3985 QTC_ASSERT(ac.agent, return);
3987 GdbMi memory = response.data.findChild("memory");
3988 QTC_ASSERT(memory.children().size() <= 1, return);
3989 if (memory.children().isEmpty())
3991 GdbMi memory0 = memory.children().at(0); // we asked for only one 'row'
3992 GdbMi data = memory0.findChild("data");
3993 foreach (const GdbMi &child, data.children()) {
3995 unsigned char c = '?';
3996 c = child.data().toUInt(&ok, 0);
3997 QTC_ASSERT(ok, return);
4000 ac.agent->addLazyData(ac.token, ac.address, ba);
4003 class DisassemblerAgentCookie
4006 DisassemblerAgentCookie() : agent(0) {}
4007 DisassemblerAgentCookie(DisassemblerAgent *agent_) : agent(agent_) {}
4010 QPointer<DisassemblerAgent> agent;
4013 void GdbEngine::fetchDisassembler(DisassemblerAgent *agent)
4015 // As of 7.2 the MI output is often less informative then the CLI version.
4016 // So globally fall back to CLI.
4017 if (agent->isMixed())
4018 fetchDisassemblerByCliPointMixed(agent);
4020 fetchDisassemblerByCliPointPlain(agent);
4022 if (agent->isMixed())
4023 fetchDisassemblerByMiRangeMixed(agent)
4025 fetchDisassemblerByMiRangePlain(agent);
4030 void GdbEngine::fetchDisassemblerByMiRangePlain(const DisassemblerAgentCookie &ac0)
4032 // Disassemble full function:
4033 const StackFrame &frame = agent->frame();
4034 DisassemblerAgentCookie ac = ac0;
4035 QTC_ASSERT(ac.agent, return);
4036 const quint64 address = ac.agent->address();
4037 QByteArray cmd = "-data-disassemble"
4038 " -f " + frame.file.toLocal8Bit() +
4039 " -l " + QByteArray::number(frame.line) + " -n -1 -- 1";
4040 postCommand(cmd, Discardable, CB(handleFetchDisassemblerByMiPointMixed),
4041 QVariant::fromValue(DisassemblerAgentCookie(agent)));
4046 void GdbEngine::fetchDisassemblerByMiRangeMixed(const DisassemblerAgentCookie &ac0)
4048 DisassemblerAgentCookie ac = ac0;
4049 QTC_ASSERT(ac.agent, return);
4050 const quint64 address = ac.agent->address();
4051 QByteArray start = QByteArray::number(address - 20, 16);
4052 QByteArray end = QByteArray::number(address + 100, 16);
4053 // -data-disassemble [ -s start-addr -e end-addr ]
4054 // | [ -f filename -l linenum [ -n lines ] ] -- mode
4055 postCommand("-data-disassemble -s 0x" + start + " -e 0x" + end + " -- 1",
4056 Discardable, CB(handleFetchDisassemblerByMiRangeMixed),
4057 QVariant::fromValue(ac));
4062 void GdbEngine::fetchDisassemblerByMiRangePlain(const DisassemblerAgentCookie &ac0)
4064 DisassemblerAgentCookie ac = ac0;
4065 QTC_ASSERT(ac.agent, return);
4066 const quint64 address = ac.agent->address();
4067 QByteArray start = QByteArray::number(address - 20, 16);
4068 QByteArray end = QByteArray::number(address + 100, 16);
4069 // -data-disassemble [ -s start-addr -e end-addr ]
4070 // | [ -f filename -l linenum [ -n lines ] ] -- mode
4071 postCommand("-data-disassemble -s 0x" + start + " -e 0x" + end + " -- 0",
4072 Discardable, CB(handleFetchDisassemblerByMiRangePlain),
4073 QVariant::fromValue(ac));
4077 void GdbEngine::fetchDisassemblerByCliPointMixed(const DisassemblerAgentCookie &ac0)
4079 DisassemblerAgentCookie ac = ac0;
4080 QTC_ASSERT(ac.agent, return);
4081 const quint64 address = ac.agent->address();
4082 QByteArray cmd = "disassemble /m 0x" + QByteArray::number(address, 16);
4083 postCommand(cmd, Discardable, CB(handleFetchDisassemblerByCliPointMixed),
4084 QVariant::fromValue(ac));
4087 void GdbEngine::fetchDisassemblerByCliPointPlain(const DisassemblerAgentCookie &ac0)
4089 DisassemblerAgentCookie ac = ac0;
4090 QTC_ASSERT(ac.agent, return);
4091 const quint64 address = ac.agent->address();
4092 QByteArray cmd = "disassemble 0x" + QByteArray::number(address, 16);
4093 postCommand(cmd, Discardable, CB(handleFetchDisassemblerByCliPointPlain),
4094 QVariant::fromValue(ac));
4097 void GdbEngine::fetchDisassemblerByCliRangeMixed(const DisassemblerAgentCookie &ac0)
4099 DisassemblerAgentCookie ac = ac0;
4100 QTC_ASSERT(ac.agent, return);
4101 const quint64 address = ac.agent->address();
4102 QByteArray start = QByteArray::number(address - 20, 16);
4103 QByteArray end = QByteArray::number(address + 100, 16);
4104 const char sep = m_disassembleUsesComma ? ',' : ' ';
4105 QByteArray cmd = "disassemble /m 0x" + start + sep + "0x" + end;
4106 postCommand(cmd, Discardable, CB(handleFetchDisassemblerByCliRangeMixed),
4107 QVariant::fromValue(ac));
4110 void GdbEngine::fetchDisassemblerByCliRangePlain(const DisassemblerAgentCookie &ac0)
4112 DisassemblerAgentCookie ac = ac0;
4113 QTC_ASSERT(ac.agent, return);
4114 const quint64 address = ac.agent->address();
4115 QByteArray start = QByteArray::number(address - 20, 16);
4116 QByteArray end = QByteArray::number(address + 100, 16);
4117 const char sep = m_disassembleUsesComma ? ',' : ' ';
4118 QByteArray cmd = "disassemble 0x" + start + sep + "0x" + end;
4119 postCommand(cmd, Discardable, CB(handleFetchDisassemblerByCliRangePlain),
4120 QVariant::fromValue(ac));
4123 static DisassemblerLine parseLine(const GdbMi &line)
4125 DisassemblerLine dl;
4126 QByteArray address = line.findChild("address").data();
4127 dl.address = address.toULongLong(0, 0);
4128 dl.data = _(line.findChild("inst").data());
4132 DisassemblerLines GdbEngine::parseMiDisassembler(const GdbMi &lines)
4134 // ^done,data={asm_insns=[src_and_asm_line={line="1243",file=".../app.cpp",
4135 // line_asm_insn=[{address="0x08054857",func-name="main",offset="27",
4136 // inst="call 0x80545b0 <_Z13testQFileInfov>"}]},
4137 // src_and_asm_line={line="1244",file=".../app.cpp",
4138 // line_asm_insn=[{address="0x0805485c",func-name="main",offset="32",
4139 //inst="call 0x804cba1 <_Z11testObject1v>"}]}]}
4141 // ^done,asm_insns=[
4142 // {address="0x0805acf8",func-name="...",offset="25",inst="and $0xe8,%al"},
4143 // {address="0x0805acfa",func-name="...",offset="27",inst="pop %esp"}, ..]
4145 // ^done,asm_insns={
4146 // {address="0x0d8f69e0",func-name="...",offset="1952",inst="add $0x0,%al"},..}
4148 QStringList fileContents;
4149 bool fileLoaded = false;
4150 DisassemblerLines result;
4152 // FIXME: Performance?
4153 foreach (const GdbMi &child, lines.children()) {
4154 if (child.hasName("src_and_asm_line")) {
4157 QString fileName = QFile::decodeName(child.findChild("file").data());
4158 fileName = cleanupFullName(fileName);
4159 QFile file(fileName);
4160 file.open(QIODevice::ReadOnly);
4161 QTextStream ts(&file);
4162 fileContents = ts.readAll().split(QLatin1Char('\n'));
4165 int line = child.findChild("line").data().toInt();
4166 if (line >= 1 && line <= fileContents.size())
4167 result.appendComment(fileContents.at(line - 1));
4168 GdbMi insn = child.findChild("line_asm_insn");
4169 foreach (const GdbMi &item, insn.children())
4170 result.appendLine(parseLine(item));
4172 // The non-mixed version.
4173 result.appendLine(parseLine(child));
4179 DisassemblerLines GdbEngine::parseCliDisassembler(const GdbMi &output)
4181 const QString someSpace = _(" ");
4182 // First line is something like
4183 // "Dump of assembler code from 0xb7ff598f to 0xb7ff5a07:"
4184 DisassemblerLines dlines;
4185 QByteArray lastFunction;
4186 foreach (const QByteArray &line0, output.data().split('\n')) {
4187 QByteArray line = line0.trimmed();
4188 if (line.startsWith("=> "))
4192 if (line.startsWith("Current language:"))
4194 if (line.startsWith("Dump of assembler"))
4196 if (line.startsWith("The current source"))
4198 if (line.startsWith("End of assembler"))
4200 if (line.startsWith("0x")) {
4201 int pos1 = line.indexOf('<') + 1;
4202 int pos2 = line.indexOf('+', pos1);
4203 int pos3 = line.indexOf('>', pos1);
4204 if (pos1 < pos2 && pos2 < pos3) {
4205 QByteArray function = line.mid(pos1, pos2 - pos1);
4206 if (function != lastFunction) {
4207 dlines.appendComment(QString());
4208 dlines.appendComment(_("Function: ") + _(function));
4209 lastFunction = function;
4211 line.replace(pos1, pos2 - pos1, "");
4213 if (pos3 - pos2 == 1)
4214 line.insert(pos2 + 1, "000");
4215 if (pos3 - pos2 == 2)
4216 line.insert(pos2 + 1, "00");
4217 if (pos3 - pos2 == 3)
4218 line.insert(pos2 + 1, "0");
4219 dlines.appendLine(DisassemblerLine(_(line)));
4222 dlines.appendComment(someSpace + _(line));
4227 DisassemblerLines GdbEngine::parseDisassembler(const GdbMi &data)
4229 // Apple's gdb produces MI output even for CLI commands.
4230 // FIXME: Check whether wrapping this into -interpreter-exec console
4231 // (i.e. usgind the 'ConsoleCommand' GdbCommandFlag makes a
4233 GdbMi lines = data.findChild("asm_insns");
4234 if (lines.isValid())
4235 return parseMiDisassembler(lines);
4236 GdbMi output = data.findChild("consolestreamoutput");
4237 return parseCliDisassembler(output);
4240 void GdbEngine::handleDisassemblerCheck(const GdbResponse &response)
4242 m_disassembleUsesComma = response.resultClass != GdbResultDone;
4245 void GdbEngine::handleFetchDisassemblerByCliPointMixed(const GdbResponse &response)
4247 DisassemblerAgentCookie ac = response.cookie.value<DisassemblerAgentCookie>();
4248 QTC_ASSERT(ac.agent, return);
4250 if (response.resultClass == GdbResultDone) {
4251 DisassemblerLines dlines = parseDisassembler(response.data);
4252 if (dlines.coversAddress(ac.agent->address())) {
4253 ac.agent->setContents(dlines);
4257 fetchDisassemblerByCliPointPlain(ac);
4260 void GdbEngine::handleFetchDisassemblerByCliPointPlain(const GdbResponse &response)
4262 DisassemblerAgentCookie ac = response.cookie.value<DisassemblerAgentCookie>();
4263 QTC_ASSERT(ac.agent, return);
4265 if (response.resultClass == GdbResultDone) {
4266 DisassemblerLines dlines = parseDisassembler(response.data);
4267 if (dlines.coversAddress(ac.agent->address())) {
4268 ac.agent->setContents(dlines);
4272 if (ac.agent->isMixed())
4273 fetchDisassemblerByCliRangeMixed(ac);
4275 fetchDisassemblerByCliRangePlain(ac);
4278 void GdbEngine::handleFetchDisassemblerByCliRangeMixed(const GdbResponse &response)
4280 DisassemblerAgentCookie ac = response.cookie.value<DisassemblerAgentCookie>();
4281 QTC_ASSERT(ac.agent, return);
4283 if (response.resultClass == GdbResultDone) {
4284 DisassemblerLines dlines = parseDisassembler(response.data);
4285 if (dlines.coversAddress(ac.agent->address())) {
4286 ac.agent->setContents(dlines);
4290 fetchDisassemblerByCliRangePlain(ac);
4293 void GdbEngine::handleFetchDisassemblerByCliRangePlain(const GdbResponse &response)
4295 DisassemblerAgentCookie ac = response.cookie.value<DisassemblerAgentCookie>();
4296 QTC_ASSERT(ac.agent, return);
4298 if (response.resultClass == GdbResultDone) {
4299 DisassemblerLines dlines = parseDisassembler(response.data);
4300 if (dlines.size()) {
4301 ac.agent->setContents(dlines);
4306 // Finally, give up.
4307 //76^error,msg="No function contains program counter for selected..."
4308 //76^error,msg="No function contains specified address."
4309 //>568^error,msg="Line number 0 out of range;
4310 QByteArray msg = response.data.findChild("msg").data();
4311 showStatusMessage(tr("Disassembler failed: %1")
4312 .arg(QString::fromLocal8Bit(msg)), 5000);
4315 // Binary/configuration check logic.
4317 static QString gdbBinary(const DebuggerStartParameters &sp)
4320 const QByteArray envBinary = qgetenv("QTC_DEBUGGER_PATH");
4321 if (!envBinary.isEmpty())
4322 return QString::fromLocal8Bit(envBinary);
4323 // 2) Command explicitly specified.
4324 if (!sp.debuggerCommand.isEmpty()) {
4326 // Do not use a CDB binary if we got started for a project with MSVC runtime.
4327 const bool abiMatch = sp.toolChainAbi.os() != ProjectExplorer::Abi::WindowsOS
4328 || sp.toolChainAbi.osFlavor() == ProjectExplorer::Abi::WindowsMSysFlavor;
4330 const bool abiMatch = true;
4333 return sp.debuggerCommand;
4335 // 3) Find one from tool chains.
4336 return debuggerCore()->debuggerForAbi(sp.toolChainAbi, GdbEngineType);
4339 bool checkGdbConfiguration(const DebuggerStartParameters &sp, ConfigurationCheck *check)
4341 const QString binary = gdbBinary(sp);
4342 if (gdbBinary(sp).isEmpty()) {
4343 check->errorDetails.push_back(msgNoGdbBinaryForToolChain(sp.toolChainAbi));
4344 check->settingsCategory = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
4345 check->settingsPage = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
4349 // See initialization below, we need an absolute path to be able to locate Python on Windows.
4350 if (!QFileInfo(binary).isAbsolute()) {
4351 check->errorDetails.push_back(GdbEngine::tr("The gdb location must be given as an "
4352 "absolute path in the debugger settings (%1).").arg(binary));
4353 check->settingsCategory = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
4354 check->settingsPage = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
4362 // Starting up & shutting down
4365 bool GdbEngine::startGdb(const QStringList &args, const QString &settingsIdHint)
4367 gdbProc()->disconnect(); // From any previous runs
4369 const DebuggerStartParameters &sp = startParameters();
4370 m_gdb = gdbBinary(sp);
4371 if (m_gdb.isEmpty()) {
4372 handleAdapterStartFailed(
4373 msgNoGdbBinaryForToolChain(sp.toolChainAbi),
4374 _(Constants::DEBUGGER_COMMON_SETTINGS_ID));
4377 QStringList gdbArgs;
4380 if (!debuggerCore()->boolSetting(LoadGdbInit))
4385 // Set python path. By convention, python is located below gdb executable.
4386 // Extend the environment set on the process in startAdapter().
4387 const QFileInfo fi(m_gdb);
4388 QTC_ASSERT(fi.isAbsolute(), return false; )
4390 const QString winPythonVersion = _(winPythonVersionC);
4391 const QDir dir = fi.absoluteDir();
4393 QProcessEnvironment environment = gdbProc()->processEnvironment();
4394 const QString pythonPathVariable = _("PYTHONPATH");
4397 const QString environmentPythonPath = environment.value(pythonPathVariable);
4398 if (dir.exists(winPythonVersion)) {
4399 pythonPath = QDir::toNativeSeparators(dir.absoluteFilePath(winPythonVersion));
4400 } else if (dir.exists(_("lib"))) {
4401 // Needed for our gdb 7.2 packages
4402 pythonPath = QDir::toNativeSeparators(dir.absoluteFilePath(_("lib")));
4404 pythonPath = environmentPythonPath;
4406 if (pythonPath.isEmpty()) {
4407 const QString nativeGdb = QDir::toNativeSeparators(m_gdb);
4408 showMessage(_("GDB %1 CANNOT FIND THE PYTHON INSTALLATION.").arg(nativeGdb));
4409 showStatusMessage(_("%1 cannot find python").arg(nativeGdb));
4410 const QString msg = tr("The GDB installed at %1 cannot "
4411 "find a valid python installation in its %2 subdirectory.\n"
4412 "You may set the environment variable PYTHONPATH to point to your installation.")
4413 .arg(nativeGdb).arg(winPythonVersion);
4414 handleAdapterStartFailed(msg, settingsIdHint);
4417 showMessage(_("Python path: %1").arg(pythonPath), LogMisc);
4419 if (pythonPath != environmentPythonPath) {
4420 environment.insert(pythonPathVariable, pythonPath);
4421 gdbProc()->setProcessEnvironment(environment);
4425 connect(gdbProc(), SIGNAL(error(QProcess::ProcessError)),
4426 SLOT(handleGdbError(QProcess::ProcessError)));
4427 connect(gdbProc(), SIGNAL(finished(int, QProcess::ExitStatus)),
4428 SLOT(handleGdbFinished(int, QProcess::ExitStatus)));
4429 connect(gdbProc(), SIGNAL(readyReadStandardOutput()),
4430 SLOT(readGdbStandardOutput()));
4431 connect(gdbProc(), SIGNAL(readyReadStandardError()),
4432 SLOT(readGdbStandardError()));
4434 showMessage(_("STARTING ") + m_gdb + _(" ") + gdbArgs.join(_(" ")));
4435 gdbProc()->start(m_gdb, gdbArgs);
4437 if (!gdbProc()->waitForStarted()) {
4438 const QString msg = errorMessage(QProcess::FailedToStart);
4439 handleAdapterStartFailed(msg, settingsIdHint);
4443 showMessage(_("GDB STARTED, INITIALIZING IT"));
4444 postCommand("show version", CB(handleShowVersion));
4446 //postCommand("-enable-timings");
4447 //postCommand("set print static-members off"); // Seemingly doesn't work.
4448 //postCommand("set debug infrun 1");
4449 //postCommand("define hook-stop\n-thread-list-ids\n-stack-list-frames\nend");
4450 //postCommand("define hook-stop\nprint 4\nend");
4451 //postCommand("define hookpost-stop\nprint 5\nend");
4452 //postCommand("define hook-call\nprint 6\nend");
4453 //postCommand("define hookpost-call\nprint 7\nend");
4454 //postCommand("set print object on"); // works with CLI, but not MI
4455 //postCommand("set step-mode on"); // we can't work with that yes
4456 //postCommand("set exec-done-display on");
4457 //postCommand("set print pretty on");
4458 //postCommand("set confirm off");
4459 //postCommand("set pagination off");
4461 // The following does not work with 6.3.50-20050815 (Apple version gdb-1344)
4462 // (Mac OS 10.6), but does so for gdb-966 (10.5):
4463 //postCommand("set print inferior-events 1");
4465 postCommand("set breakpoint pending on");
4466 postCommand("set print elements 10000");
4467 // Produces a few messages during symtab loading
4468 //postCommand("set verbose on");
4470 //postCommand("set substitute-path /var/tmp/qt-x11-src-4.5.0 "
4471 // "/home/sandbox/qtsdk-2009.01/qt");
4473 // one of the following is needed to prevent crashes in gdb on code like:
4474 // template <class T> T foo() { return T(0); }
4475 // int main() { return foo<int>(); }
4476 // (gdb) call 'int foo<int>'()
4477 // /build/buildd/gdb-6.8/gdb/valops.c:2069: internal-error:
4478 postCommand("set overload-resolution off");
4479 //postCommand(_("set demangle-style none"));
4481 // Stop means reenter debugger if this signal happens (implies print).
4482 // Print means print a message if this signal happens.
4483 // Pass means let program see this signal;
4484 // otherwise program doesn't know.
4485 // Pass and Stop may be combined.
4486 // We need "print" as otherwise we will get no feedback whatsoever
4487 // when Custom DebuggingHelper crash (which happen regularly when accessing
4488 // uninitialized variables).
4489 postCommand("handle SIGSEGV nopass stop print");
4491 // This is useful to kill the inferior whenever gdb dies.
4492 //postCommand(_("handle SIGTERM pass nostop print"));
4494 postCommand("set unwindonsignal on");
4496 postCommand("set width 0");
4497 postCommand("set height 0");
4498 postCommand("set auto-solib-add on");
4500 if (debuggerCore()->boolSetting(TargetAsync)) {
4501 postCommand("set target-async on");
4502 postCommand("set non-stop on");
4505 // Work around http://bugreports.qt.nokia.com/browse/QTCREATORBUG-2004
4506 postCommand("maintenance set internal-warning quit no", ConsoleCommand);
4507 postCommand("maintenance set internal-error quit no", ConsoleCommand);
4509 postCommand("disassemble 0 0", ConsoleCommand, CB(handleDisassemblerCheck));
4511 loadPythonDumpers();
4513 QString scriptFileName = debuggerCore()->stringSetting(GdbScriptFile);
4514 if (!scriptFileName.isEmpty()) {
4515 if (QFileInfo(scriptFileName).isReadable()) {
4516 postCommand("source " + scriptFileName.toLocal8Bit());
4518 showMessageBox(QMessageBox::Warning,
4519 tr("Cannot find debugger initialization script"),
4520 tr("The debugger settings point to a script file at '%1' "
4521 "which is not accessible. If a script file is not needed, "
4522 "consider clearing that entry to avoid this warning. "
4523 ).arg(scriptFileName));
4530 void GdbEngine::loadPythonDumpers()
4532 const QByteArray dumperSourcePath =
4533 Core::ICore::instance()->resourcePath().toLocal8Bit() + "/gdbmacros/";
4535 postCommand("python execfile('" + dumperSourcePath + "dumper.py')",
4536 ConsoleCommand|NonCriticalResponse);
4537 postCommand("python execfile('" + dumperSourcePath + "gdbmacros.py')",
4538 ConsoleCommand|NonCriticalResponse);
4539 postCommand("bbsetup",
4540 ConsoleCommand, CB(handleHasPython));
4543 void GdbEngine::handleGdbError(QProcess::ProcessError error)
4545 const QString msg = errorMessage(error);
4546 showMessage(_("HANDLE GDB ERROR: ") + msg);
4547 // Show a message box for asynchronously reported issues.
4549 case QProcess::FailedToStart:
4550 // This should be handled by the code trying to start the process.
4552 case QProcess::Crashed:
4553 // This will get a processExited() as well.
4555 case QProcess::ReadError:
4556 case QProcess::WriteError:
4557 case QProcess::Timedout:
4559 //gdbProc()->kill();
4560 //notifyEngineIll();
4561 showMessageBox(QMessageBox::Critical, tr("GDB I/O Error"), msg);
4566 void GdbEngine::handleGdbFinished(int code, QProcess::ExitStatus type)
4568 if (m_commandTimer.isActive())
4569 m_commandTimer.stop();
4571 showMessage(_("GDB PROCESS FINISHED, status %1, code %2").arg(type).arg(code));
4574 case EngineShutdownRequested:
4575 notifyEngineShutdownOk();
4578 // This could either be a real gdb crash or a quickly exited inferior
4579 // in the terminal adapter. In this case the stub proc will die soon,
4580 // too, so there's no need to act here.
4581 showMessage(_("The gdb process exited somewhat unexpectedly."));
4582 notifyEngineSpontaneousShutdown();
4585 notifyEngineIll(); // Initiate shutdown sequence
4586 const QString msg = type == QProcess::CrashExit ?
4587 tr("The gdb process crashed.") :
4588 tr("The gdb process exited unexpectedly (code %1)").arg(code);
4589 showMessageBox(QMessageBox::Critical, tr("Unexpected GDB Exit"), msg);
4595 void GdbEngine::handleAdapterStartFailed(const QString &msg,
4596 const QString &settingsIdHint)
4598 QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state());
4599 showMessage(_("ADAPTER START FAILED"));
4600 if (!msg.isEmpty()) {
4601 const QString title = tr("Adapter start failed");
4602 if (settingsIdHint.isEmpty()) {
4603 Core::ICore::instance()->showWarningWithOptions(title, msg);
4605 Core::ICore::instance()->showWarningWithOptions(title, msg, QString(),
4606 _(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY), settingsIdHint);
4609 notifyEngineSetupFailed();
4612 void GdbEngine::handleAdapterStarted()
4614 QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state());
4615 showMessage(_("ADAPTER SUCCESSFULLY STARTED"));
4616 notifyEngineSetupOk();
4619 void GdbEngine::setupInferior()
4621 QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
4622 showStatusMessage(tr("Setting up inferior..."));
4623 m_gdbAdapter->setupInferior();
4626 void GdbEngine::notifyInferiorSetupFailed()
4628 // FIXME: that's not enough to stop gdb from getting confused
4629 // by a timeout of the adapter.
4630 //resetCommandQueue();
4631 DebuggerEngine::notifyInferiorSetupFailed();
4634 void GdbEngine::handleInferiorPrepared()
4636 typedef GlobalDebuggerOptions::SourcePathMap SourcePathMap;
4637 typedef SourcePathMap::const_iterator SourcePathMapIterator;
4639 QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
4641 // Apply source path mappings from global options.
4642 const SourcePathMap sourcePathMap =
4643 DebuggerSourcePathMappingWidget::mergePlatformQtPath(
4644 startParameters().qtInstallPath,
4645 debuggerCore()->globalDebuggerOptions()->sourcePathMap);
4647 if (!sourcePathMap.isEmpty()) {
4648 const SourcePathMapIterator cend = sourcePathMap.constEnd();
4649 for (SourcePathMapIterator it = sourcePathMap.constBegin(); it != cend; ++it) {
4650 QByteArray command = "set substitute-path ";
4651 command += it.key().toLocal8Bit();
4653 command += it.value().toLocal8Bit();
4654 postCommand(command);
4658 // Initial attempt to set breakpoints.
4659 if (startParameters().startMode != AttachCore) {
4660 showStatusMessage(tr("Setting breakpoints..."));
4661 showMessage(tr("Setting breakpoints..."));
4662 attemptBreakpointSynchronization();
4665 if (m_cookieForToken.isEmpty()) {
4666 finishInferiorSetup();
4668 QTC_ASSERT(m_commandsDoneCallback == 0, /**/);
4669 m_commandsDoneCallback = &GdbEngine::finishInferiorSetup;
4673 void GdbEngine::finishInferiorSetup()
4675 QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
4676 // Extract Qt namespace.
4679 QTemporaryFile symbols(QDir::tempPath() + _("/gdb_ns_"));
4681 fileName = symbols.fileName();
4683 postCommand("maint print msymbols " + fileName.toLocal8Bit(),
4684 CB(handleNamespaceExtraction), fileName);
4687 void GdbEngine::handleNamespaceExtraction(const GdbResponse &response)
4689 QFile file(response.cookie.toString());
4690 file.open(QIODevice::ReadOnly);
4691 QByteArray ba = file.readAll();
4693 int pos = ba.indexOf("7QString9fromAscii");
4695 while (pos1 > 0 && ba.at(pos1) != 'N' && ba.at(pos1) > '@')
4698 const QByteArray ns = ba.mid(pos1, pos - pos1);
4700 showMessage(_("FOUND NON-NAMESPACED QT"));
4702 showMessage(_("FOUND NAMESPACED QT: " + ns));
4703 setQtNamespace(ns + "::");
4705 postCommand("-break-insert -f '" + qtNamespace() + "qFatal'",
4706 CB(handleBreakOnQFatal));
4709 void GdbEngine::handleBreakOnQFatal(const GdbResponse &response)
4711 if (response.resultClass == GdbResultDone) {
4712 GdbMi bkpt = response.data.findChild("bkpt");
4713 GdbMi number = bkpt.findChild("number");
4714 int bpnr = number.data().toInt();
4716 m_qFatalBreakpointNumber = bpnr;
4717 postCommand("-break-commands " + number.data() + " return");
4722 notifyInferiorSetupOk();
4725 void GdbEngine::runEngine()
4727 QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
4728 m_gdbAdapter->runEngine();
4731 void GdbEngine::notifyInferiorSetupFailed(const QString &msg)
4733 showStatusMessage(tr("Failed to start application: ") + msg);
4734 if (state() == EngineSetupFailed) {
4735 showMessage(_("INFERIOR START FAILED, BUT ADAPTER DIED ALREADY"));
4736 return; // Adapter crashed meanwhile, so this notification is meaningless.
4738 showMessage(_("INFERIOR START FAILED"));
4739 showMessageBox(QMessageBox::Critical, tr("Failed to start application"), msg);
4740 DebuggerEngine::notifyInferiorSetupFailed();
4743 void GdbEngine::handleAdapterCrashed(const QString &msg)
4745 showMessage(_("ADAPTER CRASHED"));
4747 // The adapter is expected to have cleaned up after itself when we get here,
4748 // so the effect is about the same as AdapterStartFailed => use it.
4749 // Don't bother with state transitions - this can happen in any state and
4750 // the end result is always the same, so it makes little sense to find a
4751 // "path" which does not assert.
4752 if (state() == EngineSetupRequested)
4753 notifyEngineSetupFailed();
4757 // No point in being friendly here ...
4761 showMessageBox(QMessageBox::Critical, tr("Adapter crashed"), msg);
4764 void GdbEngine::setUseDebuggingHelpers(const QVariant &)
4770 bool GdbEngine::hasPython() const
4775 void GdbEngine::createFullBacktrace()
4777 postCommand("thread apply all bt full",
4778 NeedsStop|ConsoleCommand, CB(handleCreateFullBacktrace));
4781 void GdbEngine::handleCreateFullBacktrace(const GdbResponse &response)
4783 if (response.resultClass == GdbResultDone) {
4784 debuggerCore()->openTextEditor(_("Backtrace $"),
4785 _(response.data.findChild("consolestreamoutput").data()));
4789 void GdbEngine::resetCommandQueue()
4791 m_commandTimer.stop();
4792 if (!m_cookieForToken.isEmpty()) {
4794 QTextStream ts(&msg);
4795 ts << "RESETING COMMAND QUEUE. LEFT OVER TOKENS: ";
4796 foreach (const GdbCommand &cookie, m_cookieForToken)
4797 ts << "CMD:" << cookie.command << cookie.callbackName;
4798 m_cookieForToken.clear();
4803 void GdbEngine::handleRemoteSetupDone(int gdbServerPort, int qmlPort)
4805 m_gdbAdapter->handleRemoteSetupDone(gdbServerPort, qmlPort);
4808 void GdbEngine::handleRemoteSetupFailed(const QString &message)
4810 m_gdbAdapter->handleRemoteSetupFailed(message);
4813 bool GdbEngine::setupQmlStep(bool on)
4815 QTC_ASSERT(isSlaveEngine(), return false);
4816 m_qmlBreakpointNumbers.clear();
4817 //qDebug() << "CLEAR: " << m_qmlBreakpointNumbers;
4818 postCommand("tbreak '" + qtNamespace() + "QScript::FunctionWrapper::proxyCall'\n"
4820 "set $d=(void*)((FunctionWrapper*)callee)->data->function\n"
4821 "tbreak *$d\nprintf \"QMLBP:%d \\n\",$bpnum\ncontinue\nend",
4822 NeedsStop, CB(handleSetQmlStepBreakpoint));
4823 m_preparedForQmlBreak = on;
4827 void GdbEngine::handleSetQmlStepBreakpoint(const GdbResponse &response)
4829 //QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
4830 if (response.resultClass == GdbResultDone) {
4831 // "{logstreamoutput="tbreak 'myns::QScript::FunctionWrapper::proxyCall'\n"
4832 //,consolestreamoutput="Temporary breakpoint 1 at 0xf166e7:
4833 // file bridge/qscriptfunction.cpp, line 75.\n"}
4834 QByteArray ba = parsePlainConsoleStream(response);
4835 const int pos2 = ba.indexOf(" at 0x");
4836 const int pos1 = ba.lastIndexOf(" ", pos2 - 1) + 1;
4837 QByteArray mid = ba.mid(pos1, pos2 - pos1);
4838 const int bpnr = mid.toInt();
4839 m_qmlBreakpointNumbers[1] = bpnr;
4840 //qDebug() << "SET: " << m_qmlBreakpointNumbers;
4842 QTC_ASSERT(masterEngine(), return);
4843 masterEngine()->readyToExecuteQmlStep();
4846 bool GdbEngine::isQmlStepBreakpoint1(int bpnr) const
4848 //qDebug() << "CHECK 1: " << m_qmlBreakpointNumbers[1] << bpnr;
4849 return bpnr && m_qmlBreakpointNumbers[1] == bpnr;
4852 bool GdbEngine::isQmlStepBreakpoint2(int bpnr) const
4854 //qDebug() << "CHECK 2: " << m_qmlBreakpointNumbers[2] << bpnr;
4855 return bpnr && m_qmlBreakpointNumbers[2] == bpnr;
4858 bool GdbEngine::isQFatalBreakpoint(int bpnr) const
4860 return bpnr && m_qFatalBreakpointNumber == bpnr;
4867 DebuggerEngine *createGdbEngine(const DebuggerStartParameters &startParameters,
4868 DebuggerEngine *masterEngine)
4870 return new GdbEngine(startParameters, masterEngine);
4873 void addGdbOptionPages(QList<Core::IOptionsPage *> *opts)
4875 opts->push_back(new GdbOptionsPage());
4878 } // namespace Internal
4879 } // namespace Debugger
4881 Q_DECLARE_METATYPE(Debugger::Internal::MemoryAgentCookie)
4882 Q_DECLARE_METATYPE(Debugger::Internal::DisassemblerAgentCookie)
4883 Q_DECLARE_METATYPE(Debugger::Internal::GdbMi)