1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** Licensees holding valid Qt Commercial licenses may use this file in
12 ** accordance with the Qt Commercial License Agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and Nokia.
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 ** If you are unsure which license is appropriate for your use, please
26 ** contact the sales department at http://qt.nokia.com/contact.
28 **************************************************************************/
30 #define QT_NO_CAST_FROM_ASCII
32 #include "gdbengine.h"
33 #include "gdboptionspage.h"
34 #include "trkoptions.h"
35 #include "trkoptionspage.h"
37 #include "attachgdbadapter.h"
38 #include "coregdbadapter.h"
39 #include "plaingdbadapter.h"
40 #include "termgdbadapter.h"
41 #include "remotegdbadapter.h"
42 #include "trkgdbadapter.h"
44 #include "watchutils.h"
45 #include "debuggeractions.h"
46 #include "debuggeragents.h"
47 #include "debuggerconstants.h"
48 #include "debuggermanager.h"
49 #include "debuggertooltip.h"
50 #include "debuggerstringutils.h"
53 #include "breakhandler.h"
54 #include "moduleshandler.h"
55 #include "registerhandler.h"
56 #include "stackhandler.h"
57 #include "watchhandler.h"
58 #include "sourcefileswindow.h"
60 #include "debuggerdialogs.h"
62 #include <utils/qtcassert.h>
63 #include <utils/fancymainwindow.h>
64 #include <texteditor/itexteditor.h>
65 #include <projectexplorer/toolchain.h>
66 #include <coreplugin/icore.h>
68 #include <QtCore/QDebug>
69 #include <QtCore/QDir>
70 #include <QtCore/QFileInfo>
71 #include <QtCore/QMetaObject>
72 #include <QtCore/QTime>
73 #include <QtCore/QTimer>
74 #include <QtCore/QTextStream>
76 #include <QtGui/QAction>
77 #include <QtCore/QCoreApplication>
78 #include <QtGui/QLabel>
79 #include <QtGui/QMainWindow>
80 #include <QtGui/QMessageBox>
81 #include <QtGui/QDialogButtonBox>
82 #include <QtGui/QPushButton>
84 # include "shared/sharedlibraryinjector.h"
96 //#define DEBUG_PENDING 1
97 //#define DEBUG_SUBITEM 1
100 # define PENDING_DEBUG(s) qDebug() << s
102 # define PENDING_DEBUG(s)
104 #define PENDING_DEBUGX(s) qDebug() << s
106 #define CB(callback) &GdbEngine::callback, STRINGIFY(callback)
108 static bool stateAcceptsGdbCommands(DebuggerState state)
111 case AdapterStarting:
113 case AdapterStartFailed:
114 case InferiorUnrunnable:
115 case InferiorStarting:
116 case InferiorStartFailed:
117 case InferiorRunningRequested:
118 case InferiorRunningRequested_Kill:
119 case InferiorRunning:
120 case InferiorStopping:
121 case InferiorStopping_Kill:
122 case InferiorStopped:
123 case InferiorShuttingDown:
124 case InferiorShutDown:
125 case InferiorShutdownFailed:
127 case DebuggerNotReady:
129 case InferiorStopFailed:
130 case EngineShuttingDown:
136 static int ¤tToken()
138 static int token = 0;
142 // reads a MI-encoded item frome the consolestream
143 static bool parseConsoleStream(const GdbResponse &response, GdbMi *contents)
145 GdbMi output = response.data.findChild("consolestreamoutput");
146 QByteArray out = output.data();
148 int markerPos = out.indexOf('"') + 1; // position of 'success marker'
149 if (markerPos == 0 || out.at(markerPos) == 'f') { // 't' or 'f'
150 // custom dumper produced no output
154 out = out.mid(markerPos + 1);
155 out = out.left(out.lastIndexOf('"'));
156 // optimization: dumper output never needs real C unquoting
157 out.replace('\\', "");
158 out = "dummy={" + out + "}";
160 contents->fromString(out);
161 //qDebug() << "CONTENTS" << contents->toString(true);
162 return contents->isValid();
165 static QByteArray parsePlainConsoleStream(const GdbResponse &response)
167 GdbMi output = response.data.findChild("consolestreamoutput");
168 QByteArray out = output.data();
169 // FIXME: proper decoding needed
170 if (out.endsWith("\\n"))
172 while (out.endsWith('\n') || out.endsWith(' '))
174 int pos = out.indexOf(" = ");
175 return out.mid(pos + 3);
178 ///////////////////////////////////////////////////////////////////////
182 ///////////////////////////////////////////////////////////////////////
184 GdbEngine::GdbEngine(DebuggerManager *manager) :
185 IDebuggerEngine(manager),
186 #ifdef Q_OS_WIN // Do injection loading with MinGW (call loading does not work with 64bit)
187 m_dumperInjectionLoad(true)
189 m_dumperInjectionLoad(false)
192 m_trkOptions = QSharedPointer<TrkOptions>(new TrkOptions);
193 m_trkOptions->fromSettings(Core::ICore::instance()->settings());
196 m_commandTimer = new QTimer(this);
197 m_commandTimer->setSingleShot(true);
198 m_commandTimer->setInterval(COMMAND_TIMEOUT);
199 connect(m_commandTimer, SIGNAL(timeout()), SLOT(commandTimeout()));
201 // Needs no resetting in initializeVariables()
204 connect(theDebuggerAction(AutoDerefPointers), SIGNAL(valueChanged(QVariant)),
205 this, SLOT(setAutoDerefPointers(QVariant)));
208 void GdbEngine::connectDebuggingHelperActions()
210 connect(theDebuggerAction(UseDebuggingHelpers), SIGNAL(valueChanged(QVariant)),
211 this, SLOT(setUseDebuggingHelpers(QVariant)));
212 connect(theDebuggerAction(DebugDebuggingHelpers), SIGNAL(valueChanged(QVariant)),
213 this, SLOT(setDebugDebuggingHelpers(QVariant)));
214 connect(theDebuggerAction(RecheckDebuggingHelpers), SIGNAL(triggered()),
215 this, SLOT(recheckDebuggingHelperAvailability()));
218 void GdbEngine::disconnectDebuggingHelperActions()
220 disconnect(theDebuggerAction(UseDebuggingHelpers), 0, this, 0);
221 disconnect(theDebuggerAction(DebugDebuggingHelpers), 0, this, 0);
222 disconnect(theDebuggerAction(RecheckDebuggingHelpers), 0, this, 0);
225 DebuggerStartMode GdbEngine::startMode() const
227 QTC_ASSERT(!m_startParameters.isNull(), return NoStartMode);
228 return m_startParameters->startMode;
231 QMainWindow *GdbEngine::mainWindow() const
233 return m_manager->mainWindow();
236 GdbEngine::~GdbEngine()
238 // prevent sending error messages afterwards
239 disconnect(&m_gdbProc, 0, this, 0);
244 void GdbEngine::connectAdapter()
246 connect(m_gdbAdapter, SIGNAL(adapterStarted()),
247 this, SLOT(handleAdapterStarted()));
248 connect(m_gdbAdapter, SIGNAL(adapterStartFailed(QString,QString)),
249 this, SLOT(handleAdapterStartFailed(QString,QString)));
251 connect(m_gdbAdapter, SIGNAL(inferiorPrepared()),
252 this, SLOT(handleInferiorPrepared()));
254 connect(m_gdbAdapter, SIGNAL(inferiorStartFailed(QString)),
255 this, SLOT(handleInferiorStartFailed(QString)));
257 connect(m_gdbAdapter, SIGNAL(adapterCrashed(QString)),
258 this, SLOT(handleAdapterCrashed(QString)));
261 void GdbEngine::initializeVariables()
263 m_debuggingHelperState = DebuggingHelperUninitialized;
265 m_gdbBuildVersion = -1;
267 m_isSynchroneous = false;
268 m_registerNamesListed = false;
270 m_fullToShortName.clear();
271 m_shortToFullName.clear();
274 m_modulesListOutdated = m_sourcesListOutdated = true;
275 m_sourcesListUpdating = false;
276 m_oldestAcceptableToken = -1;
277 m_outputCodec = QTextCodec::codecForLocale();
278 m_pendingRequests = 0;
279 m_commandsDoneCallback = 0;
280 m_commandsToRunOnTemporaryBreak.clear();
281 m_cookieForToken.clear();
283 m_pendingConsoleStreamOutput.clear();
284 m_pendingLogStreamOutput.clear();
288 m_commandTimer->stop();
290 // ConverterState has no reset() function.
291 m_outputCodecState.~ConverterState();
292 new (&m_outputCodecState) QTextCodec::ConverterState();
294 m_currentFunctionArgs.clear();
295 m_currentFrame.clear();
296 m_dumperHelper.clear();
298 m_entryPoint.clear();
302 QString GdbEngine::errorMessage(QProcess::ProcessError error)
305 case QProcess::FailedToStart:
306 return tr("The Gdb process failed to start. Either the "
307 "invoked program '%1' is missing, or you may have insufficient "
308 "permissions to invoke the program.")
309 .arg(theDebuggerStringSetting(GdbLocation));
310 case QProcess::Crashed:
311 return tr("The Gdb process crashed some time after starting "
313 case QProcess::Timedout:
314 return tr("The last waitFor...() function timed out. "
315 "The state of QProcess is unchanged, and you can try calling "
316 "waitFor...() again.");
317 case QProcess::WriteError:
318 return tr("An error occurred when attempting to write "
319 "to the Gdb process. For example, the process may not be running, "
320 "or it may have closed its input channel.");
321 case QProcess::ReadError:
322 return tr("An error occurred when attempting to read from "
323 "the Gdb process. For example, the process may not be running.");
325 return tr("An unknown error in the Gdb process occurred. ");
330 static void dump(const char *first, const char *middle, const QString & to)
332 QByteArray ba(first, middle - first);
334 // note that qDebug cuts off output after a certain size... (bug?)
335 qDebug("\n>>>>> %s\n%s\n====\n%s\n<<<<<\n",
336 qPrintable(currentTime()),
337 qPrintable(QString(ba).trimmed()),
338 qPrintable(to.trimmed()));
340 //qDebug() << qPrintable(currentTime())
341 // << " Reading response: " << QString(ba).trimmed() << "\n";
345 void GdbEngine::readDebugeeOutput(const QByteArray &data)
347 m_manager->showApplicationOutput(m_outputCodec->toUnicode(
348 data.constData(), data.length(), &m_outputCodecState));
351 void GdbEngine::debugMessage(const QString &msg)
353 gdbOutputAvailable(LogDebug, msg);
356 void GdbEngine::handleResponse(const QByteArray &buff)
358 static QTime lastTime;
360 if (theDebuggerBoolSetting(LogTimeStamps))
361 gdbOutputAvailable(LogTime, currentTime());
362 gdbOutputAvailable(LogOutput, QString::fromLocal8Bit(buff, buff.length()));
365 qDebug() // << "#### start response handling #### "
367 << lastTime.msecsTo(QTime::currentTime()) << "ms,"
368 << "buf:" << buff.left(1500) << "..."
370 << "size:" << buff.size();
372 //qDebug() << "buf:" << buff;
375 lastTime = QTime::currentTime();
377 if (buff.isEmpty() || buff == "(gdb) ")
380 const char *from = buff.constData();
381 const char *to = from + buff.size();
385 // token is a sequence of numbers
386 for (inner = from; inner != to; ++inner)
387 if (*inner < '0' || *inner > '9')
390 token = QByteArray(from, inner - from).toInt();
392 //qDebug() << "found token" << token;
395 // next char decides kind of response
396 const char c = *from++;
397 //qDebug() << "CODE:" << c;
402 QByteArray asyncClass;
403 for (; from != to; ++from) {
404 const char c = *from;
409 //qDebug() << "ASYNCCLASS" << asyncClass;
415 // happens on archer where we get
416 // 23^running <NL> *running,thread-id="all" <NL> (gdb)
417 result.m_type = GdbMi::Tuple;
421 data.parseResultOrValue(from, to);
422 if (data.isValid()) {
423 //qDebug() << "parsed result:" << data.toString();
424 result.m_children += data;
425 result.m_type = GdbMi::Tuple;
428 if (asyncClass == "stopped") {
429 handleStopResponse(result);
430 m_pendingLogStreamOutput.clear();
431 m_pendingConsoleStreamOutput.clear();
432 } else if (asyncClass == "running") {
433 // Archer has 'thread-id="all"' here
434 } else if (asyncClass == "library-loaded") {
435 // Archer has 'id="/usr/lib/libdrm.so.2",
436 // target-name="/usr/lib/libdrm.so.2",
437 // host-name="/usr/lib/libdrm.so.2",
438 // symbols-loaded="0"
439 QByteArray id = result.findChild("id").data();
441 showStatusMessage(tr("Library %1 loaded.").arg(_(id)));
442 m_modulesListOutdated = m_sourcesListOutdated = true;
443 } else if (asyncClass == "library-unloaded") {
444 // Archer has 'id="/usr/lib/libdrm.so.2",
445 // target-name="/usr/lib/libdrm.so.2",
446 // host-name="/usr/lib/libdrm.so.2"
447 QByteArray id = result.findChild("id").data();
448 showStatusMessage(tr("Library %1 unloaded.").arg(_(id)));
449 m_modulesListOutdated = m_sourcesListOutdated = true;
450 } else if (asyncClass == "thread-group-created") {
451 // Archer has "{id="28902"}"
452 QByteArray id = result.findChild("id").data();
453 showStatusMessage(tr("Thread group %1 created.").arg(_(id)));
454 int pid = id.toInt();
455 if (pid != inferiorPid())
456 handleInferiorPidChanged(pid);
457 } else if (asyncClass == "thread-created") {
458 //"{id="1",group-id="28902"}"
459 QByteArray id = result.findChild("id").data();
460 showStatusMessage(tr("Thread %1 created.").arg(_(id)));
461 } else if (asyncClass == "thread-group-exited") {
462 // Archer has "{id="28902"}"
463 QByteArray id = result.findChild("id").data();
464 showStatusMessage(tr("Thread group %1 exited.").arg(_(id)));
465 } else if (asyncClass == "thread-exited") {
466 //"{id="1",group-id="28902"}"
467 QByteArray id = result.findChild("id").data();
468 QByteArray groupid = result.findChild("group-id").data();
469 showStatusMessage(tr("Thread %1 in group %2 exited.")
470 .arg(_(id)).arg(_(groupid)));
471 } else if (asyncClass == "thread-selected") {
472 QByteArray id = result.findChild("id").data();
473 showStatusMessage(tr("Thread %1 selected.").arg(_(id)));
475 #if defined(Q_OS_MAC)
476 } else if (asyncClass == "shlibs-updated") {
477 // MAC announces updated libs
478 m_modulesListOutdated = m_sourcesListOutdated = true;
479 } else if (asyncClass == "shlibs-added") {
480 // MAC announces added libs
481 // {shlib-info={num="2", name="libmathCommon.A_debug.dylib",
482 // kind="-", dyld-addr="0x7f000", reason="dyld", requested-state="Y",
483 // state="Y", path="/usr/lib/system/libmathCommon.A_debug.dylib",
484 // description="/usr/lib/system/libmathCommon.A_debug.dylib",
485 // loaded_addr="0x7f000", slide="0x7f000", prefix=""}}
486 m_modulesListOutdated = m_sourcesListOutdated = true;
489 qDebug() << "IGNORED ASYNC OUTPUT"
490 << asyncClass << result.toString();
496 QByteArray data = GdbMi::parseCString(from, to);
497 m_pendingConsoleStreamOutput += data;
499 // Parse pid from noise.
500 if (!inferiorPid()) {
501 // Linux/Mac gdb: [New [Tt]hread 0x545 (LWP 4554)]
502 static QRegExp re1(_("New .hread 0x[0-9a-f]+ \\(LWP ([0-9]*)\\)"));
503 // MinGW 6.8: [New thread 2437.0x435345]
504 static QRegExp re2(_("New .hread ([0-9]+)\\.0x[0-9a-f]*"));
505 // Mac: [Switching to process 9294 local thread 0x2e03] or
506 // [Switching to process 31773]
507 static QRegExp re3(_("Switching to process ([0-9]+)"));
508 QTC_ASSERT(re1.isValid() && re2.isValid(), return);
509 if (re1.indexIn(_(data)) != -1)
510 maybeHandleInferiorPidChanged(re1.cap(1));
511 else if (re2.indexIn(_(data)) != -1)
512 maybeHandleInferiorPidChanged(re2.cap(1));
513 else if (re3.indexIn(_(data)) != -1)
514 maybeHandleInferiorPidChanged(re3.cap(1));
517 // Show some messages to give the impression something happens.
518 if (data.startsWith("Reading symbols from ")) {
519 showStatusMessage(tr("Reading %1...").arg(_(data.mid(21))), 1000);
520 m_modulesListOutdated = m_sourcesListOutdated = true;
521 } else if (data.startsWith("[New ") || data.startsWith("[Thread ")) {
522 if (data.endsWith('\n'))
524 showStatusMessage(_(data), 1000);
530 readDebugeeOutput(GdbMi::parseCString(from, to));
535 QByteArray data = GdbMi::parseCString(from, to);
536 m_pendingLogStreamOutput += data;
537 // On Windows, the contents seem to depend on the debugger
538 // version and/or OS version used.
539 if (data.startsWith("warning:"))
540 manager()->showApplicationOutput(_(data.mid(9))); // cut "warning: "
545 GdbResponse response;
547 response.token = token;
549 for (inner = from; inner != to; ++inner)
550 if (*inner < 'a' || *inner > 'z')
553 QByteArray resultClass = QByteArray::fromRawData(from, inner - from);
554 if (resultClass == "done") {
555 response.resultClass = GdbResultDone;
556 } else if (resultClass == "running") {
557 if (state() == InferiorStopped) { // Result of manual command.
558 m_manager->resetLocation();
560 setState(InferiorRunningRequested);
562 setState(InferiorRunning);
563 showStatusMessage(tr("Running..."));
564 response.resultClass = GdbResultRunning;
565 } else if (resultClass == "connected") {
566 response.resultClass = GdbResultConnected;
567 } else if (resultClass == "error") {
568 response.resultClass = GdbResultError;
569 } else if (resultClass == "exit") {
570 response.resultClass = GdbResultExit;
572 response.resultClass = GdbResultUnknown;
579 response.data.parseTuple_helper(from, to);
580 response.data.m_type = GdbMi::Tuple;
581 response.data.m_name = "data";
584 response.data.m_type = GdbMi::Tuple;
585 response.data.m_name = "data";
589 //qDebug() << "\nLOG STREAM:" + m_pendingLogStreamOutput;
590 //qDebug() << "\nCONSOLE STREAM:" + m_pendingConsoleStreamOutput;
591 response.data.setStreamOutput("logstreamoutput",
592 m_pendingLogStreamOutput);
593 response.data.setStreamOutput("consolestreamoutput",
594 m_pendingConsoleStreamOutput);
595 m_pendingLogStreamOutput.clear();
596 m_pendingConsoleStreamOutput.clear();
598 handleResultRecord(&response);
602 qDebug() << "UNKNOWN RESPONSE TYPE" << c;
608 void GdbEngine::readGdbStandardError()
610 QByteArray err = m_gdbProc.readAllStandardError();
611 if (err == "Undefined command: \"bb\". Try \"help\".\n")
613 qWarning() << "Unexpected gdb stderr:" << err;
616 void GdbEngine::readGdbStandardOutput()
618 if (m_commandTimer->isActive())
619 m_commandTimer->start(); // Retrigger
622 int scan = m_inbuffer.size();
624 m_inbuffer.append(m_gdbProc.readAllStandardOutput());
626 // This can trigger when a dialog starts a nested event loop
630 while (newstart < m_inbuffer.size()) {
631 int start = newstart;
632 int end = m_inbuffer.indexOf('\n', scan);
634 m_inbuffer.remove(0, start);
641 #if defined(Q_OS_WIN)
642 if (m_inbuffer.at(end - 1) == '\r') {
649 handleResponse(QByteArray::fromRawData(m_inbuffer.constData() + start, end - start));
655 void GdbEngine::interruptInferior()
657 QTC_ASSERT(state() == InferiorRunning, qDebug() << state(); return);
659 setState(InferiorStopping);
660 showStatusMessage(tr("Stop requested..."), 5000);
662 debugMessage(_("TRYING TO INTERRUPT INFERIOR"));
663 m_gdbAdapter->interruptInferior();
666 void GdbEngine::interruptInferiorTemporarily()
669 foreach (const GdbCommand &cmd, m_commandsToRunOnTemporaryBreak)
670 if (cmd.flags & LosesChild) {
671 setState(InferiorStopping_Kill);
676 void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
678 const qint64 pid = pid0.toLongLong();
680 debugMessage(_("Cannot parse PID from %1").arg(pid0));
683 if (pid == inferiorPid())
685 debugMessage(_("FOUND PID %1").arg(pid));
687 handleInferiorPidChanged(pid);
688 if (m_dumperInjectionLoad)
689 tryLoadDebuggingHelpers();
692 void GdbEngine::postCommand(const QString &command, AdapterCallback callback,
693 const char *callbackName, const QVariant &cookie)
695 postCommand(command, NoFlags, callback, callbackName, cookie);
698 void GdbEngine::postCommand(const QString &command, GdbCommandFlags flags,
699 AdapterCallback callback,
700 const char *callbackName, const QVariant &cookie)
703 cmd.command = command;
705 cmd.adapterCallback = callback;
706 cmd.callbackName = callbackName;
708 postCommandHelper(cmd);
711 void GdbEngine::postCommand(const QString &command, GdbCommandCallback callback,
712 const char *callbackName, const QVariant &cookie)
714 postCommand(command, NoFlags, callback, callbackName, cookie);
717 void GdbEngine::postCommand(const QString &command, GdbCommandFlags flags,
718 GdbCommandCallback callback, const char *callbackName,
719 const QVariant &cookie)
722 cmd.command = command;
724 cmd.callback = callback;
725 cmd.callbackName = callbackName;
727 postCommandHelper(cmd);
730 void GdbEngine::postCommandHelper(const GdbCommand &cmd)
732 if (!stateAcceptsGdbCommands(state())) {
733 PENDING_DEBUG(_("NO GDB PROCESS RUNNING, CMD IGNORED: ") + cmd.command);
734 debugMessage(_("NO GDB PROCESS RUNNING, CMD IGNORED: %1 %2")
735 .arg(cmd.command).arg(state()));
739 if (cmd.flags & RebuildModel) {
741 PENDING_DEBUG(" MODEL:" << cmd.command << "=>" << cmd.callbackName
742 << "INCREMENTS PENDING TO" << m_pendingRequests);
744 PENDING_DEBUG(" OTHER (IN):" << cmd.command << "=>" << cmd.callbackName
745 << "LEAVES PENDING AT" << m_pendingRequests);
748 if ((cmd.flags & NeedsStop) || !m_commandsToRunOnTemporaryBreak.isEmpty()) {
749 if (state() == InferiorStopped || state() == InferiorUnrunnable
750 || state() == InferiorStarting || state() == AdapterStarted) {
751 // Can be safely sent now.
754 // Queue the commands that we cannot send at once.
755 debugMessage(_("QUEUING COMMAND ") + cmd.command);
756 m_commandsToRunOnTemporaryBreak.append(cmd);
757 if (state() == InferiorStopping) {
758 if (cmd.flags & LosesChild)
759 setState(InferiorStopping_Kill);
760 debugMessage(_("CHILD ALREADY BEING INTERRUPTED"));
761 } else if (state() == InferiorStopping_Kill) {
762 debugMessage(_("CHILD ALREADY BEING INTERRUPTED (KILL PENDING)"));
763 } else if (state() == InferiorRunningRequested) {
764 if (cmd.flags & LosesChild)
765 setState(InferiorRunningRequested_Kill);
766 debugMessage(_("RUNNING REQUESTED; POSTPONING INTERRUPT"));
767 } else if (state() == InferiorRunningRequested_Kill) {
768 debugMessage(_("RUNNING REQUESTED; POSTPONING INTERRUPT (KILL PENDING)"));
769 } else if (state() == InferiorRunning) {
770 showStatusMessage(tr("Stopping temporarily."), 1000);
771 interruptInferiorTemporarily();
773 qDebug() << "ATTEMPTING TO QUEUE COMMAND IN INAPPROPRIATE STATE" << state();
776 } else if (!cmd.command.isEmpty()) {
781 void GdbEngine::flushQueuedCommands()
783 showStatusMessage(tr("Processing queued commands."), 1000);
784 while (!m_commandsToRunOnTemporaryBreak.isEmpty()) {
785 GdbCommand cmd = m_commandsToRunOnTemporaryBreak.takeFirst();
786 debugMessage(_("RUNNING QUEUED COMMAND %1 %2")
787 .arg(cmd.command).arg(_(cmd.callbackName)));
792 void GdbEngine::flushCommand(const GdbCommand &cmd0)
794 GdbCommand cmd = cmd0;
795 if (state() == DebuggerNotReady) {
796 gdbInputAvailable(LogInput, cmd.command);
797 debugMessage(_("GDB PROCESS NOT RUNNING, PLAIN CMD IGNORED: ") + cmd.command);
802 cmd.postTime = QTime::currentTime();
803 m_cookieForToken[currentToken()] = cmd;
804 cmd.command = QString::number(currentToken()) + cmd.command;
805 if (cmd.flags & EmbedToken)
806 cmd.command = cmd.command.arg(currentToken());
807 gdbInputAvailable(LogInput, cmd.command);
809 m_gdbAdapter->write(cmd.command.toLatin1() + "\r\n");
811 m_commandTimer->start();
813 if (cmd.flags & LosesChild)
814 setState(InferiorShuttingDown);
817 void GdbEngine::commandTimeout()
819 // FIXME this needs a proper message box
820 debugMessage(_("TIMED OUT WAITING FOR GDB REPLY. COMMANDS STILL IN PROGRESS:"));
821 QList<int> keys = m_cookieForToken.keys();
823 foreach (int key, keys) {
824 const GdbCommand &cmd = m_cookieForToken[key];
825 debugMessage(_(" %1: %2 => %3").arg(key).arg(cmd.command).arg(_(cmd.callbackName)));
827 // This is an entirely undefined state, so we just pull the emergency brake.
828 setState(EngineShuttingDown, true);
832 void GdbEngine::handleResultRecord(GdbResponse *response)
834 //qDebug() << "TOKEN:" << response.token
835 // << " ACCEPTABLE:" << m_oldestAcceptableToken;
836 //qDebug() << "\nRESULT" << response.token << response.toString();
838 int token = response->token;
842 if (!m_cookieForToken.contains(token)) {
843 // In theory this should not happen (rather the error should be
844 // reported in the "first" response to the command) in practice it
845 // does. We try to handle a few situations we are aware of gracefully.
846 // Ideally, this code should not be present at all.
847 debugMessage(_("COOKIE FOR TOKEN %1 ALREADY EATEN. "
848 "TWO RESPONSES FOR ONE COMMAND?").arg(token));
849 if (response->resultClass == GdbResultError) {
850 QByteArray msg = response->data.findChild("msg").data();
851 if (msg == "Cannot find new threads: generic error") {
852 // Handle a case known to occur on Linux/gdb 6.8 when debugging moc
853 // with helpers enabled. In this case we get a second response with
854 // msg="Cannot find new threads: generic error"
855 debugMessage(_("APPLYING WORKAROUND #1"));
856 showMessageBox(QMessageBox::Critical,
857 tr("Executable failed"), QString::fromLocal8Bit(msg));
858 showStatusMessage(tr("Process failed to start."));
860 } else if (msg == "\"finish\" not meaningful in the outermost frame.") {
861 // Handle a case known to appear on gdb 6.4 symbianelf when
862 // the stack is cut due to access to protected memory.
863 debugMessage(_("APPLYING WORKAROUND #2"));
864 setState(InferiorStopping);
865 setState(InferiorStopped);
866 } else if (msg.startsWith("Cannot find bounds of current function")) {
867 // Happens when running "-exec-next" in a function for which
868 // there is no debug information. Divert to "-exec-next-step"
869 debugMessage(_("APPLYING WORKAROUND #3"));
870 setState(InferiorStopping);
871 setState(InferiorStopped);
873 } else if (msg.startsWith("Couldn't get registers: No such process.")) {
874 // Happens on archer-tromey-python 6.8.50.20090910-cvs
875 // There might to be a race between a process shutting down
876 // and library load messages.
877 debugMessage(_("APPLYING WORKAROUND #4"));
878 setState(InferiorStopping);
879 setState(InferiorStopped);
880 setState(InferiorShuttingDown);
881 setState(InferiorShutDown);
882 showStatusMessage(tr("Executable failed: %1")
883 .arg(QString::fromLocal8Bit(msg)));
885 showMessageBox(QMessageBox::Critical,
886 tr("Executable failed"), QString::fromLocal8Bit(msg));
888 showMessageBox(QMessageBox::Critical,
889 tr("Executable failed"), QString::fromLocal8Bit(msg));
890 showStatusMessage(tr("Executable failed: %1")
891 .arg(QString::fromLocal8Bit(msg)));
897 GdbCommand cmd = m_cookieForToken.take(token);
898 if (theDebuggerBoolSetting(LogTimeStamps)) {
899 gdbOutputAvailable(LogTime, _("Response time: %1: %2 s")
901 .arg(cmd.postTime.msecsTo(QTime::currentTime()) / 1000.));
904 if (response->token < m_oldestAcceptableToken && (cmd.flags & Discardable)) {
905 //debugMessage(_("### SKIPPING OLD RESULT") + response.toString());
909 response->cookie = cmd.cookie;
911 if (response->resultClass != GdbResultError &&
912 response->resultClass != ((cmd.flags & RunRequest) ? GdbResultRunning :
913 (cmd.flags & ExitRequest) ? GdbResultExit :
915 QString rsp = _(GdbResponse::stringFromResultClass(response->resultClass));
916 qWarning() << "UNEXPECTED RESPONSE " << rsp << " TO COMMAND" << cmd.command << " AT " __FILE__ ":" STRINGIFY(__LINE__);
917 debugMessage(_("UNEXPECTED RESPONSE %1 TO COMMAND %2").arg(rsp).arg(cmd.command));
920 (this->*cmd.callback)(*response);
921 else if (cmd.adapterCallback)
922 (m_gdbAdapter->*cmd.adapterCallback)(*response);
925 if (cmd.flags & RebuildModel) {
927 PENDING_DEBUG(" WATCH" << cmd.command << "=>" << cmd.callbackName
928 << "DECREMENTS PENDING TO" << m_pendingRequests);
929 if (m_pendingRequests <= 0) {
930 PENDING_DEBUG("\n\n ... AND TRIGGERS MODEL UPDATE\n");
934 PENDING_DEBUG(" OTHER (OUT):" << cmd.command << "=>" << cmd.callbackName
935 << "LEAVES PENDING AT" << m_pendingRequests);
938 // Commands were queued, but we were in RunningRequested state, so the interrupt
940 // This is done after the command callbacks so the running-requesting commands
941 // can assert on the right state.
942 if (state() == InferiorRunning && !m_commandsToRunOnTemporaryBreak.isEmpty())
943 interruptInferiorTemporarily();
945 // Continue only if there are no commands wire anymore, so this will
946 // be fully synchroneous.
947 // This is somewhat inefficient, as it makes the last command synchronous.
948 // An optimization would be requesting the continue immediately when the
949 // event loop is entered, and let individual commands have a flag to suppress
951 if (m_commandsDoneCallback && m_cookieForToken.isEmpty()) {
952 debugMessage(_("ALL COMMANDS DONE; INVOKING CALLBACK"));
953 CommandsDoneCallback cont = m_commandsDoneCallback;
954 m_commandsDoneCallback = 0;
957 PENDING_DEBUG("MISSING TOKENS: " << m_cookieForToken.keys());
960 if (m_cookieForToken.isEmpty())
961 m_commandTimer->stop();
964 void GdbEngine::executeDebuggerCommand(const QString &command)
966 if (state() == DebuggerNotReady) {
967 debugMessage(_("GDB PROCESS NOT RUNNING, PLAIN CMD IGNORED: ") + command);
971 m_gdbAdapter->write(command.toLatin1() + "\r\n");
974 // Called from CoreAdapter and AttachAdapter
975 void GdbEngine::updateAll()
977 QTC_ASSERT(state() == InferiorUnrunnable || state() == InferiorStopped, /**/);
978 tryLoadDebuggingHelpers();
979 reloadModulesInternal();
980 postCommand(_("-stack-list-frames"), WatchUpdate, CB(handleStackListFrames),
981 QVariant::fromValue<StackCookie>(StackCookie(false, true)));
982 manager()->stackHandler()->setCurrentIndex(0);
983 if (supportsThreads())
984 postCommand(_("-thread-list-ids"), WatchUpdate, CB(handleStackListThreads), 0);
985 manager()->reloadRegisters();
989 void GdbEngine::handleQuerySources(const GdbResponse &response)
991 if (response.resultClass == GdbResultDone) {
992 QMap<QString, QString> oldShortToFull = m_shortToFullName;
993 m_shortToFullName.clear();
994 m_fullToShortName.clear();
995 // "^done,files=[{file="../../../../bin/gdbmacros/gdbmacros.cpp",
996 // fullname="/data5/dev/ide/main/bin/gdbmacros/gdbmacros.cpp"},
997 GdbMi files = response.data.findChild("files");
998 foreach (const GdbMi &item, files.children()) {
999 GdbMi fullName = item.findChild("fullname");
1000 if (fullName.isValid()) {
1001 QString full = cleanupFullName(QString::fromLocal8Bit(fullName.data()));
1002 QString fileName = QString::fromLocal8Bit(item.findChild("file").data());
1003 m_shortToFullName[fileName] = full;
1004 m_fullToShortName[full] = fileName;
1007 if (m_shortToFullName != oldShortToFull)
1008 manager()->sourceFileWindow()->setSourceFiles(m_shortToFullName);
1013 void GdbEngine::handleExecJumpToLine(const GdbResponse &response)
1015 // FIXME: remove this special case as soon as 'jump'
1016 // is supported by MI
1017 // "&"jump /home/apoenitz/dev/work/test1/test1.cpp:242"
1018 // ~"Continuing at 0x4058f3."
1019 // ~"run1 (argc=1, argv=0x7fffb213a478) at test1.cpp:242"
1022 setState(InferiorStopped);
1023 showStatusMessage(tr("Jumped. Stopped."));
1024 QByteArray output = response.data.findChild("logstreamoutput").data();
1025 if (output.isEmpty())
1027 int idx1 = output.indexOf(' ') + 1;
1029 int idx2 = output.indexOf(':', idx1);
1031 QString file = QString::fromLocal8Bit(output.mid(idx1, idx2 - idx1));
1032 int line = output.mid(idx2 + 1).toInt();
1033 gotoLocation(file, line, true);
1039 //void GdbEngine::handleExecRunToFunction(const GdbResponse &response)
1041 // // FIXME: remove this special case as soon as there's a real
1042 // // reason given when the temporary breakpoint is hit.
1043 // // reight now we get:
1044 // // 14*stopped,thread-id="1",frame={addr="0x0000000000403ce4",
1045 // // func="foo",args=[{name="str",value="@0x7fff0f450460"}],
1046 // // file="main.cpp",fullname="/tmp/g/main.cpp",line="37"}
1047 // QTC_ASSERT(state() == InferiorStopping, qDebug() << state())
1048 // setState(InferiorStopped);
1049 // showStatusMessage(tr("Function reached. Stopped."));
1050 // GdbMi frame = response.data.findChild("frame");
1051 // StackFrame f = parseStackFrame(frame, 0);
1052 // gotoLocation(f, true);
1055 static bool isExitedReason(const QByteArray &reason)
1057 return reason == "exited-normally" // inferior exited normally
1058 || reason == "exited-signalled" // inferior exited because of a signal
1059 //|| reason == "signal-received" // inferior received signal
1060 || reason == "exited"; // inferior exited
1064 void GdbEngine::handleAqcuiredInferior()
1066 // Reverse debugging. FIXME: Should only be used when available.
1067 //if (theDebuggerBoolSetting(EnableReverseDebugging))
1068 // postCommand(_("target response"));
1070 tryLoadDebuggingHelpers();
1073 // intentionally after tryLoadDebuggingHelpers(),
1074 // otherwise we'd interupt solib loading.
1075 if (theDebuggerBoolSetting(AllPluginBreakpoints)) {
1076 postCommand(_("set auto-solib-add on"));
1077 postCommand(_("set stop-on-solib-events 0"));
1078 postCommand(_("sharedlibrary .*"));
1079 } else if (theDebuggerBoolSetting(SelectedPluginBreakpoints)) {
1080 postCommand(_("set auto-solib-add on"));
1081 postCommand(_("set stop-on-solib-events 1"));
1082 postCommand(_("sharedlibrary ")
1083 + theDebuggerStringSetting(SelectedPluginBreakpointsPattern));
1084 } else if (theDebuggerBoolSetting(NoPluginBreakpoints)) {
1085 // should be like that already
1086 if (!m_dumperInjectionLoad)
1087 postCommand(_("set auto-solib-add off"));
1088 postCommand(_("set stop-on-solib-events 0"));
1092 // It's nicer to see a bit of the world we live in.
1093 reloadModulesInternal();
1094 attemptBreakpointSynchronization();
1098 void GdbEngine::handleStopResponse(const GdbMi &data)
1100 // This is gdb 7+'s initial *stopped in response to attach.
1101 // For consistency, we just discard it.
1102 if (state() == InferiorStarting)
1105 const QByteArray reason = data.findChild("reason").data();
1107 if (isExitedReason(reason)) {
1108 if (state() == InferiorRunning) {
1109 setState(InferiorStopping);
1111 // The user triggered a stop, but meanwhile the app simply exited ...
1112 QTC_ASSERT(state() == InferiorStopping || state() == InferiorStopping_Kill,
1113 qDebug() << state());
1115 setState(InferiorStopped);
1117 if (reason == "exited") {
1118 msg = tr("Program exited with exit code %1.")
1119 .arg(_(data.findChild("exit-code").toString()));
1120 } else if (reason == "exited-signalled" || reason == "signal-received") {
1121 msg = tr("Program exited after receiving signal %1.")
1122 .arg(_(data.findChild("signal-name").toString()));
1124 msg = tr("Program exited normally.");
1126 showStatusMessage(msg);
1127 setState(InferiorShuttingDown);
1128 setState(InferiorShutDown);
1133 if (!m_commandsToRunOnTemporaryBreak.isEmpty()) {
1134 QTC_ASSERT(state() == InferiorStopping || state() == InferiorStopping_Kill,
1135 qDebug() << state())
1136 setState(InferiorStopped);
1137 flushQueuedCommands();
1138 if (state() == InferiorStopped) {
1139 QTC_ASSERT(m_commandsDoneCallback == 0, /**/);
1140 m_commandsDoneCallback = &GdbEngine::autoContinueInferior;
1142 QTC_ASSERT(state() == InferiorShuttingDown, qDebug() << state())
1147 if (state() == InferiorRunning) {
1148 // Stop triggered by a breakpoint or otherwise not directly
1149 // initiated by the user.
1150 setState(InferiorStopping);
1152 QTC_ASSERT(state() == InferiorStopping, qDebug() << state());
1154 setState(InferiorStopped);
1156 // Due to LD_PRELOADing the dumpers, these events can occur even before
1157 // reaching the entry point. So handle it before the entry point hacks below.
1158 if (reason.isEmpty() && m_gdbVersion < 70000 && !m_isMacGdb) {
1159 // On Linux it reports "Stopped due to shared library event\n", but
1160 // on Windows it simply forgets about it. Thus, we identify the response
1161 // based on it having no frame information.
1162 if (!data.findChild("frame").isValid()) {
1163 m_modulesListOutdated = m_sourcesListOutdated = true;
1164 // Each stop causes a roundtrip and button flicker, so prevent
1165 // a flood of useless stops. Will be automatically re-enabled.
1166 postCommand(_("set stop-on-solib-events 0"));
1168 // The related code (handleAqcuiredInferior()) is disabled as well.
1169 if (theDebuggerBoolSetting(SelectedPluginBreakpoints)) {
1170 QString dataStr = _(data.toString());
1171 debugMessage(_("SHARED LIBRARY EVENT: ") + dataStr);
1172 QString pat = theDebuggerStringSetting(SelectedPluginBreakpointsPattern);
1173 debugMessage(_("PATTERN: ") + pat);
1174 postCommand(_("sharedlibrary ") + pat);
1175 showStatusMessage(tr("Loading %1...").arg(dataStr));
1178 continueInferiorInternal();
1184 if (!m_entryPoint.isEmpty()) {
1185 GdbMi frameData = data.findChild("frame");
1186 if (frameData.findChild("addr").data() == m_entryPoint) {
1187 // There are two expected reasons for getting here:
1188 // 1) For some reason, attaching to a stopped process causes *two* SIGSTOPs
1189 // when trying to continue (kernel i386 2.6.24-23-ubuntu, gdb 6.8).
1190 // Interestingly enough, on MacOSX no signal is delivered at all.
1191 // 2) The explicit tbreak at the entry point we set to query the PID.
1192 // Gdb <= 6.8 reports a frame but no reason, 6.8.50+ reports everything.
1193 // The case of the user really setting a breakpoint at _start is simply
1195 if (!inferiorPid()) // For programs without -pthread under gdb <= 6.8.
1196 postCommand(_("info proc"), CB(handleInfoProc));
1197 continueInferiorInternal();
1200 // We are past the initial stop(s). No need to waste time on further checks.
1201 m_entryPoint.clear();
1205 // seen on XP after removing a breakpoint while running
1206 // >945*stopped,reason="signal-received",signal-name="SIGTRAP",
1207 // signal-meaning="Trace/breakpoint trap",thread-id="2",
1208 // frame={addr="0x7c91120f",func="ntdll!DbgUiConnectToDbg",
1209 // args=[],from="C:\\WINDOWS\\system32\\ntdll.dll"}
1210 //if (reason == "signal-received"
1211 // && data.findChild("signal-name").data() == "SIGTRAP") {
1212 // continueInferiorInternal();
1216 // jump over well-known frames
1217 static int stepCounter = 0;
1218 if (theDebuggerBoolSetting(SkipKnownFrames)) {
1219 if (reason == "end-stepping-range" || reason == "function-finished") {
1220 GdbMi frame = data.findChild("frame");
1221 //debugMessage(frame.toString());
1222 QString funcName = _(frame.findChild("func").data());
1223 QString fileName = QString::fromLocal8Bit(frame.findChild("file").data());
1224 if (isLeavableFunction(funcName, fileName)) {
1225 //debugMessage(_("LEAVING ") + funcName);
1227 m_manager->stepOutExec();
1231 if (isSkippableFunction(funcName, fileName)) {
1232 //debugMessage(_("SKIPPING ") + funcName);
1234 m_manager->stepExec();
1238 // qDebug() << "STEPCOUNTER:" << stepCounter;
1243 bool initHelpers = m_debuggingHelperState == DebuggingHelperUninitialized
1244 || m_debuggingHelperState == DebuggingHelperLoadTried;
1245 // Don't load helpers on stops triggered by signals unless it's
1246 // an intentional trap.
1247 if (initHelpers && reason == "signal-received"
1248 && data.findChild("signal-name").data() != "SIGTRAP")
1249 initHelpers = false;
1250 if (isSynchroneous())
1251 initHelpers = false;
1253 tryLoadDebuggingHelpers();
1254 QVariant var = QVariant::fromValue<GdbMi>(data);
1255 postCommand(_("p 4"), CB(handleStop1), var); // dummy
1259 // Dumper loading is sequenced, as otherwise the display functions
1260 // will start requesting data without knowing that dumpers are available.
1263 void GdbEngine::handleStop1(const GdbResponse &response)
1265 handleStop1(response.cookie.value<GdbMi>());
1268 void GdbEngine::handleStop1(const GdbMi &data)
1270 if (m_modulesListOutdated)
1271 reloadModulesInternal(); // This is for display only
1272 if (m_sourcesListOutdated)
1273 reloadSourceFilesInternal(); // This needs to be done before fullName() may need it
1275 QByteArray reason = data.findChild("reason").data();
1276 if (reason == "breakpoint-hit") {
1277 showStatusMessage(tr("Stopped at breakpoint."));
1279 if (reason == "signal-received"
1280 && theDebuggerBoolSetting(UseMessageBoxForSignals)) {
1281 QByteArray name = data.findChild("signal-name").data();
1282 // Ignore SIGTRAP as they are showing up regularily when
1283 // stopping debugging.
1284 if (name != "SIGTRAP") {
1285 QByteArray meaning = data.findChild("signal-meaning").data();
1286 QString msg = tr("<p>The inferior stopped because it received a "
1287 "signal from the Operating System.<p>"
1288 "<table><tr><td>Signal name : </td><td>%1</td></tr>"
1289 "<tr><td>Signal meaning : </td><td>%2</td></tr></table>")
1290 .arg(name.isEmpty() ? tr(" <Unknown> ", "name") : _(name))
1291 .arg(meaning.isEmpty() ? tr(" <Unknown> ", "meaning") : _(meaning));
1292 showMessageBox(QMessageBox::Information,
1293 tr("Signal received"), msg);
1297 if (reason.isEmpty())
1298 showStatusMessage(tr("Stopped."));
1300 showStatusMessage(tr("Stopped: \"%1\"").arg(_(reason)));
1303 const GdbMi gdbmiFrame = data.findChild("frame");
1305 m_currentFrame = _(gdbmiFrame.findChild("addr").data() + '%' +
1306 gdbmiFrame.findChild("func").data() + '%');
1308 // Quick shot: Jump to stack frame #0.
1310 if (gdbmiFrame.isValid()) {
1311 frame = parseStackFrame(gdbmiFrame, 0);
1312 gotoLocation(frame, true);
1318 manager()->stackHandler()->setCurrentIndex(0);
1319 updateLocals(qVariantFromValue(frame)); // Quick shot
1323 if (supportsThreads()) {
1324 int currentId = data.findChild("thread-id").data().toInt();
1325 postCommand(_("-thread-list-ids"), WatchUpdate,
1326 CB(handleStackListThreads), currentId);
1332 manager()->reloadRegisters();
1336 void GdbEngine::handleInfoProc(const GdbResponse &response)
1338 if (response.resultClass == GdbResultDone) {
1339 static QRegExp re(_("\\bprocess ([0-9]+)\n"));
1340 QTC_ASSERT(re.isValid(), return);
1341 if (re.indexIn(_(response.data.findChild("consolestreamoutput").data())) != -1)
1342 maybeHandleInferiorPidChanged(re.cap(1));
1347 void GdbEngine::handleShowVersion(const GdbResponse &response)
1349 //qDebug () << "VERSION 2:" << response.data.findChild("consolestreamoutput").data();
1350 //qDebug () << "VERSION:" << response.toString();
1351 debugMessage(_("VERSION: " + response.toString()));
1352 if (response.resultClass == GdbResultDone) {
1354 m_gdbBuildVersion = -1;
1356 QString msg = QString::fromLocal8Bit(response.data.findChild("consolestreamoutput").data());
1357 QRegExp supported(_("GNU gdb(.*) (\\d+)\\.(\\d+)(\\.(\\d+))?(-(\\d+))?"));
1358 if (supported.indexIn(msg) == -1) {
1359 debugMessage(_("UNSUPPORTED GDB VERSION ") + msg);
1361 QStringList list = msg.split(_c('\n'));
1362 while (list.size() > 2)
1364 msg = tr("The debugger you are using identifies itself as:")
1365 + _("<p><p>") + list.join(_("<br>")) + _("<p><p>")
1366 + tr("This version is not officially supported by Qt Creator.\n"
1367 "Debugging will most likely not work well.\n"
1368 "Using gdb 6.7 or later is strongly recommended.");
1370 // ugly, but 'Show again' check box...
1371 static QErrorMessage *err = new QErrorMessage(mainWindow());
1372 err->setMinimumSize(400, 300);
1373 err->showMessage(msg);
1375 //showMessageBox(QMessageBox::Information, tr("Warning"), msg);
1379 m_gdbVersion = 10000 * supported.cap(2).toInt()
1380 + 100 * supported.cap(3).toInt()
1381 + 1 * supported.cap(5).toInt();
1382 m_gdbBuildVersion = supported.cap(7).toInt();
1383 m_isMacGdb = msg.contains(__("Apple version"));
1384 debugMessage(_("GDB VERSION: %1, BUILD: %2%3").arg(m_gdbVersion)
1385 .arg(m_gdbBuildVersion).arg(_(m_isMacGdb ? " (APPLE)" : "")));
1387 //qDebug () << "VERSION 3:" << m_gdbVersion << m_gdbBuildVersion;
1391 void GdbEngine::handleIsSynchroneous(const GdbResponse &response)
1394 if (response.resultClass == GdbResultDone) {
1395 m_isSynchroneous = true;
1397 m_isSynchroneous = false;
1401 void GdbEngine::handleExecContinue(const GdbResponse &response)
1403 if (response.resultClass == GdbResultRunning) {
1404 // The "running" state is picked up in handleResponse()
1405 QTC_ASSERT(state() == InferiorRunning, /**/);
1407 if (state() == InferiorRunningRequested_Kill) {
1408 setState(InferiorStopped);
1412 QTC_ASSERT(state() == InferiorRunningRequested, /**/);
1413 setState(InferiorStopped);
1414 QByteArray msg = response.data.findChild("msg").data();
1415 if (msg.startsWith("Cannot find bounds of current function")) {
1416 if (!m_commandsToRunOnTemporaryBreak.isEmpty())
1417 flushQueuedCommands();
1418 showStatusMessage(tr("Stopped."), 5000);
1419 //showStatusMessage(tr("No debug information available. "
1420 // "Leaving function..."));
1423 showMessageBox(QMessageBox::Critical, tr("Execution Error"),
1424 tr("Cannot continue debugged process:\n") + QString::fromLocal8Bit(msg));
1430 QString GdbEngine::fullName(const QString &fileName)
1432 if (fileName.isEmpty())
1434 QTC_ASSERT(!m_sourcesListOutdated, /* */)
1435 QTC_ASSERT(!m_sourcesListUpdating, /* */)
1436 return m_shortToFullName.value(fileName, QString());
1440 QString GdbEngine::cleanupFullName(const QString &fileName)
1442 QTC_ASSERT(!fileName.isEmpty(), return QString())
1443 // Gdb on windows often delivers "fullnames" which
1444 // a) have no drive letter and b) are not normalized.
1445 QFileInfo fi(fileName);
1446 if (!fi.isReadable())
1448 return QDir::cleanPath(fi.absoluteFilePath());
1452 void GdbEngine::shutdown()
1454 debugMessage(_("INITIATE GDBENGINE SHUTDOWN"));
1456 case DebuggerNotReady: // Nothing to do! :)
1457 case EngineStarting: // We can't get here, really
1458 case InferiorShuttingDown: // Will auto-trigger further shutdown steps
1459 case EngineShuttingDown: // Do not disturb! :)
1460 case InferiorRunningRequested_Kill:
1461 case InferiorStopping_Kill:
1463 case AdapterStarting: // GDB is up, adapter is "doing something"
1464 setState(AdapterStartFailed);
1465 m_gdbAdapter->shutdown();
1467 case AdapterStartFailed: // Adapter "did something", but it did not help
1468 if (m_gdbProc.state() == QProcess::Running) {
1469 m_commandsToRunOnTemporaryBreak.clear();
1470 postCommand(_("-gdb-exit"), GdbEngine::ExitRequest, CB(handleGdbExit));
1472 setState(DebuggerNotReady);
1475 case InferiorRunningRequested:
1476 case InferiorRunning:
1477 case InferiorStopping:
1478 case InferiorStopped:
1479 m_commandsToRunOnTemporaryBreak.clear();
1480 postCommand(_(m_gdbAdapter->inferiorShutdownCommand()),
1481 NeedsStop | LosesChild, CB(handleInferiorShutdown));
1483 case AdapterStarted: // We can't get here, really
1484 case InferiorStartFailed:
1485 case InferiorShutDown:
1486 case InferiorShutdownFailed: // Whatever
1487 case InferiorUnrunnable:
1488 m_commandsToRunOnTemporaryBreak.clear();
1489 postCommand(_("-gdb-exit"), GdbEngine::ExitRequest, CB(handleGdbExit));
1490 setState(EngineShuttingDown); // Do it after posting the command!
1492 case InferiorStarting: // This may take some time, so just short-circuit it
1493 setState(InferiorStartFailed);
1495 case InferiorStopFailed: // Tough luck, I guess. But unreachable as of now anyway.
1496 setState(EngineShuttingDown);
1502 void GdbEngine::handleInferiorShutdown(const GdbResponse &response)
1504 QTC_ASSERT(state() == InferiorShuttingDown, qDebug() << state());
1505 if (response.resultClass == GdbResultDone) {
1506 debugMessage(_("INFERIOR SUCCESSFULLY SHUT DOWN"));
1507 setState(InferiorShutDown);
1509 debugMessage(_("INFERIOR SHUTDOWN FAILED"));
1510 setState(InferiorShutdownFailed);
1511 QString msg = m_gdbAdapter->msgInferiorStopFailed(_(response.data.findChild("msg").data()));
1512 showMessageBox(QMessageBox::Critical, tr("Inferior shutdown failed"), msg);
1514 shutdown(); // re-iterate...
1517 void GdbEngine::handleGdbExit(const GdbResponse &response)
1519 if (response.resultClass == GdbResultExit) {
1520 debugMessage(_("GDB CLAIMS EXIT; WAITING"));
1521 m_commandsDoneCallback = 0;
1522 // don't set state here, this will be handled in handleGdbFinished()
1524 QString msg = m_gdbAdapter->msgGdbStopFailed(_(response.data.findChild("msg").data()));
1525 debugMessage(_("GDB WON'T EXIT (%1); KILLING IT").arg(msg));
1530 void GdbEngine::detachDebugger()
1532 QTC_ASSERT(state() == InferiorStopped, /**/);
1533 QTC_ASSERT(startMode() != AttachCore, /**/);
1534 postCommand(_("detach"));
1535 setState(InferiorShuttingDown);
1536 setState(InferiorShutDown);
1540 void GdbEngine::exitDebugger() // called from the manager
1542 disconnectDebuggingHelperActions();
1546 int GdbEngine::currentFrame() const
1548 return manager()->stackHandler()->currentIndex();
1551 bool GdbEngine::checkConfiguration(int toolChain, QString *errorMessage, QString *settingsPage) const
1553 switch (toolChain) {
1554 case ProjectExplorer::ToolChain::WINSCW: // S60
1555 case ProjectExplorer::ToolChain::GCCE:
1556 case ProjectExplorer::ToolChain::RVCT_ARMV5:
1557 case ProjectExplorer::ToolChain::RVCT_ARMV6:
1558 if (!m_trkOptions->check(errorMessage)) {
1560 *settingsPage = TrkOptionsPage::settingsId();
1569 AbstractGdbAdapter *GdbEngine::createAdapter(const DebuggerStartParametersPtr &sp)
1571 switch (sp->toolChainType) {
1572 case ProjectExplorer::ToolChain::WINSCW: // S60
1573 case ProjectExplorer::ToolChain::GCCE:
1574 case ProjectExplorer::ToolChain::RVCT_ARMV5:
1575 case ProjectExplorer::ToolChain::RVCT_ARMV6:
1576 return new TrkGdbAdapter(this, m_trkOptions);
1580 // @todo: remove testing hack
1581 if (sp->processArgs.size() == 3 && sp->processArgs.at(0) == _("@sym@"))
1582 return new TrkGdbAdapter(this, m_trkOptions);
1583 switch (sp->startMode) {
1585 return new CoreGdbAdapter(this);
1587 return new RemoteGdbAdapter(this, sp->toolChainType);
1588 case AttachExternal:
1589 return new AttachGdbAdapter(this);
1591 if (sp->useTerminal)
1592 return new TermGdbAdapter(this);
1593 return new PlainGdbAdapter(this);
1597 void GdbEngine::startDebugger(const DebuggerStartParametersPtr &sp)
1599 QTC_ASSERT(state() == EngineStarting, qDebug() << state());
1600 // This should be set by the constructor or in exitDebugger()
1601 // via initializeVariables()
1602 //QTC_ASSERT(m_debuggingHelperState == DebuggingHelperUninitialized,
1603 // initializeVariables());
1604 //QTC_ASSERT(m_gdbAdapter == 0, delete m_gdbAdapter; m_gdbAdapter = 0);
1606 initializeVariables();
1608 m_startParameters = sp;
1610 delete m_gdbAdapter;
1611 m_gdbAdapter = createAdapter(sp);
1614 if (m_gdbAdapter->dumperHandling() != AbstractGdbAdapter::DumperNotAvailable)
1615 connectDebuggingHelperActions();
1617 m_gdbAdapter->startAdapter();
1620 void GdbEngine::continueInferiorInternal()
1622 QTC_ASSERT(state() == InferiorStopped || state() == InferiorStarting,
1623 qDebug() << state());
1624 setState(InferiorRunningRequested);
1625 postCommand(_("-exec-continue"), RunRequest, CB(handleExecContinue));
1628 void GdbEngine::autoContinueInferior()
1630 continueInferiorInternal();
1631 showStatusMessage(tr("Continuing after temporary stop..."), 1000);
1634 void GdbEngine::continueInferior()
1636 m_manager->resetLocation();
1638 continueInferiorInternal();
1639 showStatusMessage(tr("Running requested..."), 5000);
1642 void GdbEngine::stepExec()
1644 QTC_ASSERT(state() == InferiorStopped, qDebug() << state());
1646 setState(InferiorRunningRequested);
1647 showStatusMessage(tr("Step requested..."), 5000);
1648 if (manager()->isReverseDebugging())
1649 postCommand(_("-reverse-step"), RunRequest, CB(handleExecContinue));
1651 postCommand(_("-exec-step"), RunRequest, CB(handleExecContinue));
1654 void GdbEngine::stepIExec()
1656 QTC_ASSERT(state() == InferiorStopped, qDebug() << state());
1658 setState(InferiorRunningRequested);
1659 showStatusMessage(tr("Step by instruction requested..."), 5000);
1660 if (manager()->isReverseDebugging())
1661 postCommand(_("-reverse-stepi"), RunRequest, CB(handleExecContinue));
1663 postCommand(_("-exec-step-instruction"), RunRequest, CB(handleExecContinue));
1666 void GdbEngine::stepOutExec()
1668 QTC_ASSERT(state() == InferiorStopped, qDebug() << state());
1670 setState(InferiorRunningRequested);
1671 showStatusMessage(tr("Finish function requested..."), 5000);
1672 postCommand(_("-exec-finish"), RunRequest, CB(handleExecContinue));
1675 void GdbEngine::nextExec()
1677 QTC_ASSERT(state() == InferiorStopped, qDebug() << state());
1679 setState(InferiorRunningRequested);
1680 showStatusMessage(tr("Step next requested..."), 5000);
1681 if (manager()->isReverseDebugging())
1682 postCommand(_("-reverse-next"), RunRequest, CB(handleExecContinue));
1685 postCommand(_("-exec-next"), RunRequest, CB(handleExecContinue));
1687 postCommand(_("tbreak \"%2\":%1").arg(lastLine + 1).arg(breakLocation(lastFile)));
1688 postCommand(_("-exec-continue"), RunRequest, CB(handleExecContinue));
1693 void GdbEngine::nextIExec()
1695 QTC_ASSERT(state() == InferiorStopped, qDebug() << state());
1697 setState(InferiorRunningRequested);
1698 showStatusMessage(tr("Step next instruction requested..."), 5000);
1699 if (manager()->isReverseDebugging())
1700 postCommand(_("-reverse-nexti"), RunRequest, CB(handleExecContinue));
1702 postCommand(_("-exec-next-instruction"), RunRequest, CB(handleExecContinue));
1705 void GdbEngine::runToLineExec(const QString &fileName, int lineNumber)
1707 QTC_ASSERT(state() == InferiorStopped, qDebug() << state());
1709 setState(InferiorRunningRequested);
1710 showStatusMessage(tr("Run to line %1 requested...").arg(lineNumber), 5000);
1711 postCommand(_("-exec-until \"%2\":%1").arg(lineNumber).arg(breakLocation(fileName)),
1712 RunRequest, CB(handleExecContinue));
1715 void GdbEngine::runToFunctionExec(const QString &functionName)
1717 QTC_ASSERT(state() == InferiorStopped, qDebug() << state());
1719 postCommand(_("-break-insert -t ") + functionName);
1720 continueInferiorInternal();
1721 //setState(InferiorRunningRequested);
1722 //postCommand(_("-exec-continue"), handleExecRunToFunction);
1723 showStatusMessage(tr("Run to function %1 requested...").arg(functionName), 5000);
1726 void GdbEngine::jumpToLineExec(const QString &fileName, int lineNumber)
1728 QTC_ASSERT(state() == InferiorStopped, qDebug() << state());
1730 frame.file = fileName;
1731 frame.line = lineNumber;
1733 QString loc = breakLocation(fileName);
1734 postCommand(_("tbreak \"%2\":%1").arg(lineNumber).arg(loc));
1735 setState(InferiorRunningRequested);
1736 postCommand(_("jump \"%2\":%1").arg(lineNumber).arg(loc), RunRequest);
1737 // will produce something like
1738 // &"jump \"/home/apoenitz/dev/work/test1/test1.cpp\":242"
1739 // ~"Continuing at 0x4058f3."
1740 // ~"run1 (argc=1, argv=0x7fffbf1f5538) at test1.cpp:242"
1743 gotoLocation(frame, true);
1745 //postCommand(_("jump ") + fileName + ':' + QString::number(lineNumber));
1747 gotoLocation(frame, true);
1748 setBreakpoint(fileName, lineNumber);
1749 setState(InferiorRunningRequested);
1750 postCommand(_("jump ") + fileName + ':' + QString::number(lineNumber), RunRequest);
1755 \fn void GdbEngine::setTokenBarrier()
1756 \brief Discard the results of all pending watch-updating commands.
1758 This method is called at the beginning of all step/next/finish etc.
1760 If non-watch-updating commands with call-backs are still in the pipe,
1764 void GdbEngine::setTokenBarrier()
1766 foreach (const GdbCommand &cookie, m_cookieForToken) {
1767 QTC_ASSERT(!cookie.callback || (cookie.flags & Discardable),
1768 qDebug() << "CMD:" << cookie.command << " CALLBACK:" << cookie.callbackName;
1772 PENDING_DEBUG("\n--- token barrier ---\n");
1773 gdbInputAvailable(LogMisc, _("--- token barrier ---"));
1774 m_oldestAcceptableToken = currentToken();
1777 void GdbEngine::setDebugDebuggingHelpers(const QVariant &on)
1780 debugMessage(_("SWITCHING ON DUMPER DEBUGGING"));
1781 postCommand(_("set unwindonsignal off"));
1782 m_manager->breakByFunction(_("qDumpObjectData440"));
1785 debugMessage(_("SWITCHING OFF DUMPER DEBUGGING"));
1786 postCommand(_("set unwindonsignal on"));
1791 //////////////////////////////////////////////////////////////////////
1793 // Breakpoint specific stuff
1795 //////////////////////////////////////////////////////////////////////
1797 void GdbEngine::breakpointDataFromOutput(BreakpointData *data, const GdbMi &bkpt)
1799 if (!bkpt.isValid())
1803 data->pending = false;
1804 data->bpMultiple = false;
1805 data->bpEnabled = true;
1806 data->bpCondition.clear();
1807 QByteArray file, fullName;
1808 foreach (const GdbMi &child, bkpt.children()) {
1809 if (child.hasName("number")) {
1810 data->bpNumber = _(child.data());
1811 } else if (child.hasName("func")) {
1812 data->bpFuncName = _(child.data());
1813 } else if (child.hasName("addr")) {
1814 // <MULTIPLE> happens in constructors. In this case there are
1815 // _two_ fields named "addr" in the response. On Linux that is...
1816 if (child.data() == "<MULTIPLE>")
1817 data->bpMultiple = true;
1819 data->bpAddress = _(child.data());
1820 } else if (child.hasName("file")) {
1821 file = child.data();
1822 } else if (child.hasName("fullname")) {
1823 fullName = child.data();
1824 } else if (child.hasName("line")) {
1825 data->bpLineNumber = _(child.data());
1826 if (child.data().toInt())
1827 data->markerLineNumber = child.data().toInt();
1828 } else if (child.hasName("cond")) {
1829 data->bpCondition = _(child.data());
1830 // gdb 6.3 likes to "rewrite" conditions. Just accept that fact.
1831 if (data->bpCondition != data->condition && data->conditionsMatch())
1832 data->condition = data->bpCondition;
1833 } else if (child.hasName("enabled")) {
1834 data->bpEnabled = (child.data() == "y");
1835 } else if (child.hasName("pending")) {
1836 data->pending = true;
1837 // Any content here would be interesting only if we did accept
1838 // spontaneously appearing breakpoints (user using gdb commands).
1839 } else if (child.hasName("at")) {
1840 // Happens with (e.g.?) gdb 6.4 symbianelf
1841 QByteArray ba = child.data();
1842 if (ba.startsWith('<') && ba.endsWith('>'))
1843 ba = ba.mid(1, ba.size() - 2);
1844 data->bpFuncName = _(ba);
1847 // This field is not present. Contents needs to be parsed from
1848 // the plain "ignore" response.
1849 //else if (child.hasName("ignore"))
1850 // data->bpIgnoreCount = child.data();
1853 if (!fullName.isEmpty()) {
1854 name = cleanupFullName(QFile::decodeName(fullName));
1855 if (data->markerFileName.isEmpty())
1856 data->markerFileName = name;
1858 name = QFile::decodeName(file);
1859 // Use fullName() once we have a mapping which is more complete than gdb's own ...
1860 // No point in assigning markerFileName for now.
1862 data->bpFileName = name;
1865 QString GdbEngine::breakLocation(const QString &file) const
1867 QTC_ASSERT(!m_sourcesListOutdated, /* */)
1868 QTC_ASSERT(!m_sourcesListUpdating, /* */)
1869 QString where = m_fullToShortName.value(file);
1870 if (where.isEmpty())
1871 return QFileInfo(file).fileName();
1875 void GdbEngine::sendInsertBreakpoint(int index)
1877 const BreakpointData *data = manager()->breakHandler()->at(index);
1879 if (data->funcName.isEmpty()) {
1880 where = data->useFullPath ? data->fileName : breakLocation(data->fileName);
1881 // The argument is simply a C-quoted version of the argument to the
1882 // non-MI "break" command, including the "original" quoting it wants.
1883 where = _("\"\\\"%2\\\":%1\"").arg(data->lineNumber).arg(GdbMi::escapeCString(where));
1885 where = data->funcName;
1888 // set up fallback in case of pending breakpoints which aren't handled
1889 // by the MI interface
1892 cmd = _("-break-insert -l -1 -f ");
1893 else if (m_gdbVersion >= 60800) // Probably some earlier version would work as well ...
1894 cmd = _("-break-insert -f ");
1896 cmd = _("-break-insert ");
1897 //if (!data->condition.isEmpty())
1898 // cmd += _("-c ") + data->condition + _c(' ');
1900 postCommand(cmd, NeedsStop, CB(handleBreakInsert), index);
1903 void GdbEngine::handleBreakList(const GdbResponse &response)
1905 m_sourcesListUpdating = false;
1907 // 45^done,BreakpointTable={nr_rows="2",nr_cols="6",hdr=[
1908 // {width="3",alignment="-1",col_name="number",colhdr="Num"}, ...
1909 // body=[bkpt={number="1",type="breakpoint",disp="keep",enabled="y",
1910 // addr="0x000000000040109e",func="main",file="app.cpp",
1911 // fullname="/home/apoenitz/dev/work/plugintest/app/app.cpp",
1912 // line="11",times="1"},
1913 // bkpt={number="2",type="breakpoint",disp="keep",enabled="y",
1914 // addr="<PENDING>",pending="plugin.cpp:7",times="0"}] ... }
1916 if (response.resultClass == GdbResultDone) {
1917 GdbMi table = response.data.findChild("BreakpointTable");
1918 if (table.isValid())
1919 handleBreakList(table);
1923 void GdbEngine::handleBreakList(const GdbMi &table)
1925 GdbMi body = table.findChild("body");
1927 if (body.isValid()) {
1929 bkpts = body.children();
1932 bkpts = table.children();
1933 // Remove the 'hdr' and artificial items.
1934 for (int i = bkpts.size(); --i >= 0; ) {
1935 int num = bkpts.at(i).findChild("number").data().toInt();
1939 //qDebug() << "LEFT" << bkpts.size() << "BREAKPOINTS";
1942 BreakHandler *handler = manager()->breakHandler();
1943 for (int index = 0; index != bkpts.size(); ++index) {
1944 BreakpointData temp(handler);
1945 breakpointDataFromOutput(&temp, bkpts.at(index));
1946 int found = handler->findBreakpoint(temp);
1948 breakpointDataFromOutput(handler->at(found), bkpts.at(index));
1950 //qDebug() << "CANNOT HANDLE RESPONSE" << bkpts.at(index).toString();
1953 attemptBreakpointSynchronization();
1956 void GdbEngine::handleBreakIgnore(const GdbResponse &response)
1958 int index = response.cookie.toInt();
1961 // ~"Will stop next time breakpoint 2 is reached.\n"
1965 // ~"Will ignore next 12 crossings of breakpoint 2.\n"
1968 // gdb 6.3 does not produce any console output
1969 BreakHandler *handler = manager()->breakHandler();
1970 if (response.resultClass == GdbResultDone && index < handler->size()) {
1971 QString msg = _(response.data.findChild("consolestreamoutput").data());
1972 BreakpointData *data = handler->at(index);
1973 //if (msg.contains(__("Will stop next time breakpoint"))) {
1974 // data->bpIgnoreCount = _("0");
1975 //} else if (msg.contains(__("Will ignore next"))) {
1976 // data->bpIgnoreCount = data->ignoreCount;
1978 // FIXME: this assumes it is doing the right thing...
1979 data->bpIgnoreCount = data->ignoreCount;
1980 handler->updateMarkers();
1984 void GdbEngine::handleBreakCondition(const GdbResponse &response)
1986 int index = response.cookie.toInt();
1987 BreakHandler *handler = manager()->breakHandler();
1988 if (response.resultClass == GdbResultDone) {
1989 // We just assume it was successful. Otherwise we had to parse
1990 // the output stream data.
1991 BreakpointData *data = handler->at(index);
1992 //qDebug() << "HANDLE BREAK CONDITION" << index << data->condition;
1993 data->bpCondition = data->condition;
1995 QByteArray msg = response.data.findChild("msg").data();
1997 if (1 || msg.startsWith("Error parsing breakpoint condition. "
1998 " Will try again when we hit the breakpoint.")) {
1999 BreakpointData *data = handler->at(index);
2000 //qDebug() << "ERROR BREAK CONDITION" << index << data->condition;
2001 data->bpCondition = data->condition;
2004 handler->updateMarkers();
2007 void GdbEngine::handleBreakInsert(const GdbResponse &response)
2009 int index = response.cookie.toInt();
2010 BreakHandler *handler = manager()->breakHandler();
2011 if (response.resultClass == GdbResultDone) {
2012 //#if defined(Q_OS_MAC)
2013 // Interesting only on Mac?
2014 BreakpointData *data = handler->at(index);
2015 GdbMi bkpt = response.data.findChild("bkpt");
2016 breakpointDataFromOutput(data, bkpt);
2018 attemptBreakpointSynchronization();
2020 if (m_gdbVersion < 60800 && !m_isMacGdb) {
2021 // This gdb version doesn't "do" pending breakpoints.
2023 QTC_ASSERT(false, /**/);
2028 void GdbEngine::extractDataFromInfoBreak(const QString &output, BreakpointData *data)
2030 data->bpFileName = _("<MULTIPLE>");
2032 //qDebug() << output;
2033 if (output.isEmpty())
2035 // "Num Type Disp Enb Address What
2036 // 4 breakpoint keep y <MULTIPLE> 0x00000000004066ad
2037 // 4.1 y 0x00000000004066ad in CTorTester
2038 // at /data5/dev/ide/main/tests/manual/gdbdebugger/simple/app.cpp:124
2040 // everything on a single line on Windows for constructors of classes
2041 // within namespaces.
2042 // Sometimes the path is relative too.
2044 // 2 breakpoint keep y <MULTIPLE> 0x0040168e
2045 // 2.1 y 0x0040168e in MainWindow::MainWindow(QWidget*) at mainwindow.cpp:7
2046 // 2.2 y 0x00401792 in MainWindow::MainWindow(QWidget*) at mainwindow.cpp:7
2048 // tested in ../../../tests/auto/debugger/
2049 QRegExp re(_("MULTIPLE.*(0x[0-9a-f]+) in (.*)\\s+at (.*):([\\d]+)([^\\d]|$)"));
2050 re.setMinimal(true);
2052 if (re.indexIn(output) != -1) {
2053 data->bpAddress = re.cap(1);
2054 data->bpFuncName = re.cap(2).trimmed();
2055 data->bpLineNumber = re.cap(4);
2056 QString full = fullName(re.cap(3));
2057 if (full.isEmpty()) {
2058 qDebug() << "NO FULL NAME KNOWN FOR" << re.cap(3);
2059 full = cleanupFullName(re.cap(3));
2060 if (full.isEmpty()) {
2061 qDebug() << "FILE IS NOT RESOLVABLE" << re.cap(3);
2062 full = re.cap(3); // FIXME: wrong, but prevents recursion
2065 data->markerLineNumber = data->bpLineNumber.toInt();
2066 data->markerFileName = full;
2067 data->bpFileName = full;
2069 qDebug() << "COULD NOT MATCH " << re.pattern() << " AND " << output;
2070 data->bpNumber = _("<unavailable>");
2074 void GdbEngine::handleBreakInfo(const GdbResponse &response)
2076 int bpNumber = response.cookie.toInt();
2077 BreakHandler *handler = manager()->breakHandler();
2078 if (response.resultClass == GdbResultDone) {
2079 // Old-style output for multiple breakpoints, presumably in a
2081 int found = handler->findBreakpoint(bpNumber);
2083 QString str = QString::fromLocal8Bit(response.data.findChild("consolestreamoutput").data());
2084 extractDataFromInfoBreak(str, handler->at(found));
2085 attemptBreakpointSynchronization(); // trigger "ready"
2090 void GdbEngine::handleBreakInsert1(const GdbResponse &response)
2092 int index = response.cookie.toInt();
2093 BreakHandler *handler = manager()->breakHandler();
2094 if (response.resultClass == GdbResultDone) {
2095 // Pending breakpoints in dylibs on Mac only?
2096 BreakpointData *data = handler->at(index);
2097 GdbMi bkpt = response.data.findChild("bkpt");
2098 breakpointDataFromOutput(data, bkpt);
2100 qDebug() << "INSERTING BREAKPOINT WITH BASE NAME FAILED. GIVING UP";
2101 BreakpointData *data = handler->at(index);
2102 data->bpNumber = _("<unavailable>");
2104 attemptBreakpointSynchronization(); // trigger "ready"
2107 void GdbEngine::attemptBreakpointSynchronization()
2110 case InferiorStarting:
2111 case InferiorRunningRequested:
2112 case InferiorRunning:
2113 case InferiorStopping:
2114 case InferiorStopped:
2117 //qDebug() << "attempted breakpoint sync in state" << state();
2121 // For best results, we rely on an up-to-date fullname mapping.
2122 // The listing completion will retrigger us, so no futher action is needed.
2123 if (m_sourcesListOutdated) {
2124 reloadSourceFilesInternal();
2126 } else if (m_sourcesListUpdating) {
2130 BreakHandler *handler = manager()->breakHandler();
2132 foreach (BreakpointData *data, handler->takeDisabledBreakpoints()) {
2133 QString bpNumber = data->bpNumber;
2134 if (!bpNumber.trimmed().isEmpty()) {
2135 postCommand(_("-break-disable ") + bpNumber, NeedsStop);
2136 data->bpEnabled = false;
2140 foreach (BreakpointData *data, handler->takeEnabledBreakpoints()) {
2141 QString bpNumber = data->bpNumber;
2142 if (!bpNumber.trimmed().isEmpty()) {
2143 postCommand(_("-break-enable ") + bpNumber, NeedsStop);
2144 data->bpEnabled = true;
2148 foreach (BreakpointData *data, handler->takeRemovedBreakpoints()) {
2149 QString bpNumber = data->bpNumber;
2150 debugMessage(_("DELETING BP %1 IN %2").arg(bpNumber)
2151 .arg(data->markerFileName));
2152 if (!bpNumber.trimmed().isEmpty())
2153 postCommand(_("-break-delete ") + bpNumber, NeedsStop);
2157 for (int index = 0; index != handler->size(); ++index) {
2158 BreakpointData *data = handler->at(index);
2159 if (data->bpNumber.isEmpty()) { // unset breakpoint?
2160 data->bpNumber = _(" "); // Sent, but no feedback yet
2161 sendInsertBreakpoint(index);
2162 } else if (data->bpNumber.toInt()) {
2163 if (data->bpMultiple && data->bpFileName.isEmpty()) {
2164 postCommand(_("info break %1").arg(data->bpNumber),
2165 CB(handleBreakInfo), data->bpNumber.toInt());
2168 // update conditions if needed
2169 if (data->condition != data->bpCondition && !data->conditionsMatch())
2170 postCommand(_("condition %1 %2").arg(data->bpNumber).arg(data->condition),
2171 CB(handleBreakCondition), index);
2172 // update ignorecount if needed
2173 if (data->ignoreCount != data->bpIgnoreCount)
2174 postCommand(_("ignore %1 %2").arg(data->bpNumber).arg(data->ignoreCount),
2175 CB(handleBreakIgnore), index);
2176 if (!data->enabled && data->bpEnabled) {
2177 postCommand(_("-break-disable ") + data->bpNumber, NeedsStop);
2178 data->bpEnabled = false;
2183 handler->updateMarkers();
2187 //////////////////////////////////////////////////////////////////////
2189 // Modules specific stuff
2191 //////////////////////////////////////////////////////////////////////
2193 void GdbEngine::loadSymbols(const QString &moduleName)
2195 // FIXME: gdb does not understand quoted names here (tested with 6.8)
2196 postCommand(_("sharedlibrary ") + dotEscape(moduleName));
2197 reloadModulesInternal();
2200 void GdbEngine::loadAllSymbols()
2202 postCommand(_("sharedlibrary .*"));
2203 reloadModulesInternal();
2206 QList<Symbol> GdbEngine::moduleSymbols(const QString &moduleName)
2209 bool success = false;
2210 QString errorMessage;
2212 const QString nmBinary = _("nm");
2214 proc.start(nmBinary, QStringList() << _("-D") << moduleName);
2215 if (!proc.waitForFinished()) {
2216 errorMessage = tr("Unable to run '%1': %2").arg(nmBinary, proc.errorString());
2219 const QString contents = QString::fromLocal8Bit(proc.readAllStandardOutput());
2220 const QRegExp re(_("([0-9a-f]+)?\\s+([^\\s]+)\\s+([^\\s]+)"));
2221 Q_ASSERT(re.isValid());
2222 foreach (const QString &line, contents.split(_c('\n'))) {
2223 if (re.indexIn(line) != -1) {
2225 symbol.address = re.cap(1);
2226 symbol.state = re.cap(2);
2227 symbol.name = re.cap(3);
2228 rc.push_back(symbol);
2230 qWarning("moduleSymbols: unhandled: %s", qPrintable(line));
2236 qWarning("moduleSymbols: %s\n", qPrintable(errorMessage));
2240 void GdbEngine::reloadModules()
2242 if (state() == InferiorRunning || state() == InferiorStopped)
2243 reloadModulesInternal();
2246 void GdbEngine::reloadModulesInternal()
2248 m_modulesListOutdated = false;
2249 postCommand(_("info shared"), NeedsStop, CB(handleModulesList));
2250 if (m_gdbVersion < 70000 && !m_isMacGdb)
2251 postCommand(_("set stop-on-solib-events 1"));
2254 void GdbEngine::handleModulesList(const GdbResponse &response)
2256 QList<Module> modules;
2257 if (response.resultClass == GdbResultDone) {
2258 // That's console-based output, likely Linux or Windows,
2259 // but we can avoid the #ifdef here.
2260 QString data = QString::fromLocal8Bit(response.data.findChild("consolestreamoutput").data());
2261 QTextStream ts(&data, QIODevice::ReadOnly);
2262 while (!ts.atEnd()) {
2263 QString line = ts.readLine();
2265 QString symbolsRead;
2266 QTextStream ts(&line, QIODevice::ReadOnly);
2267 if (line.startsWith(__("0x"))) {
2268 ts >> module.startAddress >> module.endAddress >> symbolsRead;
2269 module.moduleName = ts.readLine().trimmed();
2270 module.symbolsRead = (symbolsRead == __("Yes"));
2271 modules.append(module);
2272 } else if (line.trimmed().startsWith(__("No"))) {
2273 // gdb 6.4 symbianelf
2275 QTC_ASSERT(symbolsRead == __("No"), continue);
2276 module.moduleName = ts.readLine().trimmed();
2277 modules.append(module);
2280 if (modules.isEmpty()) {
2281 // Mac has^done,shlib-info={num="1",name="dyld",kind="-",
2282 // dyld-addr="0x8fe00000",reason="dyld",requested-state="Y",
2283 // state="Y",path="/usr/lib/dyld",description="/usr/lib/dyld",
2284 // loaded_addr="0x8fe00000",slide="0x0",prefix="__dyld_"},
2285 // shlib-info={...}...
2286 foreach (const GdbMi &item, response.data.children()) {
2288 module.moduleName = QString::fromLocal8Bit(item.findChild("path").data());
2289 module.symbolsRead = (item.findChild("state").data() == "Y");
2290 module.startAddress = _(item.findChild("loaded_addr").data());
2291 //: End address of loaded module
2292 module.endAddress = tr("<unknown>", "address");
2293 modules.append(module);
2297 manager()->modulesHandler()->setModules(modules);
2301 //////////////////////////////////////////////////////////////////////
2303 // Source files specific stuff
2305 //////////////////////////////////////////////////////////////////////
2307 void GdbEngine::reloadSourceFiles()
2309 if ((state() == InferiorRunning || state() == InferiorStopped)
2310 && !m_sourcesListUpdating)
2311 reloadSourceFilesInternal();
2314 void GdbEngine::reloadSourceFilesInternal()
2316 m_sourcesListUpdating = true;
2317 m_sourcesListOutdated = false;
2318 postCommand(_("-file-list-exec-source-files"), NeedsStop, CB(handleQuerySources));
2319 postCommand(_("-break-list"), CB(handleBreakList));
2320 if (m_gdbVersion < 70000 && !m_isMacGdb)
2321 postCommand(_("set stop-on-solib-events 1"));
2325 //////////////////////////////////////////////////////////////////////
2327 // Stack specific stuff
2329 //////////////////////////////////////////////////////////////////////
2331 void GdbEngine::selectThread(int index)
2333 ThreadsHandler *threadsHandler = manager()->threadsHandler();
2334 threadsHandler->setCurrentThread(index);
2336 QList<ThreadData> threads = threadsHandler->threads();
2337 QTC_ASSERT(index < threads.size(), return);
2338 int id = threads.at(index).id;
2339 showStatusMessage(tr("Retrieving data for stack view..."), 10000);
2340 postCommand(_("-thread-select %1").arg(id), CB(handleStackSelectThread));
2343 void GdbEngine::handleStackSelectThread(const GdbResponse &)
2345 QTC_ASSERT(state() == InferiorUnrunnable || state() == InferiorStopped, /**/);
2346 //qDebug("FIXME: StackHandler::handleOutput: SelectThread");
2347 showStatusMessage(tr("Retrieving data for stack view..."), 3000);
2348 manager()->reloadRegisters();
2353 void GdbEngine::reloadFullStack()
2355 QString cmd = _("-stack-list-frames");
2356 postCommand(cmd, WatchUpdate, CB(handleStackListFrames),
2357 QVariant::fromValue<StackCookie>(StackCookie(true, true)));
2360 void GdbEngine::reloadStack(bool forceGotoLocation)
2362 QString cmd = _("-stack-list-frames");
2363 int stackDepth = theDebuggerAction(MaximalStackDepth)->value().toInt();
2364 if (stackDepth && !m_gdbAdapter->isTrkAdapter())
2365 cmd += _(" 0 ") + QString::number(stackDepth);
2366 // FIXME: gdb 6.4 symbianelf likes to be asked twice. The first time it
2367 // returns with "^error,msg="Previous frame identical to this frame
2368 // (corrupt stack?)". Might be related to the fact that we can't
2369 // access the memory belonging to the lower frames. But as we know
2370 // this sometimes happens, ask the second time immediately instead
2371 // of waiting for the first request to fail.
2372 if (m_gdbAdapter->isTrkAdapter())
2373 postCommand(cmd, WatchUpdate);
2374 postCommand(cmd, WatchUpdate, CB(handleStackListFrames),
2375 QVariant::fromValue<StackCookie>(StackCookie(false, forceGotoLocation)));
2378 StackFrame GdbEngine::parseStackFrame(const GdbMi &frameMi, int level)
2380 //qDebug() << "HANDLING FRAME:" << frameMi.toString();
2382 frame.level = level;
2383 GdbMi fullName = frameMi.findChild("fullname");
2384 if (fullName.isValid())
2385 frame.file = cleanupFullName(QFile::decodeName(fullName.data()));
2387 frame.file = QFile::decodeName(frameMi.findChild("file").data());
2388 frame.function = _(frameMi.findChild("func").data());
2389 frame.from = _(frameMi.findChild("from").data());
2390 frame.line = frameMi.findChild("line").data().toInt();
2391 frame.address = _(frameMi.findChild("addr").data());
2395 void GdbEngine::handleStackListFrames(const GdbResponse &response)
2397 bool handleIt = (m_isMacGdb || response.resultClass == GdbResultDone);
2399 // That always happens on symbian gdb with
2400 // ^error,data={msg="Previous frame identical to this frame (corrupt stack?)"
2401 // logstreamoutput="Previous frame identical to this frame (corrupt stack?)\n"
2402 //qDebug() << "LISTING STACK FAILED: " << response.toString();
2406 StackCookie cookie = response.cookie.value<StackCookie>();
2407 QList<StackFrame> stackFrames;
2409 GdbMi stack = response.data.findChild("stack");
2410 if (!stack.isValid()) {
2411 qDebug() << "FIXME: stack:" << stack.toString();
2415 int targetFrame = -1;
2417 int n = stack.childCount();
2418 for (int i = 0; i != n; ++i) {
2419 stackFrames.append(parseStackFrame(stack.childAt(i), i));
2420 const StackFrame &frame = stackFrames.back();
2422 #if defined(Q_OS_WIN)
2423 const bool isBogus =
2424 // Assume this is wrong and points to some strange stl_algobase
2425 // implementation. Happens on Karsten's XP system with Gdb 5.50
2426 (frame.file.endsWith(__("/bits/stl_algobase.h")) && frame.line == 150)
2427 // Also wrong. Happens on Vista with Gdb 5.50
2428 || (frame.function == __("operator new") && frame.line == 151);
2430 // Immediately leave bogus frames.
2431 if (targetFrame == -1 && isBogus) {
2433 setState(InferiorRunningRequested);
2434 postCommand(_("-exec-finish"), RunRequest, CB(handleExecContinue));
2435 showStatusMessage(tr("Jumping out of bogus frame..."), 1000);
2440 // Initialize top frame to the first valid frame.
2441 const bool isValid = frame.isUsable() && !frame.function.isEmpty();
2442 if (isValid && targetFrame == -1)
2446 bool canExpand = !cookie.isFull
2447 && (n >= theDebuggerAction(MaximalStackDepth)->value().toInt());
2448 theDebuggerAction(ExpandStack)->setEnabled(canExpand);
2449 manager()->stackHandler()->setFrames(stackFrames, canExpand);
2451 // We can't jump to any file if we don't have any frames.
2452 if (stackFrames.isEmpty())
2455 // targetFrame contains the top most frame for which we have source
2456 // information. That's typically the frame we'd like to jump to, with
2457 // a few exceptions:
2459 // Always jump to frame #0 when stepping by instruction.
2460 if (theDebuggerBoolSetting(OperateByInstruction))
2463 // If there is no frame with source, jump to frame #0.
2464 if (targetFrame == -1)
2467 // Mac gdb does not add the location to the "stopped" message,
2468 // so the early gotoLocation() was not triggered. Force it here.
2469 // For targetFrame == 0 we already issued a 'gotoLocation'
2470 // when reading the *stopped message.
2471 bool jump = (m_isMacGdb || targetFrame != 0);
2473 manager()->stackHandler()->setCurrentIndex(targetFrame);
2474 if (jump || cookie.gotoLocation) {
2475 const StackFrame &frame = manager()->stackHandler()->currentFrame();
2476 //qDebug() << "GOTO, 2ND ATTEMPT: " << frame.toString() << targetFrame;
2477 gotoLocation(frame, true);
2481 void GdbEngine::activateFrame(int frameIndex)
2483 m_manager->resetLocation();
2484 if (state() != InferiorStopped && state() != InferiorUnrunnable)
2487 StackHandler *stackHandler = manager()->stackHandler();
2488 int oldIndex = stackHandler->currentIndex();
2490 if (frameIndex == stackHandler->stackSize()) {
2495 QTC_ASSERT(frameIndex < stackHandler->stackSize(), return);
2497 if (oldIndex != frameIndex) {
2500 // Assuming the command always succeeds this saves a roundtrip.
2501 // Otherwise the lines below would need to get triggered
2502 // after a response to this -stack-select-frame here.
2503 postCommand(_("-stack-select-frame ") + QString::number(frameIndex));
2505 stackHandler->setCurrentIndex(frameIndex);
2510 gotoLocation(stackHandler->currentFrame(), true);
2513 void GdbEngine::handleStackListThreads(const GdbResponse &response)
2515 int id = response.cookie.toInt();
2516 // "72^done,{thread-ids={thread-id="2",thread-id="1"},number-of-threads="2"}
2517 const QList<GdbMi> items = response.data.findChild("thread-ids").children();
2518 QList<ThreadData> threads;
2519 int currentIndex = -1;
2520 for (int index = 0, n = items.size(); index != n; ++index) {
2522 thread.id = items.at(index).data().toInt();
2523 threads.append(thread);
2524 if (thread.id == id) {
2525 //qDebug() << "SETTING INDEX TO:" << index << " ID:"
2526 // << id << " RECOD:" << response.toString();
2527 currentIndex = index;
2530 ThreadsHandler *threadsHandler = manager()->threadsHandler();
2531 threadsHandler->setThreads(threads);
2532 threadsHandler->setCurrentThread(currentIndex);
2536 //////////////////////////////////////////////////////////////////////
2538 // Register specific stuff
2540 //////////////////////////////////////////////////////////////////////
2542 void GdbEngine::reloadRegisters()
2544 if (!m_registerNamesListed) {
2545 postCommand(_("-data-list-register-names"), CB(handleRegisterListNames));
2546 m_registerNamesListed = true;
2549 if (m_gdbAdapter->isTrkAdapter()) {
2550 // FIXME: remove that special case. This is only to prevent
2551 // gdb from asking for the values of the fixed point registers
2552 postCommand(_("-data-list-register-values x 0 1 2 3 4 5 6 7 8 9 "
2553 "10 11 12 13 14 15 25"),
2554 Discardable, CB(handleRegisterListValues));
2556 postCommand(_("-data-list-register-values x"),
2557 Discardable, CB(handleRegisterListValues));
2561 void GdbEngine::setRegisterValue(int nr, const QString &value)
2563 Register reg = manager()->registerHandler()->registers().at(nr);
2564 //qDebug() << "NOT IMPLEMENTED: CHANGE REGISTER " << nr << reg.name << ":"
2566 postCommand(_("-var-delete \"R@\""));
2567 postCommand(_("-var-create \"R@\" * $%1").arg(reg.name));
2568 postCommand(_("-var-assign \"R@\" %1").arg(value));
2569 postCommand(_("-var-delete \"R@\""));
2570 //postCommand(_("-data-list-register-values d"),
2571 // Discardable, CB(handleRegisterListValues));
2575 void GdbEngine::handleRegisterListNames(const GdbResponse &response)
2577 if (response.resultClass != GdbResultDone) {
2578 m_registerNamesListed = false;
2582 QList<Register> registers;
2583 foreach (const GdbMi &item, response.data.findChild("register-names").children())
2584 registers.append(Register(_(item.data())));
2586 manager()->registerHandler()->setRegisters(registers);
2589 void GdbEngine::handleRegisterListValues(const GdbResponse &response)
2591 if (response.resultClass != GdbResultDone)
2594 QList<Register> registers = manager()->registerHandler()->registers();
2596 // 24^done,register-values=[{number="0",value="0xf423f"},...]
2597 foreach (const GdbMi &item, response.data.findChild("register-values").children()) {
2598 int index = item.findChild("number").data().toInt();
2599 if (index < registers.size()) {
2600 Register ® = registers[index];
2601 QString value = _(item.findChild("value").data());
2602 reg.changed = (value != reg.value);
2607 manager()->registerHandler()->setRegisters(registers);
2611 //////////////////////////////////////////////////////////////////////
2613 // Thread specific stuff
2615 //////////////////////////////////////////////////////////////////////
2617 bool GdbEngine::supportsThreads() const
2619 // FSF gdb 6.3 crashes happily on -thread-list-ids. So don't use it.
2620 // The test below is a semi-random pick, 6.8 works fine
2621 return m_isMacGdb || m_gdbVersion > 60500;
2625 //////////////////////////////////////////////////////////////////////
2627 // Tooltip specific stuff
2629 //////////////////////////////////////////////////////////////////////
2631 static QString m_toolTipExpression;
2632 static QPoint m_toolTipPos;
2634 static QString tooltipINameForExpression(const QString &exp)
2636 // FIXME: 'exp' can contain illegal characters
2637 //return QLatin1String("tooltip.") + exp;
2639 return QLatin1String("tooltip.x");
2642 bool GdbEngine::showToolTip()
2644 WatchHandler *handler = manager()->watchHandler();
2645 WatchModel *model = handler->model(TooltipsWatch);
2646 QString iname = tooltipINameForExpression(m_toolTipExpression);
2647 WatchItem *item = model->findItem(iname, model->rootItem());
2649 hideDebuggerToolTip();
2652 QModelIndex index = model->watchIndex(item);
2653 showDebuggerToolTip(m_toolTipPos, model, index, m_toolTipExpression);
2657 void GdbEngine::setToolTipExpression(const QPoint &mousePos,
2658 TextEditor::ITextEditor *editor, int cursorPos)
2660 if (state() != InferiorStopped || !isCppEditor(editor)) {
2661 //qDebug() << "SUPPRESSING DEBUGGER TOOLTIP, INFERIOR NOT STOPPED/Non Cpp editor";
2665 if (theDebuggerBoolSetting(DebugDebuggingHelpers)) {
2666 // minimize interference
2670 m_toolTipPos = mousePos;
2672 QString exp = cppExpressionAt(editor, cursorPos, &line, &column);
2673 m_toolTipExpression = exp;
2675 // FIXME: enable caching
2676 //if (showToolTip())
2679 if (exp.isEmpty() || exp.startsWith(_c('#'))) {
2680 //QToolTip::hideText();
2684 if (!hasLetterOrNumber(exp)) {
2685 //QToolTip::showText(m_toolTipPos,
2686 // tr("'%1' contains no identifier").arg(exp));
2693 if (exp.startsWith(_c('"')) && exp.endsWith(_c('"'))) {
2694 //QToolTip::showText(m_toolTipPos, tr("String literal %1").arg(exp));
2698 if (exp.startsWith(__("++")) || exp.startsWith(__("--")))
2701 if (exp.endsWith(__("++")) || exp.endsWith(__("--")))
2704 if (exp.startsWith(_c('<')) || exp.startsWith(_c('[')))
2707 if (hasSideEffects(exp)) {
2708 //QToolTip::showText(m_toolTipPos,
2709 // tr("Cowardly refusing to evaluate expression '%1' "
2710 // "with potential side effects").arg(exp));
2714 // Gdb crashes when creating a variable object with the name
2715 // of the type of 'this'
2717 for (int i = 0; i != m_currentLocals.childCount(); ++i) {
2718 if (m_currentLocals.childAt(i).exp == "this") {
2719 qDebug() << "THIS IN ROW" << i;
2720 if (m_currentLocals.childAt(i).type.startsWith(exp)) {
2721 QToolTip::showText(m_toolTipPos,
2722 tr("%1: type of current 'this'").arg(exp));
2723 qDebug() << " TOOLTIP CRASH SUPPRESSED";
2734 toolTip.iname = tooltipINameForExpression(exp);
2735 manager()->watchHandler()->removeData(toolTip.iname);
2736 manager()->watchHandler()->insertData(toolTip);
2740 //////////////////////////////////////////////////////////////////////
2742 // Watch specific stuff
2744 //////////////////////////////////////////////////////////////////////
2746 static void setWatchDataValue(WatchData &data, const GdbMi &mi,
2750 data.setValue(decodeData(mi.data(), encoding));
2752 data.setValueNeeded();
2755 static void setWatchDataEditValue(WatchData &data, const GdbMi &mi)
2758 data.editvalue = mi.data();
2761 static void setWatchDataValueToolTip(WatchData &data, const GdbMi &mi,
2765 data.setValueToolTip(decodeData(mi.data(), encoding));
2768 static void setWatchDataChildCount(WatchData &data, const GdbMi &mi)
2771 data.setHasChildren(mi.data().toInt() > 0);
2774 static void setWatchDataValueEnabled(WatchData &data, const GdbMi &mi)
2776 if (mi.data() == "true")
2777 data.valueEnabled = true;
2778 else if (mi.data() == "false")
2779 data.valueEnabled = false;
2782 static void setWatchDataValueEditable(WatchData &data, const GdbMi &mi)
2784 if (mi.data() == "true")
2785 data.valueEditable = true;
2786 else if (mi.data() == "false")
2787 data.valueEditable = false;
2790 static void setWatchDataExpression(WatchData &data, const GdbMi &mi)
2793 data.exp = _('(' + mi.data() + ')');
2796 static void setWatchDataAddress(WatchData &data, const GdbMi &mi)
2799 data.addr = _(mi.data());
2800 if (data.exp.isEmpty() && !data.addr.startsWith(_("$")))
2801 data.exp = _("(*(") + gdbQuoteTypes(data.type) + _("*)") + data.addr + _c(')');
2805 static void setWatchDataSAddress(WatchData &data, const GdbMi &mi)
2808 data.saddr = _(mi.data());
2811 void GdbEngine::setUseDebuggingHelpers(const QVariant &on)
2813 //qDebug() << "SWITCHING ON/OFF DUMPER DEBUGGING:" << on;
2819 void GdbEngine::setAutoDerefPointers(const QVariant &on)
2826 bool GdbEngine::hasDebuggingHelperForType(const QString &type) const
2828 if (!theDebuggerBoolSetting(UseDebuggingHelpers))
2831 if (m_gdbAdapter->dumperHandling() == AbstractGdbAdapter::DumperNotAvailable) {
2832 // "call" is not possible in gdb when looking at core files
2833 return type == __("QString") || type.endsWith(__("::QString"))
2834 || type == __("QStringList") || type.endsWith(__("::QStringList"));
2837 if (theDebuggerBoolSetting(DebugDebuggingHelpers)
2838 && manager()->stackHandler()->isDebuggingDebuggingHelpers())
2841 if (m_debuggingHelperState != DebuggingHelperAvailable)
2845 return m_dumperHelper.type(type) != QtDumperHelper::UnknownType;
2848 static inline QString msgRetrievingWatchData(int pending)
2850 return GdbEngine::tr("Retrieving data for watch view (%n requests pending)...", 0, pending);
2853 void GdbEngine::runDirectDebuggingHelper(const WatchData &data, bool dumpChildren)
2855 Q_UNUSED(dumpChildren)
2856 QString type = data.type;
2859 if (type == __("QString") || type.endsWith(__("::QString")))
2860 cmd = _("qdumpqstring (&") + data.exp + _c(')');
2861 else if (type == __("QStringList") || type.endsWith(__("::QStringList")))
2862 cmd = _("qdumpqstringlist (&") + data.exp + _c(')');
2866 postCommand(cmd, WatchUpdate, CB(handleDebuggingHelperValue3), var);
2868 showStatusMessage(msgRetrievingWatchData(m_pendingRequests + 1), 10000);
2871 void GdbEngine::runDebuggingHelper(const WatchData &data0, bool dumpChildren)
2873 if (m_debuggingHelperState != DebuggingHelperAvailable) {
2874 runDirectDebuggingHelper(data0, dumpChildren);
2877 WatchData data = data0;
2879 // Avoid endless loops created by faulty dumpers.
2880 QString processedName = QString(_("%1-%2").arg(dumpChildren).arg(data.iname));
2881 if (m_processedNames.contains(processedName)) {
2882 gdbInputAvailable(LogStatus,
2883 _("<Breaking endless loop for %1>").arg(data.iname));
2884 data.setAllUnneeded();
2885 data.setValue(_("<unavailable>"));
2886 data.setHasChildren(false);
2890 m_processedNames.insert(processedName);
2893 QStringList extraArgs;
2894 const QtDumperHelper::TypeData td = m_dumperHelper.typeData(data0.type);
2895 m_dumperHelper.evaluationParameters(data, td, QtDumperHelper::GdbDebugger, ¶ms, &extraArgs);
2897 //int protocol = (data.iname.startsWith("watch") && data.type == "QImage") ? 3 : 2;
2898 //int protocol = data.iname.startsWith("watch") ? 3 : 2;
2899 const int protocol = 2;
2900 //int protocol = isDisplayedIName(data.iname) ? 3 : 2;
2903 if (data.addr.startsWith(__("0x")))
2904 addr = _("(void*)") + data.addr;
2905 else if (data.exp.isEmpty()) // happens e.g. for QAbstractItem
2908 addr = _("&(") + data.exp + _c(')');
2910 sendWatchParameters(params);
2913 QTextStream(&cmd) << "call " << "(void*)qDumpObjectData440(" <<
2914 protocol << ',' << "%1+1" // placeholder for token
2915 <<',' << addr << ',' << (dumpChildren ? "1" : "0")
2916 << ',' << extraArgs.join(QString(_c(','))) << ')';
2918 postCommand(cmd, WatchUpdate | EmbedToken);
2920 showStatusMessage(msgRetrievingWatchData(m_pendingRequests + 1), 10000);
2922 // retrieve response
2923 postCommand(_("p (char*)&qDumpOutBuffer"), WatchUpdate,
2924 CB(handleDebuggingHelperValue2), qVariantFromValue(data));
2927 void GdbEngine::createGdbVariable(const WatchData &data)
2929 if (data.iname == _("local.flist.0")) {
2933 postCommand(_("-var-delete \"%1\"").arg(data.iname), WatchUpdate);
2934 QString exp = data.exp;
2935 if (exp.isEmpty() && data.addr.startsWith(__("0x")))
2936 exp = _("*(") + gdbQuoteTypes(data.type) + _("*)") + data.addr;
2937 QVariant val = QVariant::fromValue<WatchData>(data);
2938 postCommand(_("-var-create \"%1\" * \"%2\"").arg(data.iname).arg(exp),
2939 WatchUpdate, CB(handleVarCreate), val);
2942 void GdbEngine::updateSubItem(const WatchData &data0)
2944 WatchData data = data0;
2946 qDebug() << "UPDATE SUBITEM:" << data.toString();
2948 QTC_ASSERT(data.isValid(), return);
2950 // in any case we need the type first
2951 if (data.isTypeNeeded()) {
2952 // This should only happen if we don't have a variable yet.
2953 // Let's play safe, though.
2954 if (!data.variable.isEmpty()) {
2955 // Update: It does so for out-of-scope watchers.
2957 qDebug() << "FIXME: GdbEngine::updateSubItem:"
2958 << data.toString() << "should not happen";
2960 data.setType(WatchData::msgNotInScope());
2961 data.setValue(WatchData::msgNotInScope());
2962 data.setHasChildren(false);
2967 // The WatchVarCreate handler will receive type information
2968 // and re-insert a WatchData item with correct type, so
2969 // we will not re-enter this bit.
2970 // FIXME: Concurrency issues?
2971 createGdbVariable(data);
2975 // we should have a type now. this is relied upon further below
2976 QTC_ASSERT(!data.type.isEmpty(), return);
2978 // a common case that can be easily solved
2979 if (data.isChildrenNeeded() && isPointerType(data.type)
2980 && !hasDebuggingHelperForType(data.type)) {
2981 // We sometimes know what kind of children pointers have
2983 qDebug() << "IT'S A POINTER";
2986 if (theDebuggerBoolSetting(AutoDerefPointers)) {
2987 // Try automatic dereferentiation
2988 data.exp = _("(*(") + data.exp + _("))");
2989 data.type = data.type + _("."); // FIXME: fragile HACK to avoid recursion
2992 data.setChildrenUnneeded();
2995 data1.iname = data.iname + QLatin1String(".*");
2996 data1.name = QLatin1Char('*') + data.name;
2997 data1.exp = QLatin1String("(*(") + data.exp + QLatin1String("))");
2998 data1.type = stripPointerType(data.type);
2999 data1.setValueNeeded();
3000 data1.setChildrenUnneeded();
3006 if (data.isValueNeeded() && hasDebuggingHelperForType(data.type)) {
3008 qDebug() << "UPDATE SUBITEM: CUSTOMVALUE";
3010 runDebuggingHelper(data, manager()->watchHandler()->isExpandedIName(data.iname));
3015 if (data.isValueNeeded() && data.exp.isEmpty()) {
3017 qDebug() << "UPDATE SUBITEM: NO EXPRESSION?";
3019 data.setError("<no expression given>");
3025 if (data.isValueNeeded() && data.variable.isEmpty()) {
3027 qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR VALUE";
3029 createGdbVariable(data);
3030 // the WatchVarCreate handler will re-insert a WatchData
3031 // item, with valueNeeded() set.
3035 if (data.isValueNeeded()) {
3036 QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
3038 qDebug() << "UPDATE SUBITEM: VALUE";
3040 QString cmd = _("-var-evaluate-expression \"") + data.iname + _c('"');
3041 postCommand(cmd, WatchUpdate, CB(handleEvaluateExpression),
3042 QVariant::fromValue(data));
3046 if (data.isChildrenNeeded() && hasDebuggingHelperForType(data.type)) {
3048 qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN";
3050 runDebuggingHelper(data, true);
3054 if (data.isChildrenNeeded() && data.variable.isEmpty()) {
3056 qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDREN";
3058 createGdbVariable(data);
3059 // the WatchVarCreate handler will re-insert a WatchData
3060 // item, with childrenNeeded() set.
3064 if (data.isChildrenNeeded()) {
3065 QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
3066 QString cmd = _("-var-list-children --all-values \"") + data.variable + _c('"');
3067 postCommand(cmd, WatchUpdate, CB(handleVarListChildren), QVariant::fromValue(data));
3071 if (data.isHasChildrenNeeded() && hasDebuggingHelperForType(data.type)) {
3073 qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN";
3075 runDebuggingHelper(data, manager()->watchHandler()->isExpandedIName(data.iname));
3080 if (data.isHasChildrenNeeded() && data.variable.isEmpty()) {
3082 qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDCOUNT";
3084 createGdbVariable(data);
3085 // the WatchVarCreate handler will re-insert a WatchData
3086 // item, with childrenNeeded() set.
3091 if (data.isHasChildrenNeeded()) {
3092 QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
3093 QString cmd = _("-var-list-children --all-values \"") + data.variable + _c('"');
3094 postCommand(cmd, Discardable, CB(handleVarListChildren), QVariant::fromValue(data));
3098 qDebug() << "FIXME: UPDATE SUBITEM:" << data.toString();
3099 QTC_ASSERT(false, return);
3102 void GdbEngine::updateWatchData(const WatchData &data)
3104 if (isSynchroneous()) {
3105 // This should only be called for fresh expanded items, not for
3106 // items that had their children retrieved earlier.
3107 //qDebug() << "\nUPDATE WATCH DATA: " << data.toString() << "\n";
3109 WatchData data1 = data;
3110 data1.setAllUnneeded();
3114 if (data.iname.endsWith(_(".")))
3117 // Avoid endless loops created by faulty dumpers.
3118 QString processedName = QString(_("%1-%2").arg(1).arg(data.iname));
3119 //qDebug() << "PROCESSED NAMES: " << processedName << m_processedNames;
3120 if (m_processedNames.contains(processedName)) {
3121 WatchData data1 = data;
3122 gdbInputAvailable(LogStatus,
3123 _("<Breaking endless loop for %1>").arg(data1.iname));
3124 data1.setAllUnneeded();
3125 data1.setValue(_("<unavailable>"));
3126 data1.setHasChildren(false);
3130 m_processedNames.insert(processedName);
3135 // Bump requests to avoid model rebuilding during the nested
3136 // updateWatchModel runs.
3137 ++m_pendingRequests;
3138 PENDING_DEBUG("UPDATE WATCH BUMPS PENDING UP TO " << m_pendingRequests);
3140 QMetaObject::invokeMethod(this, "updateWatchDataHelper",
3141 Qt::QueuedConnection, Q_ARG(WatchData, data));
3143 updateWatchDataHelper(data);
3148 void GdbEngine::updateWatchDataHelper(const WatchData &data)
3150 //m_pendingRequests = 0;
3151 PENDING_DEBUG("UPDATE WATCH DATA");
3153 //qDebug() << "##############################################";
3154 qDebug() << "UPDATE MODEL, FOUND INCOMPLETE:";
3155 //qDebug() << data.toString();
3158 updateSubItem(data);
3159 //PENDING_DEBUG("INTERNAL TRIGGERING UPDATE WATCH MODEL");
3160 --m_pendingRequests;
3161 PENDING_DEBUG("UPDATE WATCH DONE BUMPS PENDING DOWN TO " << m_pendingRequests);
3162 if (m_pendingRequests <= 0)
3166 void GdbEngine::rebuildModel()
3168 static int count = 0;
3170 if (!isSynchroneous())
3171 m_processedNames.clear();
3172 PENDING_DEBUG("REBUILDING MODEL" << count);
3173 gdbInputAvailable(LogStatus, _("<Rebuild Watchmodel %1>").arg(count));
3174 showStatusMessage(tr("Finished retrieving data."), 400);
3175 manager()->watchHandler()->endCycle();
3179 static inline double getDumperVersion(const GdbMi &contents)
3181 const GdbMi dumperVersionG = contents.findChild("dumperversion");
3182 if (dumperVersionG.type() != GdbMi::Invalid) {
3184 const double v = QString::fromAscii(dumperVersionG.data()).toDouble(&ok);
3191 void GdbEngine::handleQueryDebuggingHelper(const GdbResponse &response)
3193 const double dumperVersionRequired = 1.0;
3194 //qDebug() << "DATA DUMPER TRIAL:" << response.toString();
3197 QTC_ASSERT(parseConsoleStream(response, &contents), qDebug() << response.toString());
3198 const bool ok = m_dumperHelper.parseQuery(contents, QtDumperHelper::GdbDebugger)
3199 && m_dumperHelper.typeCount();
3201 // Get version and sizes from dumpers. Expression cache
3202 // currently causes errors.
3203 const double dumperVersion = getDumperVersion(contents);
3204 if (dumperVersion < dumperVersionRequired) {
3205 manager()->showQtDumperLibraryWarning(
3206 QtDumperHelper::msgDumperOutdated(dumperVersionRequired, dumperVersion));
3207 m_debuggingHelperState = DebuggingHelperUnavailable;
3210 m_debuggingHelperState = DebuggingHelperAvailable;
3211 const QString successMsg = tr("Dumper version %1, %n custom dumpers found.",
3212 0, m_dumperHelper.typeCount()).arg(dumperVersion);
3213 showStatusMessage(successMsg);
3215 if (!m_dumperInjectionLoad) // Retry if thread has not terminated yet.
3216 m_debuggingHelperState = DebuggingHelperUnavailable;
3217 showStatusMessage(tr("Debugging helpers not found."));
3219 //qDebug() << m_dumperHelper.toString(true);
3220 //qDebug() << m_availableSimpleDebuggingHelpers << "DATA DUMPERS AVAILABLE";
3223 static inline QString arrayFillCommand(const char *array, const QByteArray ¶ms)
3226 sprintf(buf, "set {char[%d]} &%s = {", params.size(), array);
3228 encoded.append(buf);
3229 const int size = params.size();
3230 for (int i = 0; i != size; ++i) {
3231 sprintf(buf, "%d,", int(params[i]));
3232 encoded.append(buf);
3234 encoded[encoded.size() - 1] = '}';
3238 void GdbEngine::sendWatchParameters(const QByteArray ¶ms0)
3240 QByteArray params = params0;
3241 params.append('\0');
3242 const QString inBufferCmd = arrayFillCommand("qDumpInBuffer", params);
3244 params.replace('\0','!');
3245 gdbInputAvailable(LogMisc, QString::fromUtf8(params));
3248 params.append('\0');
3249 const QString outBufferCmd = arrayFillCommand("qDumpOutBuffer", params);
3251 postCommand(inBufferCmd);
3252 postCommand(outBufferCmd);
3255 void GdbEngine::handleVarAssign(const GdbResponse &)
3257 // Everything might have changed, force re-evaluation.
3258 // FIXME: Speed this up by re-using variables and only
3259 // marking values as 'unknown'
3264 // Find the "type" and "displayedtype" children of root and set up type.
3265 void GdbEngine::setWatchDataType(WatchData &data, const GdbMi &item)
3267 if (item.isValid()) {
3268 const QString miData = _(item.data());
3269 if (!data.framekey.isEmpty())
3270 m_varToType[data.framekey] = miData;
3271 data.setType(miData);
3272 } else if (data.type.isEmpty()) {
3273 data.setTypeNeeded();
3277 void GdbEngine::setWatchDataDisplayedType(WatchData &data, const GdbMi &item)
3280 data.displayedType = _(item.data());
3283 void GdbEngine::handleVarCreate(const GdbResponse &response)
3285 WatchData data = response.cookie.value<WatchData>();
3286 // happens e.g. when we already issued a var-evaluate command
3287 if (!data.isValid())
3289 //qDebug() << "HANDLE VARIABLE CREATION:" << data.toString();
3290 if (response.resultClass == GdbResultDone) {
3291 data.variable = data.iname;
3292 setWatchDataType(data, response.data.findChild("type"));
3293 if (manager()->watchHandler()->isExpandedIName(data.iname)
3294 && !response.data.findChild("children").isValid())
3295 data.setChildrenNeeded();
3297 data.setChildrenUnneeded();
3298 setWatchDataChildCount(data, response.data.findChild("numchild"));
3301 data.setError(QString::fromLocal8Bit(response.data.findChild("msg").data()));
3302 if (data.isWatcher()) {
3303 data.value = WatchData::msgNotInScope();
3305 data.setAllUnneeded();
3306 data.setHasChildren(false);
3307 data.valueEnabled = false;
3308 data.valueEditable = false;
3314 void GdbEngine::handleEvaluateExpression(const GdbResponse &response)
3316 WatchData data = response.cookie.value<WatchData>();
3317 QTC_ASSERT(data.isValid(), qDebug() << "HUH?");
3318 if (response.resultClass == GdbResultDone) {
3320 // data.name = response.data.findChild("value").data();
3322 setWatchDataValue(data, response.data.findChild("value"));
3324 data.setError(QString::fromLocal8Bit(response.data.findChild("msg").data()));
3326 //qDebug() << "HANDLE EVALUATE EXPRESSION:" << data.toString();
3328 //updateWatchModel2();
3331 void GdbEngine::handleDebuggingHelperSetup(const GdbResponse &response)
3333 //qDebug() << "CUSTOM SETUP RESULT:" << response.toString();
3334 if (response.resultClass == GdbResultDone) {
3336 QString msg = QString::fromLocal8Bit(response.data.findChild("msg").data());
3337 //qDebug() << "CUSTOM DUMPER SETUP ERROR MESSAGE:" << msg;
3338 showStatusMessage(tr("Custom dumper setup: %1").arg(msg), 10000);
3342 void GdbEngine::handleDebuggingHelperValue2(const GdbResponse &response)
3344 WatchData data = response.cookie.value<WatchData>();
3345 QTC_ASSERT(data.isValid(), return);
3347 //qDebug() << "CUSTOM VALUE RESULT:" << response.toString();
3348 //qDebug() << "FOR DATA:" << data.toString() << response.resultClass;
3349 if (response.resultClass != GdbResultDone) {
3350 qDebug() << "STRANGE CUSTOM DUMPER RESULT DATA:" << data.toString();
3355 if (!parseConsoleStream(response, &contents)) {
3356 data.setError(WatchData::msgNotInScope());
3361 setWatchDataType(data, response.data.findChild("type"));
3362 setWatchDataDisplayedType(data, response.data.findChild("displaytype"));
3363 QList<WatchData> list;
3364 handleChildren(data, contents, &list);
3365 //for (int i = 0; i != list.size(); ++i)
3366 // qDebug() << "READ: " << list.at(i).toString();
3367 manager()->watchHandler()->insertBulkData(list);
3370 void GdbEngine::handleChildren(const WatchData &data0, const GdbMi &item,
3371 QList<WatchData> *list)
3373 //qDebug() << "HANDLE CHILDREN: " << data0.toString() << item.toString();
3374 WatchData data = data0;
3375 if (!manager()->watchHandler()->isExpandedIName(data.iname))
3376 data.setChildrenUnneeded();
3378 GdbMi children = item.findChild("children");
3379 if (children.isValid() || !manager()->watchHandler()->isExpandedIName(data.iname))
3380 data.setChildrenUnneeded();
3382 if (manager()->watchHandler()->isDisplayedIName(data.iname)) {
3383 GdbMi editvalue = item.findChild("editvalue");
3384 if (editvalue.isValid()) {
3385 setWatchDataEditValue(data, editvalue);
3386 manager()->watchHandler()->showEditValue(data);
3389 setWatchDataType(data, item.findChild("type"));
3390 setWatchDataEditValue(data, item.findChild("editvalue"));
3391 setWatchDataExpression(data, item.findChild("exp"));
3392 setWatchDataChildCount(data, item.findChild("numchild"));
3393 setWatchDataValue(data, item.findChild("value"),
3394 item.findChild("valueencoded").data().toInt());
3395 setWatchDataAddress(data, item.findChild("addr"));
3396 setWatchDataSAddress(data, item.findChild("saddr"));
3397 setWatchDataValueToolTip(data, item.findChild("valuetooltip"),
3398 item.findChild("valuetooltipencoded").data().toInt());
3399 setWatchDataValueEnabled(data, item.findChild("valueenabled"));
3400 setWatchDataValueEditable(data, item.findChild("valueeditable"));
3401 //qDebug() << "\nAPPEND TO LIST: " << data.toString() << "\n";
3404 // try not to repeat data too often
3405 WatchData childtemplate;
3406 setWatchDataType(childtemplate, item.findChild("childtype"));
3407 setWatchDataChildCount(childtemplate, item.findChild("childnumchild"));
3408 //qDebug() << "CHILD TEMPLATE:" << childtemplate.toString();
3411 foreach (GdbMi child, children.children()) {
3412 WatchData data1 = childtemplate;
3413 GdbMi name = child.findChild("name");
3415 data1.name = _(name.data());
3417 data1.name = QString::number(i);
3418 data1.iname = data.iname + _c('.') + data1.name;
3419 if (!data1.name.isEmpty() && data1.name.at(0).isDigit())
3420 data1.name = _c('[') + data1.name + _c(']');
3421 QByteArray key = child.findChild("key").data();
3422 if (!key.isEmpty()) {
3423 int encoding = child.findChild("keyencoded").data().toInt();
3424 QString skey = decodeData(key, encoding);
3425 if (skey.size() > 13) {
3426 skey = skey.left(12);
3429 //data1.name += " (" + skey + ")";
3432 handleChildren(data1, child, list);
3437 void GdbEngine::handleDebuggingHelperValue3(const GdbResponse &response)
3439 if (response.resultClass == GdbResultDone) {
3440 WatchData data = response.cookie.value<WatchData>();
3441 QByteArray out = response.data.findChild("consolestreamoutput").data();
3442 while (out.endsWith(' ') || out.endsWith('\n'))
3444 QList<QByteArray> list = out.split(' ');
3445 //qDebug() << "RECEIVED" << response.toString() << "FOR" << data0.toString()
3446 // << " STREAM:" << out;
3447 if (list.isEmpty()) {
3448 //: Value for variable
3449 data.setError(WatchData::msgNotInScope());
3450 data.setAllUnneeded();
3452 } else if (data.type == __("QString")
3453 || data.type.endsWith(__("::QString"))) {
3454 QList<QByteArray> list = out.split(' ');
3456 int l = out.isEmpty() ? 0 : list.size();
3457 for (int i = 0; i < l; ++i)
3458 str.append(list.at(i).toInt());
3459 data.setValue(_c('"') + str + _c('"'));
3460 data.setHasChildren(false);
3461 data.setAllUnneeded();
3463 } else if (data.type == __("QStringList")
3464 || data.type.endsWith(__("::QStringList"))) {
3465 if (out.isEmpty()) {
3466 data.setValue(tr("<0 items>"));
3467 data.setHasChildren(false);
3468 data.setAllUnneeded();
3471 int l = list.size();
3473 data.setValue(tr("<%n items>", 0, l));
3474 data.setHasChildren(!list.empty());
3475 data.setAllUnneeded();
3477 for (int i = 0; i < l; ++i) {
3479 data1.name = _("[%1]").arg(i);
3480 data1.type = data.type.left(data.type.size() - 4);
3481 data1.iname = data.iname + _(".%1").arg(i);
3482 data1.addr = _(list.at(i));
3483 data1.exp = _("((") + gdbQuoteTypes(data1.type) + _("*)") + data1.addr + _c(')');
3484 data1.setHasChildren(false);
3485 data1.setValueNeeded();
3486 QString cmd = _("qdumpqstring (") + data1.exp + _c(')');
3488 var.setValue(data1);
3489 postCommand(cmd, WatchUpdate, CB(handleDebuggingHelperValue3), var);
3493 //: Value for variable
3494 data.setError(WatchData::msgNotInScope());
3495 data.setAllUnneeded();
3499 WatchData data = response.cookie.value<WatchData>();
3500 data.setError(WatchData::msgNotInScope());
3501 data.setAllUnneeded();
3506 void GdbEngine::updateLocals(const QVariant &cookie)
3508 m_pendingRequests = 0;
3509 if (isSynchroneous()) {
3510 m_processedNames.clear();
3511 manager()->watchHandler()->beginCycle();
3512 m_toolTipExpression.clear();
3513 QStringList expanded = m_manager->watchHandler()->expandedINames().toList();
3514 postCommand(_("-interpreter-exec console \"bb %1 %2\"")
3515 .arg(int(theDebuggerBoolSetting(UseDebuggingHelpers)))
3516 .arg(expanded.join(_(","))),
3517 CB(handleStackFrame));
3519 m_processedNames.clear();
3521 PENDING_DEBUG("\nRESET PENDING");
3522 //m_toolTipCache.clear();
3523 m_toolTipExpression.clear();
3524 manager()->watchHandler()->beginCycle();
3526 // Asynchronous load of injected library, initialize in first stop
3527 if (m_dumperInjectionLoad && m_debuggingHelperState == DebuggingHelperLoadTried
3528 && m_dumperHelper.typeCount() == 0
3529 && inferiorPid() > 0)
3530 tryQueryDebuggingHelpers();
3532 QString level = QString::number(currentFrame());
3533 // '2' is 'list with type and value'
3534 QString cmd = _("-stack-list-arguments 2 ") + level + _c(' ') + level;
3535 postCommand(cmd, WatchUpdate, CB(handleStackListArguments));
3536 // '2' is 'list with type and value'
3537 postCommand(_("-stack-list-locals 2"), WatchUpdate,
3538 CB(handleStackListLocals), cookie); // stage 2/2
3542 void GdbEngine::handleStackFrame(const GdbResponse &response)
3544 if (response.resultClass == GdbResultDone) {
3545 QByteArray out = response.data.findChild("consolestreamoutput").data();
3546 while (out.endsWith(' ') || out.endsWith('\n'))
3548 //qDebug() << "SECOND CHUNK: " << out;
3549 int pos = out.indexOf("locals=");
3551 qDebug() << "DISCARDING JUNK AT BEGIN OF RESPONSE: "
3555 GdbMi all("[" + out + "]");
3558 //qDebug() << "\n\n\nALL: " << all.toString() << "\n";
3559 GdbMi locals = all.findChild("locals");
3560 //qDebug() << "\n\n\nLOCALS: " << locals.toString() << "\n";
3561 WatchData *data = manager()->watchHandler()->findItem(_("local"));
3562 QTC_ASSERT(data, return);
3564 QList<WatchData> list;
3565 //foreach (const GdbMi &local, locals.children)
3566 // handleChildren(*data, local, &list);
3567 handleChildren(*data, locals, &list);
3568 //for (int i = 0; i != list.size(); ++i)
3569 // qDebug() << "READ: " << list.at(i).toString();
3570 manager()->watchHandler()->insertBulkData(list);
3573 //manager()->watchHandler()->updateWatchers();
3574 PENDING_DEBUG("AFTER handleStackFrame()");
3575 // FIXME: This should only be used when updateLocals() was
3576 // triggered by expanding an item in the view.
3577 if (m_pendingRequests <= 0) {
3578 PENDING_DEBUG("\n\n .... AND TRIGGERS MODEL UPDATE\n");
3582 QTC_ASSERT(false, /**/);
3586 void GdbEngine::handleStackListArguments(const GdbResponse &response)
3591 // 12^done,stack-args=
3592 // [frame={level="0",args=[
3593 // {name="argc",type="int",value="1"},
3594 // {name="argv",type="char **",value="(char **) 0x7..."}]}]
3596 // 78^done,stack-args=
3597 // {frame={level="0",args={
3599 // {exp="this",value="0x38a2fab0",name="var21",numchild="3",
3600 // type="CurrentDocumentFind * const",typecode="PTR",
3601 // dynamic_type="",in_scope="true",block_start_addr="0x3938e946",
3602 // block_end_addr="0x3938eb2d"},
3604 // {exp="before",value="@0xbfffb9f8: {d = 0x3a7f2a70}",
3605 // name="var22",numchild="1",type="const QString ...} }}}
3607 // In both cases, iterating over the children of stack-args/frame/args
3609 m_currentFunctionArgs.clear();
3610 if (response.resultClass == GdbResultDone) {
3611 const GdbMi list = response.data.findChild("stack-args");
3612 const GdbMi frame = list.findChild("frame");
3613 const GdbMi args = frame.findChild("args");
3614 m_currentFunctionArgs = args.children();
3616 qDebug() << "FIXME: GdbEngine::handleStackListArguments: should not happen"
3617 << response.toString();
3621 void GdbEngine::handleStackListLocals(const GdbResponse &response)
3625 // There could be shadowed variables
3626 QList<GdbMi> locals = response.data.findChild("locals").children();
3627 locals += m_currentFunctionArgs;
3628 QMap<QByteArray, int> seen;
3629 // If desired, retrieve list of uninitialized variables looking at
3630 // the current frame. This is invoked first time after a stop from
3631 // handleStop1, which passes on the frame as cookie. The whole stack
3632 // is not known at this point.
3633 QStringList uninitializedVariables;
3634 if (theDebuggerAction(UseCodeModel)->isChecked()) {
3635 const StackFrame frame = qVariantCanConvert<Debugger::Internal::StackFrame>(response.cookie) ?
3636 qVariantValue<Debugger::Internal::StackFrame>(response.cookie) :
3637 m_manager->stackHandler()->currentFrame();
3638 if (frame.isUsable())
3639 getUninitializedVariables(m_manager->cppCodeModelSnapshot(),
3640 frame.function, frame.file, frame.line,
3641 &uninitializedVariables);
3643 QList<WatchData> list;
3644 foreach (const GdbMi &item, locals) {
3645 const WatchData data = localVariable(item, uninitializedVariables, &seen);
3647 list.push_back(data);
3649 manager()->watchHandler()->insertBulkData(list);
3650 manager()->watchHandler()->updateWatchers();
3653 // Parse a local variable from GdbMi
3654 WatchData GdbEngine::localVariable(const GdbMi &item,
3655 const QStringList &uninitializedVariables,
3656 QMap<QByteArray, int> *seen)
3658 // Local variables of inlined code are reported as
3659 // 26^done,locals={varobj={exp="this",value="",name="var4",exp="this",
3660 // numchild="1",type="const QtSharedPointer::Basic<CPlusPlus::..."}}
3661 // We do not want these at all. Current hypotheses is that those
3662 // "spurious" locals have _two_ "exp" field. Try to filter them:
3666 foreach (const GdbMi &child, item.children())
3667 numExps += int(child.name() == "exp");
3670 name = item.findChild("exp").data();
3672 name = item.findChild("name").data();
3674 const QMap<QByteArray, int>::iterator it = seen->find(name);
3675 if (it != seen->end()) {
3676 const int n = it.value();
3679 QString nam = _(name);
3680 data.iname = _("local.") + nam + QString::number(n + 1);
3681 //: Variable %1 is the variable name, %2 is a simple count
3682 data.name = WatchData::shadowedName(nam, n);
3683 if (uninitializedVariables.contains(data.name)) {
3684 data.setError(WatchData::msgNotInScope());
3687 //: Type of local variable or parameter shadowed by another
3688 //: variable of the same name in a nested block.
3689 setWatchDataValue(data, item.findChild("value"));
3690 data.setType(GdbEngine::tr("<shadowed>"));
3691 data.setHasChildren(false);
3694 seen->insert(name, 1);
3696 QString nam = _(name);
3697 data.iname = _("local.") + nam;
3700 data.framekey = m_currentFrame + data.name;
3701 setWatchDataType(data, item.findChild("type"));
3702 if (uninitializedVariables.contains(data.name)) {
3703 data.setError(WatchData::msgNotInScope());
3706 if (isSynchroneous()) {
3707 setWatchDataValue(data, item.findChild("value"),
3708 item.findChild("valueencoded").data().toInt());
3709 // We know that the complete list of children is
3710 // somewhere in the response.
3711 data.setChildrenUnneeded();
3713 // set value only directly if it is simple enough, otherwise
3714 // pass through the insertData() machinery
3715 if (isIntOrFloatType(data.type) || isPointerType(data.type))
3716 setWatchDataValue(data, item.findChild("value"));
3717 if (isSymbianIntType(data.type)) {
3718 setWatchDataValue(data, item.findChild("value"));
3719 data.setHasChildren(false);
3723 if (!m_manager->watchHandler()->isExpandedIName(data.iname))
3724 data.setChildrenUnneeded();
3725 if (isPointerType(data.type) || data.name == __("this"))
3726 data.setHasChildren(true);
3730 void GdbEngine::insertData(const WatchData &data0)
3732 PENDING_DEBUG("INSERT DATA" << data0.toString());
3733 WatchData data = data0;
3734 if (data.value.startsWith(__("mi_cmd_var_create:"))) {
3735 qDebug() << "BOGUS VALUE:" << data.toString();
3738 manager()->watchHandler()->insertData(data);
3741 void GdbEngine::handleVarListChildrenHelper(const GdbMi &item,
3742 const WatchData &parent)
3744 //qDebug() << "VAR_LIST_CHILDREN: PARENT" << parent.toString();
3745 //qDebug() << "VAR_LIST_CHILDREN: ITEM" << item.toString();
3746 QByteArray exp = item.findChild("exp").data();
3747 QByteArray name = item.findChild("name").data();
3748 if (isAccessSpecifier(_(exp))) {
3749 // suppress 'private'/'protected'/'public' level
3751 data.variable = _(name);
3752 data.iname = parent.iname;
3753 //data.iname = data.variable;
3754 data.exp = parent.exp;
3755 data.setTypeUnneeded();
3756 data.setValueUnneeded();
3757 data.setHasChildrenUnneeded();
3758 data.setChildrenUnneeded();
3759 //qDebug() << "DATA" << data.toString();
3760 QString cmd = _("-var-list-children --all-values \"") + data.variable + _c('"');
3761 //iname += '.' + exp;
3762 postCommand(cmd, WatchUpdate,
3763 CB(handleVarListChildren), QVariant::fromValue(data));
3764 } else if (item.findChild("numchild").data() == "0") {
3765 // happens for structs without data, e.g. interfaces.
3768 data.iname = parent.iname + _c('.') + data.name;
3769 data.variable = _(name);
3770 setWatchDataType(data, item.findChild("type"));
3771 setWatchDataValue(data, item.findChild("value"));
3772 setWatchDataAddress(data, item.findChild("addr"));
3773 setWatchDataSAddress(data, item.findChild("saddr"));
3774 data.setHasChildren(false);
3776 } else if (parent.iname.endsWith(_c('.'))) {
3777 // Happens with anonymous unions
3779 data.iname = _(name);
3780 QString cmd = _("-var-list-children --all-values \"") + data.variable + _c('"');
3781 postCommand(cmd, WatchUpdate,
3782 CB(handleVarListChildren), QVariant::fromValue(data));
3783 } else if (exp == "staticMetaObject") {
3784 // && item.findChild("type").data() == "const QMetaObject")
3785 // FIXME: Namespaces?
3786 // { do nothing } FIXME: make configurable?
3787 // special "clever" hack to avoid clutter in the GUI.
3788 // I am not sure this is a good idea...
3791 data.iname = parent.iname + _c('.') + __(exp);
3792 data.variable = _(name);
3793 setWatchDataType(data, item.findChild("type"));
3794 setWatchDataValue(data, item.findChild("value"));
3795 setWatchDataAddress(data, item.findChild("addr"));
3796 setWatchDataSAddress(data, item.findChild("saddr"));
3797 setWatchDataChildCount(data, item.findChild("numchild"));
3798 if (!manager()->watchHandler()->isExpandedIName(data.iname))
3799 data.setChildrenUnneeded();
3802 if (data.type == data.name) {
3803 if (isPointerType(parent.type)) {
3804 data.exp = _("*(") + parent.exp + _c(')');
3805 data.name = _("*") + parent.name;
3807 // A type we derive from? gdb crashes when creating variables here
3808 data.exp = parent.exp;
3810 } else if (exp.startsWith("*")) {
3812 data.exp = _("*(") + parent.exp + _c(')');
3813 } else if (startsWithDigit(data.name)) {
3814 // An array. No variables needed?
3815 data.name = _c('[') + data.name + _c(']');
3816 data.exp = parent.exp + _('[' + exp + ']');
3817 } else if (0 && parent.name.endsWith(_c('.'))) {
3818 // Happens with anonymous unions
3819 data.exp = parent.exp + data.name;
3820 //data.name = "<anonymous union>";
3821 } else if (exp.isEmpty()) {
3822 // Happens with anonymous unions
3823 data.exp = parent.exp;
3824 data.name = tr("<n/a>");
3825 data.iname = parent.iname + _(".@");
3826 data.type = tr("<anonymous union>");
3828 // A structure. Hope there's nothing else...
3829 data.exp = parent.exp + _c('.') + data.name;
3832 if (hasDebuggingHelperForType(data.type)) {
3833 // we do not trust gdb if we have a custom dumper
3834 data.setValueNeeded();
3835 data.setHasChildrenNeeded();
3838 //qDebug() << "VAR_LIST_CHILDREN: PARENT 3" << parent.toString();
3839 //qDebug() << "VAR_LIST_CHILDREN: APPENDEE" << data.toString();
3844 void GdbEngine::handleVarListChildren(const GdbResponse &response)
3846 //WatchResultCounter dummy(this, WatchVarListChildren);
3847 WatchData data = response.cookie.value<WatchData>();
3848 if (!data.isValid())
3850 if (response.resultClass == GdbResultDone) {
3851 //qDebug() << "VAR_LIST_CHILDREN: PARENT" << data.toString();
3852 GdbMi children = response.data.findChild("children");
3854 foreach (const GdbMi &child, children.children())
3855 handleVarListChildrenHelper(child, data);
3857 if (children.children().isEmpty()) {
3858 // happens e.g. if no debug information is present or
3859 // if the class really has no children
3861 data1.iname = data.iname + _(".child");
3862 //: About variable's value
3863 data1.value = tr("<no information>");
3864 data1.hasChildren = false;
3865 data1.setAllUnneeded();
3867 data.setAllUnneeded();
3869 } else if (!isAccessSpecifier(data.variable.split(_c('.')).last())) {
3870 data.setChildrenUnneeded();
3873 // this skips the spurious "public", "private" etc levels
3877 data.setError(QString::fromLocal8Bit(response.data.findChild("msg").data()));
3882 void GdbEngine::handleChangedItem(QStandardItem *item)
3884 // HACK: Just store the item for the slot
3885 // handleChangedItem(QWidget *widget) below.
3886 QModelIndex index = item->index().sibling(item->index().row(), 0);
3887 //WatchData data = m_currentSet.takeData(iname);
3888 //m_editedData = inameFromItem(m_model.itemFromIndex(index)).exp;
3889 //qDebug() << "HANDLE CHANGED EXPRESSION:" << m_editedData;
3893 void GdbEngine::assignValueInDebugger(const QString &expression, const QString &value)
3895 postCommand(_("-var-delete assign"));
3896 postCommand(_("-var-create assign * ") + expression);
3897 postCommand(_("-var-assign assign ") + value, Discardable, CB(handleVarAssign));
3900 QString GdbEngine::qtDumperLibraryName() const
3902 return m_manager->qtDumperLibraryName();
3905 bool GdbEngine::checkDebuggingHelpers()
3907 if (!manager()->qtDumperLibraryEnabled())
3909 const QString lib = qtDumperLibraryName();
3910 //qDebug() << "DUMPERLIB:" << lib;
3911 const QFileInfo fi(lib);
3913 const QStringList &locations = manager()->qtDumperLibraryLocations();
3914 const QString loc = locations.join(QLatin1String(", "));
3915 const QString msg = tr("The debugging helper library was not found at %1.").arg(loc);
3917 manager()->showQtDumperLibraryWarning(msg);
3923 void GdbEngine::setDebuggingHelperState(DebuggingHelperState s)
3925 m_debuggingHelperState = s;
3928 void GdbEngine::tryLoadDebuggingHelpers()
3930 if (isSynchroneous())
3932 switch (m_debuggingHelperState) {
3933 case DebuggingHelperUninitialized:
3935 case DebuggingHelperLoadTried:
3936 tryQueryDebuggingHelpers();
3938 case DebuggingHelperAvailable:
3939 case DebuggingHelperUnavailable:
3943 if (m_gdbAdapter->dumperHandling() == AbstractGdbAdapter::DumperNotAvailable) {
3944 // Load at least gdb macro based dumpers.
3945 QFile file(_(":/gdb/gdbmacros.txt"));
3946 file.open(QIODevice::ReadOnly);
3947 QByteArray contents = file.readAll();
3948 m_debuggingHelperState = DebuggingHelperLoadTried;
3949 postCommand(_(contents));
3952 if (m_dumperInjectionLoad && inferiorPid() <= 0) // Need PID to inject
3955 PENDING_DEBUG("TRY LOAD CUSTOM DUMPERS");
3956 m_debuggingHelperState = DebuggingHelperUnavailable;
3957 if (!checkDebuggingHelpers())
3960 m_debuggingHelperState = DebuggingHelperLoadTried;
3961 const QString lib = manager()->qtDumperLibraryName();
3962 #if defined(Q_OS_WIN)
3963 if (m_dumperInjectionLoad) {
3964 /// Launch asynchronous remote thread to load.
3965 SharedLibraryInjector injector(inferiorPid());
3966 QString errorMessage;
3967 if (injector.remoteInject(lib, false, &errorMessage)) {
3968 debugMessage(_("Dumper injection loading triggered (%1)...").arg(lib));
3970 debugMessage(_("Dumper loading (%1) failed: %2").arg(lib, errorMessage));
3971 manager()->showQtDumperLibraryWarning(errorMessage);
3972 m_debuggingHelperState = DebuggingHelperUnavailable;
3976 debugMessage(_("Loading dumpers via debugger call (%1)...").arg(lib));
3977 postCommand(_("sharedlibrary .*")); // for LoadLibraryA
3978 //postCommand(_("handle SIGSEGV pass stop print"));
3979 //postCommand(_("set unwindonsignal off"));
3980 postCommand(_("call LoadLibraryA(\"") + GdbMi::escapeCString(lib) + _("\")"),
3981 CB(handleDebuggingHelperSetup));
3982 postCommand(_("sharedlibrary ") + dotEscape(lib));
3984 #elif defined(Q_OS_MAC)
3985 //postCommand(_("sharedlibrary libc")); // for malloc
3986 //postCommand(_("sharedlibrary libdl")); // for dlopen
3987 postCommand(_("call (void)dlopen(\"") + GdbMi::escapeCString(lib) + _("\", " STRINGIFY(RTLD_NOW) ")"),
3988 CB(handleDebuggingHelperSetup));
3989 //postCommand(_("sharedlibrary ") + dotEscape(lib));
3991 //postCommand(_("p dlopen"));
3992 postCommand(_("sharedlibrary libc")); // for malloc
3993 postCommand(_("sharedlibrary libdl")); // for dlopen
3994 postCommand(_("call (void*)dlopen(\"") + GdbMi::escapeCString(lib) + _("\", " STRINGIFY(RTLD_NOW) ")"),
3995 CB(handleDebuggingHelperSetup));
3996 // some older systems like CentOS 4.6 prefer this:
3997 postCommand(_("call (void*)__dlopen(\"") + GdbMi::escapeCString(lib) + _("\", " STRINGIFY(RTLD_NOW) ")"),
3998 CB(handleDebuggingHelperSetup));
3999 postCommand(_("sharedlibrary ") + dotEscape(lib));
4001 if (!m_dumperInjectionLoad)
4002 tryQueryDebuggingHelpers();
4005 void GdbEngine::tryQueryDebuggingHelpers()
4007 // retrieve list of dumpable classes
4008 postCommand(_("call (void*)qDumpObjectData440(1,%1+1,0,0,0,0,0,0)"), EmbedToken);
4009 postCommand(_("p (char*)&qDumpOutBuffer"), CB(handleQueryDebuggingHelper));
4012 void GdbEngine::recheckDebuggingHelperAvailability()
4014 if (m_gdbAdapter->dumperHandling() != AbstractGdbAdapter::DumperNotAvailable) {
4015 // retreive list of dumpable classes
4016 postCommand(_("call (void*)qDumpObjectData440(1,%1+1,0,0,0,0,0,0)"), EmbedToken);
4017 postCommand(_("p (char*)&qDumpOutBuffer"), CB(handleQueryDebuggingHelper));
4021 void GdbEngine::watchPoint(const QPoint &pnt)
4023 //qDebug() << "WATCH " << pnt;
4024 postCommand(_("call (void*)watchPoint(%1,%2)").arg(pnt.x()).arg(pnt.y()),
4025 NeedsStop, CB(handleWatchPoint));
4028 void GdbEngine::handleWatchPoint(const GdbResponse &response)
4030 //qDebug() << "HANDLE WATCH POINT:" << response.toString();
4031 if (response.resultClass == GdbResultDone) {
4032 GdbMi contents = response.data.findChild("consolestreamoutput");
4033 // "$5 = (void *) 0xbfa7ebfc\n"
4034 QString str = _(parsePlainConsoleStream(response));
4035 // "(void *) 0xbfa7ebfc"
4036 QString addr = str.mid(9);
4037 QString ns = m_dumperHelper.qtNamespace();
4038 QString type = ns.isEmpty() ? _("QWidget*") : _("'%1QWidget'*").arg(ns);
4039 QString exp = _("(*(%1)%2)").arg(type).arg(addr);
4040 theDebuggerAction(WatchExpression)->trigger(exp);
4045 struct MemoryAgentCookie
4047 MemoryAgentCookie() : agent(0), address(0) {}
4048 MemoryAgentCookie(MemoryViewAgent *agent_, quint64 address_)
4049 : agent(agent_), address(address_)
4051 QPointer<MemoryViewAgent> agent;
4055 void GdbEngine::fetchMemory(MemoryViewAgent *agent, quint64 addr, quint64 length)
4057 //qDebug() << "GDB MEMORY FETCH" << agent << addr << length;
4058 postCommand(_("-data-read-memory %1 x 1 1 %2").arg(addr).arg(length),
4059 NeedsStop, CB(handleFetchMemory),
4060 QVariant::fromValue(MemoryAgentCookie(agent, addr)));
4063 void GdbEngine::handleFetchMemory(const GdbResponse &response)
4065 // ^done,addr="0x08910c88",nr-bytes="16",total-bytes="16",
4066 // next-row="0x08910c98",prev-row="0x08910c78",next-page="0x08910c98",
4067 // prev-page="0x08910c78",memory=[{addr="0x08910c88",
4068 // data=["1","0","0","0","5","0","0","0","0","0","0","0","0","0","0","0"]}]
4069 MemoryAgentCookie ac = response.cookie.value<MemoryAgentCookie>();
4070 QTC_ASSERT(ac.agent, return);
4072 GdbMi memory = response.data.findChild("memory");
4073 QTC_ASSERT(memory.children().size() <= 1, return);
4074 if (memory.children().isEmpty())
4076 GdbMi memory0 = memory.children().at(0); // we asked for only one 'row'
4077 GdbMi data = memory0.findChild("data");
4078 foreach (const GdbMi &child, data.children()) {
4080 unsigned char c = '?';
4081 c = child.data().toUInt(&ok, 0);
4082 QTC_ASSERT(ok, return);
4085 ac.agent->addLazyData(ac.address, ba);
4089 struct DisassemblerAgentCookie
4091 DisassemblerAgentCookie() : agent(0) {}
4092 DisassemblerAgentCookie(DisassemblerViewAgent *agent_)
4095 QPointer<DisassemblerViewAgent> agent;
4098 void GdbEngine::fetchDisassembler(DisassemblerViewAgent *agent,
4099 const StackFrame &frame)
4101 if (frame.file.isEmpty()) {
4102 fetchDisassemblerByAddress(agent, true);
4104 // Disassemble full function:
4105 QString cmd = _("-data-disassemble -f %1 -l %2 -n -1 -- 1");
4106 postCommand(cmd.arg(frame.file).arg(frame.line),
4107 Discardable, CB(handleFetchDisassemblerByLine),
4108 QVariant::fromValue(DisassemblerAgentCookie(agent)));
4112 void GdbEngine::fetchDisassemblerByAddress(DisassemblerViewAgent *agent,
4115 QTC_ASSERT(agent, return);
4117 quint64 address = agent->address().toULongLong(&ok, 0);
4118 QTC_ASSERT(ok, qDebug() << "ADDRESS: " << agent->address() << address; return);
4119 QString start = QString::number(address - 20, 16);
4120 QString end = QString::number(address + 100, 16);
4121 // -data-disassemble [ -s start-addr -e end-addr ]
4122 // | [ -f filename -l linenum [ -n lines ] ] -- mode
4124 postCommand(_("-data-disassemble -s 0x%1 -e 0x%2 -- 1").arg(start).arg(end),
4125 Discardable, CB(handleFetchDisassemblerByAddress1),
4126 QVariant::fromValue(DisassemblerAgentCookie(agent)));
4128 postCommand(_("-data-disassemble -s 0x%1 -e 0x%2 -- 0").arg(start).arg(end),
4129 Discardable, CB(handleFetchDisassemblerByAddress0),
4130 QVariant::fromValue(DisassemblerAgentCookie(agent)));
4133 static QByteArray parseLine(const GdbMi &line)
4137 QByteArray address = line.findChild("address").data();
4138 //QByteArray funcName = line.findChild("func-name").data();
4139 //QByteArray offset = line.findChild("offset").data();
4140 QByteArray inst = line.findChild("inst").data();
4141 ba += address + QByteArray(15 - address.size(), ' ');
4142 //ba += funcName + "+" + offset + " ";
4143 //ba += QByteArray(30 - funcName.size() - offset.size(), ' ');
4149 QString GdbEngine::parseDisassembler(const GdbMi &lines)
4151 // ^done,data={asm_insns=[src_and_asm_line={line="1243",file=".../app.cpp",
4152 // line_asm_insn=[{address="0x08054857",func-name="main",offset="27",
4153 // inst="call 0x80545b0 <_Z13testQFileInfov>"}]},
4154 // src_and_asm_line={line="1244",file=".../app.cpp",
4155 // line_asm_insn=[{address="0x0805485c",func-name="main",offset="32",
4156 //inst="call 0x804cba1 <_Z11testObject1v>"}]}]}
4158 // ^done,asm_insns=[
4159 // {address="0x0805acf8",func-name="...",offset="25",inst="and $0xe8,%al"},
4160 // {address="0x0805acfa",func-name="...",offset="27",inst="pop %esp"},
4162 QList<QByteArray> fileContents;
4163 bool fileLoaded = false;
4165 ba.reserve(200 * lines.children().size());
4167 // FIXME: Performance?
4168 foreach (const GdbMi &child, lines.children()) {
4169 if (child.hasName("src_and_asm_line")) {
4172 QString fileName = QFile::decodeName(child.findChild("file").data());
4173 QFile file(fullName(fileName));
4174 file.open(QIODevice::ReadOnly);
4175 fileContents = file.readAll().split('\n');
4178 int line = child.findChild("line").data().toInt();
4179 if (line >= 0 && line < fileContents.size())
4180 ba += " " + fileContents.at(line) + '\n';
4181 GdbMi insn = child.findChild("line_asm_insn");
4182 foreach (const GdbMi &line, insn.children())
4183 ba += parseLine(line);
4185 // the non-mixed version
4186 ba += parseLine(child);
4192 void GdbEngine::handleFetchDisassemblerByLine(const GdbResponse &response)
4194 DisassemblerAgentCookie ac = response.cookie.value<DisassemblerAgentCookie>();
4195 QTC_ASSERT(ac.agent, return);
4197 if (response.resultClass == GdbResultDone) {
4198 GdbMi lines = response.data.findChild("asm_insns");
4199 if (lines.children().isEmpty())
4200 fetchDisassemblerByAddress(ac.agent, true);
4201 else if (lines.children().size() == 1
4202 && lines.childAt(0).findChild("line").data() == "0")
4203 fetchDisassemblerByAddress(ac.agent, true);
4205 ac.agent->setContents(parseDisassembler(lines));
4207 // 536^error,msg="mi_cmd_disassemble: Invalid line number"
4208 QByteArray msg = response.data.findChild("msg").data();
4209 if (msg == "mi_cmd_disassemble: Invalid line number")
4210 fetchDisassemblerByAddress(ac.agent, true);
4212 showStatusMessage(tr("Disassembler failed: %1").arg(_(msg)), 5000);
4216 void GdbEngine::handleFetchDisassemblerByAddress1(const GdbResponse &response)
4218 DisassemblerAgentCookie ac = response.cookie.value<DisassemblerAgentCookie>();
4219 QTC_ASSERT(ac.agent, return);
4221 if (response.resultClass == GdbResultDone) {
4222 GdbMi lines = response.data.findChild("asm_insns");
4223 if (lines.children().isEmpty())
4224 fetchDisassemblerByAddress(ac.agent, false);
4226 QString contents = parseDisassembler(lines);
4227 if (ac.agent->contentsCoversAddress(contents)) {
4228 ac.agent->setContents(parseDisassembler(lines));
4230 debugMessage(_("FALL BACK TO NON-MIXED"));
4231 fetchDisassemblerByAddress(ac.agent, false);
4235 // 26^error,msg="Cannot access memory at address 0x801ca308"
4236 QByteArray msg = response.data.findChild("msg").data();
4237 showStatusMessage(tr("Disassembler failed: %1").arg(_(msg)), 5000);
4241 void GdbEngine::handleFetchDisassemblerByAddress0(const GdbResponse &response)
4243 DisassemblerAgentCookie ac = response.cookie.value<DisassemblerAgentCookie>();
4244 QTC_ASSERT(ac.agent, return);
4246 if (response.resultClass == GdbResultDone) {
4247 GdbMi lines = response.data.findChild("asm_insns");
4248 ac.agent->setContents(parseDisassembler(lines));
4250 QByteArray msg = response.data.findChild("msg").data();
4251 showStatusMessage(tr("Disassembler failed: %1").arg(_(msg)), 5000);
4255 void GdbEngine::gotoLocation(const StackFrame &frame, bool setMarker)
4257 // qDebug() << "GOTO " << frame << setMarker;
4258 m_manager->gotoLocation(frame, setMarker);
4262 // Starting up & shutting down
4265 bool GdbEngine::startGdb(const QStringList &args, const QString &gdb, const QString &settingsIdHint)
4267 debugMessage(_("STARTING GDB ") + gdb);
4269 m_gdbProc.disconnect(); // From any previous runs
4271 QString location = gdb;
4272 const QByteArray env = qgetenv("QTC_DEBUGGER_PATH");
4274 location = QString::fromLatin1(env);
4275 if (location.isEmpty())
4276 location = theDebuggerStringSetting(GdbLocation);
4277 QStringList gdbArgs;
4281 m_gdbProc.start(location, gdbArgs);
4283 if (!m_gdbProc.waitForStarted()) {
4284 const QString msg = tr("Unable to start gdb '%1': %2").arg(location, m_gdbProc.errorString());
4285 handleAdapterStartFailed(msg, settingsIdHint);
4289 // Do this only after the process is running, so we get no needless error notifications
4290 connect(&m_gdbProc, SIGNAL(error(QProcess::ProcessError)),
4291 SLOT(handleGdbError(QProcess::ProcessError)));
4292 connect(&m_gdbProc, SIGNAL(finished(int, QProcess::ExitStatus)),
4293 SLOT(handleGdbFinished(int, QProcess::ExitStatus)));
4294 connect(&m_gdbProc, SIGNAL(readyReadStandardOutput()),
4295 SLOT(readGdbStandardOutput()));
4296 connect(&m_gdbProc, SIGNAL(readyReadStandardError()),
4297 SLOT(readGdbStandardError()));
4299 debugMessage(_("GDB STARTED, INITIALIZING IT"));
4301 postCommand(_("show version"), CB(handleShowVersion));
4302 postCommand(_("-interpreter-exec console \"help bb\""),
4303 CB(handleIsSynchroneous));
4304 //postCommand(_("-enable-timings");
4305 postCommand(_("set print static-members off")); // Seemingly doesn't work.
4306 //postCommand(_("set debug infrun 1"));
4307 //postCommand(_("define hook-stop\n-thread-list-ids\n-stack-list-frames\nend"));
4308 //postCommand(_("define hook-stop\nprint 4\nend"));
4309 //postCommand(_("define hookpost-stop\nprint 5\nend"));
4310 //postCommand(_("define hook-call\nprint 6\nend"));
4311 //postCommand(_("define hookpost-call\nprint 7\nend"));
4312 //postCommand(_("set print object on")); // works with CLI, but not MI
4313 //postCommand(_("set step-mode on")); // we can't work with that yes
4314 //postCommand(_("set exec-done-display on"));
4315 //postCommand(_("set print pretty on"));
4316 //postCommand(_("set confirm off"));
4317 //postCommand(_("set pagination off"));
4319 // The following does not work with 6.3.50-20050815 (Apple version gdb-1344)
4320 // (Mac OS 10.6), but does so for gdb-966 (10.5):
4321 //postCommand(_("set print inferior-events 1"));
4323 postCommand(_("set breakpoint pending on"));
4324 postCommand(_("set print elements 10000"));
4326 //postCommand(_("set substitute-path /var/tmp/qt-x11-src-4.5.0 "
4327 // "/home/sandbox/qtsdk-2009.01/qt"));
4329 // one of the following is needed to prevent crashes in gdb on code like:
4330 // template <class T> T foo() { return T(0); }
4331 // int main() { return foo<int>(); }
4332 // (gdb) call 'int foo<int>'()
4333 // /build/buildd/gdb-6.8/gdb/valops.c:2069: internal-error:
4334 postCommand(_("set overload-resolution off"));
4335 //postCommand(_("set demangle-style none"));
4337 // Stop means reenter debugger if this signal happens (implies print).
4338 // Print means print a message if this signal happens.
4339 // Pass means let program see this signal;
4340 // otherwise program doesn't know.
4341 // Pass and Stop may be combined.
4342 // We need "print" as otherwise we would get no feedback whatsoever
4343 // Custom DebuggingHelper crashs which happen regularily for when accessing
4344 // uninitialized variables.
4345 postCommand(_("handle SIGSEGV nopass stop print"));
4347 // This is useful to kill the inferior whenever gdb dies.
4348 //postCommand(_("handle SIGTERM pass nostop print"));
4350 postCommand(_("set unwindonsignal on"));
4351 //postCommand(_("pwd"));
4352 postCommand(_("set width 0"));
4353 postCommand(_("set height 0"));
4356 postCommand(_("-gdb-set inferior-auto-start-cfm off"));
4357 postCommand(_("-gdb-set sharedLibrary load-rules "
4358 "dyld \".*libSystem.*\" all "
4359 "dyld \".*libauto.*\" all "
4360 "dyld \".*AppKit.*\" all "
4361 "dyld \".*PBGDBIntrospectionSupport.*\" all "
4362 "dyld \".*Foundation.*\" all "
4363 "dyld \".*CFDataFormatters.*\" all "
4364 "dyld \".*libobjc.*\" all "
4365 "dyld \".*CarbonDataFormatters.*\" all"));
4368 QString scriptFileName = theDebuggerStringSetting(GdbScriptFile);
4369 if (!scriptFileName.isEmpty()) {
4370 if (QFileInfo(scriptFileName).isReadable()) {
4371 postCommand(_("source ") + scriptFileName);
4373 showMessageBox(QMessageBox::Warning,
4374 tr("Cannot find debugger initialization script"),
4375 tr("The debugger settings point to a script file at '%1' "
4376 "which is not accessible. If a script file is not needed, "
4377 "consider clearing that entry to avoid this warning. "
4378 ).arg(scriptFileName));
4381 if (m_gdbAdapter->dumperHandling() == AbstractGdbAdapter::DumperLoadedByGdbPreload
4382 && checkDebuggingHelpers()) {
4383 QString cmd = _("set environment ");
4384 cmd += _(Debugger::Constants::Internal::LD_PRELOAD_ENV_VAR);
4386 cmd += manager()->qtDumperLibraryName();
4388 m_debuggingHelperState = DebuggingHelperLoadTried;
4393 void GdbEngine::handleGdbError(QProcess::ProcessError error)
4395 debugMessage(_("HANDLE GDB ERROR"));
4397 case QProcess::Crashed:
4398 break; // will get a processExited() as well
4399 // impossible case QProcess::FailedToStart:
4400 case QProcess::ReadError:
4401 case QProcess::WriteError:
4402 case QProcess::Timedout:
4405 setState(EngineShuttingDown, true);
4406 showMessageBox(QMessageBox::Critical, tr("Gdb I/O Error"),
4407 errorMessage(error));
4412 void GdbEngine::handleGdbFinished(int code, QProcess::ExitStatus type)
4414 debugMessage(_("GDB PROCESS FINISHED, status %1, code %2").arg(type).arg(code));
4417 if (state() == EngineShuttingDown) {
4418 m_gdbAdapter->shutdown();
4419 } else if (state() != AdapterStartFailed) {
4420 showMessageBox(QMessageBox::Critical, tr("Unexpected Gdb Exit"),
4421 tr("The gdb process exited unexpectedly (%1).")
4422 .arg((type == QProcess::CrashExit)
4423 ? tr("crashed") : tr("code %1").arg(code)));
4424 m_gdbAdapter->shutdown();
4426 initializeVariables();
4427 setState(DebuggerNotReady, true);
4430 void GdbEngine::handleAdapterStartFailed(const QString &msg, const QString &settingsIdHint)
4432 setState(AdapterStartFailed);
4433 debugMessage(_("ADAPTER START FAILED"));
4434 const QString title = tr("Adapter start failed");
4435 if (settingsIdHint.isEmpty()) {
4436 Core::ICore::instance()->showWarningWithOptions(title, msg);
4438 Core::ICore::instance()->showWarningWithOptions(title, msg, QString(),
4439 _(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY),
4445 void GdbEngine::handleAdapterStarted()
4447 setState(AdapterStarted);
4448 debugMessage(_("ADAPTER SUCCESSFULLY STARTED"));
4450 showStatusMessage(tr("Starting inferior..."));
4451 setState(InferiorStarting);
4452 m_gdbAdapter->startInferior();
4455 void GdbEngine::handleInferiorPrepared()
4457 const QString qtInstallPath = m_startParameters->qtInstallPath;
4458 if (!qtInstallPath.isEmpty()) {
4459 QString qtBuildPath =
4460 #if defined(Q_OS_WIN)
4461 _("C:/qt-greenhouse/Trolltech/Code_less_create_more/Trolltech/Code_less_create_more/Troll/4.6/qt");
4462 #elif defined(Q_OS_MAC)
4465 _("/var/tmp/qt-x11-src-4.6.0");
4467 if (!qtBuildPath.isEmpty())
4468 postCommand(_("set substitute-path %1 %2")
4469 .arg(qtBuildPath).arg(qtInstallPath));
4472 // Initial attempt to set breakpoints
4473 showStatusMessage(tr("Setting breakpoints..."));
4474 attemptBreakpointSynchronization();
4476 if (m_cookieForToken.isEmpty()) {
4477 startInferiorPhase2();
4479 QTC_ASSERT(m_commandsDoneCallback == 0, /**/);
4480 m_commandsDoneCallback = &GdbEngine::startInferiorPhase2;
4484 void GdbEngine::startInferiorPhase2()
4486 debugMessage(_("BREAKPOINTS SET, CONTINUING INFERIOR STARTUP"));
4487 m_gdbAdapter->startInferiorPhase2();
4490 void GdbEngine::handleInferiorStartFailed(const QString &msg)
4492 if (state() == AdapterStartFailed)
4493 return; // Adapter crashed meanwhile, so this notification is meaningless.
4494 debugMessage(_("INFERIOR START FAILED"));
4495 showMessageBox(QMessageBox::Critical, tr("Inferior start failed"), msg);
4496 setState(InferiorStartFailed);
4500 void GdbEngine::handleAdapterCrashed(const QString &msg)
4502 debugMessage(_("ADAPTER CRASHED"));
4504 // The adapter is expected to have cleaned up after itself when we get here,
4505 // so the effect is about the same as AdapterStartFailed => use it.
4506 // Don't bother with state transitions - this can happen in any state and
4507 // the end result is always the same, so it makes little sense to find a
4508 // "path" which does not assert.
4509 setState(AdapterStartFailed, true);
4511 // No point in being friendly here ...
4515 showMessageBox(QMessageBox::Critical, tr("Adapter crashed"), msg);
4518 void GdbEngine::addOptionPages(QList<Core::IOptionsPage*> *opts) const
4520 opts->push_back(new GdbOptionsPage);
4521 opts->push_back(new TrkOptionsPage(m_trkOptions));
4524 void GdbEngine::showMessageBox(int icon, const QString &title, const QString &text)
4526 m_manager->showMessageBox(icon, title, text);
4529 bool GdbEngine::isSynchroneous() const
4531 return m_isSynchroneous;
4538 IDebuggerEngine *createGdbEngine(DebuggerManager *manager)
4540 return new GdbEngine(manager);
4543 } // namespace Internal
4544 } // namespace Debugger
4546 Q_DECLARE_METATYPE(Debugger::Internal::MemoryAgentCookie);
4547 Q_DECLARE_METATYPE(Debugger::Internal::DisassemblerAgentCookie);
4548 Q_DECLARE_METATYPE(Debugger::Internal::GdbMi);