1 /***************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Qt Software Information (qt-info@nokia.com)
10 ** Non-Open Source Usage
12 ** Licensees may use this file in accordance with the Qt Beta Version
13 ** License Agreement, Agreement version 2.2 provided with the Software or,
14 ** alternatively, in accordance with the terms contained in a written
15 ** agreement between you and Nokia.
17 ** GNU General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU General
20 ** Public License versions 2.0 or 3.0 as published by the Free Software
21 ** Foundation and appearing in the file LICENSE.GPL included in the packaging
22 ** of this file. Please review the following information to ensure GNU
23 ** General Public Licensing requirements will be met:
25 ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
26 ** http://www.gnu.org/copyleft/gpl.html.
28 ** In addition, as a special exception, Nokia gives you certain additional
29 ** rights. These rights are described in the Nokia Qt GPL Exception
30 ** version 1.3, included in the file GPL_EXCEPTION.txt in this package.
32 ***************************************************************************/
34 #include "gdbengine.h"
36 #include "debuggerconstants.h"
37 #include "debuggermanager.h"
39 #include "procinterrupt.h"
41 #include "disassemblerhandler.h"
42 #include "breakhandler.h"
43 #include "moduleshandler.h"
44 #include "registerhandler.h"
45 #include "stackhandler.h"
46 #include "watchhandler.h"
47 #include "sourcefileswindow.h"
49 #include "startexternaldialog.h"
50 #include "attachexternaldialog.h"
52 #include <utils/qtcassert.h>
54 #include <QtCore/QDebug>
55 #include <QtCore/QDir>
56 #include <QtCore/QFileInfo>
57 #include <QtCore/QTime>
58 #include <QtCore/QTimer>
60 #include <QtGui/QAction>
61 #include <QtGui/QLabel>
62 #include <QtGui/QMainWindow>
63 #include <QtGui/QMessageBox>
64 #include <QtGui/QToolTip>
66 #if defined(Q_OS_LINUX) || defined(Q_OS_MAC)
71 using namespace Debugger;
72 using namespace Debugger::Internal;
73 using namespace Debugger::Constants;
75 Q_DECLARE_METATYPE(Debugger::Internal::GdbMi);
77 //#define DEBUG_PENDING 1
78 //#define DEBUG_SUBITEM 1
81 # define PENDING_DEBUG(s) qDebug() << s
83 # define PENDING_DEBUG(s)
86 static const QString tooltipIName = "tooltip";
88 ///////////////////////////////////////////////////////////////////////
92 ///////////////////////////////////////////////////////////////////////
96 GdbInvalidCommand = 0,
99 GdbFileExecAndSymbols,
105 GdbExecRunToFunction,
119 GdbTemporaryContinue,
121 BreakCondition = 200,
131 DisassemblerList = 300,
135 RegisterListNames = 500,
138 StackSelectThread = 600,
144 WatchVarAssign = 700, // data changed by user
145 WatchVarListChildren,
147 WatchEvaluateExpression,
149 WatchDumpCustomSetup,
150 WatchDumpCustomValue1, // waiting for gdb ack
151 WatchDumpCustomValue2, // waiting for actual data
152 WatchDumpCustomEditValue,
155 QString dotEscape(QString str)
157 str.replace(' ', '.');
158 str.replace('\\', '.');
159 str.replace('/', '.');
163 QString currentTime()
165 return QTime::currentTime().toString("hh:mm:ss.zzz");
168 static int ¤tToken()
170 static int token = 0;
174 static bool isSkippableFunction(const QString &funcName, const QString &fileName)
176 if (fileName.endsWith("kernel/qobject.cpp"))
178 if (fileName.endsWith("kernel/moc_qobject.cpp"))
180 if (fileName.endsWith("kernel/qmetaobject.cpp"))
182 if (fileName.endsWith(".moc"))
185 if (funcName.endsWith("::qt_metacall"))
191 static bool isLeavableFunction(const QString &funcName, const QString &fileName)
193 if (funcName.endsWith("QObjectPrivate::setCurrentSender"))
195 if (fileName.endsWith("kernel/qmetaobject.cpp")
196 && funcName.endsWith("QMetaObject::methodOffset"))
198 if (fileName.endsWith("kernel/qobject.h"))
200 if (fileName.endsWith("kernel/qobject.cpp")
201 && funcName.endsWith("QObjectConnectionListVector::at"))
203 if (fileName.endsWith("kernel/qobject.cpp")
204 && funcName.endsWith("~QObject"))
206 if (fileName.endsWith("thread/qmutex.cpp"))
208 if (fileName.endsWith("thread/qthread.cpp"))
210 if (fileName.endsWith("thread/qthread_unix.cpp"))
212 if (fileName.endsWith("thread/qmutex.h"))
214 if (fileName.contains("thread/qbasicatomic"))
216 if (fileName.contains("thread/qorderedmutexlocker_p"))
218 if (fileName.contains("arch/qatomic"))
220 if (fileName.endsWith("tools/qvector.h"))
222 if (fileName.endsWith("tools/qlist.h"))
224 if (fileName.endsWith("tools/qhash.h"))
226 if (fileName.endsWith("tools/qmap.h"))
228 if (fileName.endsWith("tools/qstring.h"))
230 if (fileName.endsWith("global/qglobal.h"))
236 static QString startSymbolName()
239 return "WinMainCRTStartup";
251 ///////////////////////////////////////////////////////////////////////
255 ///////////////////////////////////////////////////////////////////////
257 GdbEngine::GdbEngine(DebuggerManager *parent)
260 qq = parent->engineInterface();
261 initializeVariables();
262 initializeConnections();
265 GdbEngine::~GdbEngine()
267 // prevent sending error messages afterwards
268 m_gdbProc.disconnect(this);
271 void GdbEngine::initializeConnections()
273 // Gdb Process interaction
274 connect(&m_gdbProc, SIGNAL(error(QProcess::ProcessError)), this,
275 SLOT(gdbProcError(QProcess::ProcessError)));
276 connect(&m_gdbProc, SIGNAL(readyReadStandardOutput()), this,
277 SLOT(readGdbStandardOutput()));
278 connect(&m_gdbProc, SIGNAL(readyReadStandardError()), this,
279 SLOT(readGdbStandardError()));
280 connect(&m_gdbProc, SIGNAL(finished(int, QProcess::ExitStatus)), q,
281 SLOT(exitDebugger()));
284 connect(&m_outputCollector, SIGNAL(byteDelivery(QByteArray)),
285 SLOT(readDebugeeOutput(QByteArray)));
286 connect(this, SIGNAL(gdbResponseAvailable()),
287 this, SLOT(handleResponse()), Qt::QueuedConnection);
289 connect(this, SIGNAL(gdbOutputAvailable(QString,QString)),
290 q, SLOT(showDebuggerOutput(QString,QString)),
291 Qt::QueuedConnection);
292 connect(this, SIGNAL(gdbInputAvailable(QString,QString)),
293 q, SLOT(showDebuggerInput(QString,QString)),
294 Qt::QueuedConnection);
295 connect(this, SIGNAL(applicationOutputAvailable(QString)),
296 q, SLOT(showApplicationOutput(QString)),
297 Qt::QueuedConnection);
300 void GdbEngine::initializeVariables()
302 m_dataDumperState = DataDumperUninitialized;
304 m_gdbBuildVersion = -1;
306 m_fullToShortName.clear();
307 m_shortToFullName.clear();
310 m_modulesListOutdated = true;
311 m_oldestAcceptableToken = -1;
312 m_outputCodec = QTextCodec::codecForLocale();
313 m_pendingRequests = 0;
314 m_waitingForBreakpointSynchronizationToContinue = false;
315 m_waitingForFirstBreakpointToBeHit = false;
316 m_commandsToRunOnTemporaryBreak.clear();
319 void GdbEngine::gdbProcError(QProcess::ProcessError error)
323 case QProcess::FailedToStart:
324 msg = QString(tr("The Gdb process failed to start. Either the "
325 "invoked program '%1' is missing, or you may have insufficient "
326 "permissions to invoke the program.")).arg(q->settings()->m_gdbCmd);
328 case QProcess::Crashed:
329 msg = tr("The Gdb process crashed some time after starting "
332 case QProcess::Timedout:
333 msg = tr("The last waitFor...() function timed out. "
334 "The state of QProcess is unchanged, and you can try calling "
335 "waitFor...() again.");
337 case QProcess::WriteError:
338 msg = tr("An error occurred when attempting to write "
339 "to the Gdb process. For example, the process may not be running, "
340 "or it may have closed its input channel.");
342 case QProcess::ReadError:
343 msg = tr("An error occurred when attempting to read from "
344 "the Gdb process. For example, the process may not be running.");
347 msg = tr("An unknown error in the Gdb process occurred. "
348 "This is the default return value of error().");
351 q->showStatusMessage(msg);
352 QMessageBox::critical(q->mainWindow(), tr("Error"), msg);
353 // act as if it was closed by the core
357 static void skipSpaces(const char *&from, const char *to)
359 while (from != to && QChar(*from).isSpace())
363 static inline bool isNameChar(char c)
365 // could be 'stopped' or 'shlibs-added'
366 return (c >= 'a' && c <= 'z') || c == '-';
370 static void dump(const char *first, const char *middle, const QString & to)
372 QByteArray ba(first, middle - first);
374 // note that qDebug cuts off output after a certain size... (bug?)
375 qDebug("\n>>>>> %s\n%s\n====\n%s\n<<<<<\n",
376 qPrintable(currentTime()),
377 qPrintable(QString(ba).trimmed()),
378 qPrintable(to.trimmed()));
380 //qDebug() << qPrintable(currentTime())
381 // << " Reading response: " << QString(ba).trimmed() << "\n";
385 static void skipTerminator(const char *&from, const char *to)
387 skipSpaces(from, to);
389 if (from[0] == '(' && from[1] == 'g' && from[3] == 'b' && from[4] == ')')
391 skipSpaces(from, to);
394 void GdbEngine::readDebugeeOutput(const QByteArray &data)
396 emit applicationOutputAvailable(m_outputCodec->toUnicode(
397 data.constData(), data.length(), &m_outputCodecState));
400 void GdbEngine::debugMessage(const QString &msg)
402 emit gdbOutputAvailable("debug:", msg);
405 // called asyncronously as response to Gdb stdout output in
406 // gdbResponseAvailable()
407 void GdbEngine::handleResponse()
409 static QTime lastTime;
411 emit gdbOutputAvailable(" ", currentTime());
412 emit gdbOutputAvailable("stdout:", m_inbuffer);
415 qDebug() // << "#### start response handling #### "
417 << lastTime.msecsTo(QTime::currentTime()) << "ms,"
418 << "buf: " << m_inbuffer.left(1500) << "..."
419 //<< "buf: " << m_inbuffer
420 << "size:" << m_inbuffer.size();
422 //qDebug() << "buf: " << m_inbuffer;
425 lastTime = QTime::currentTime();
428 if (m_inbuffer.isEmpty())
431 const char *from = m_inbuffer.constData();
432 // FIXME: check for line ending in '\n(gdb)\n'
433 const char *to = from + m_inbuffer.size();
436 //const char *oldfrom = from;
438 //skipSpaces(from, to);
439 skipTerminator(from, to);
442 // token is a sequence of numbers
443 for (inner = from; inner != to; ++inner)
444 if (*inner < '0' || *inner > '9')
447 token = QString(QByteArray(from, inner - from)).toInt();
449 //qDebug() << "found token " << token;
453 //qDebug() << "Returning: " << toString();
457 // next char decides kind of record
458 const char c = *from++;
459 //qDebug() << "CODE:" << c;
465 QByteArray asyncClass;
466 for (; from != to; ++from) {
467 const char c = *from;
472 //qDebug() << "ASYNCCLASS" << asyncClass;
475 while (from != to && *from == ',') {
478 data.parseResultOrValue(from, to);
479 if (data.isValid()) {
480 //qDebug() << "parsed response: " << data.toString();
481 record.m_children += data;
482 record.m_type = GdbMi::Tuple;
485 //dump(oldfrom, from, record.toString());
486 skipTerminator(from, to);
487 m_inbuffer = QByteArray(from, to - from);
488 if (asyncClass == "stopped") {
489 handleAsyncOutput(record);
490 } else if (asyncClass == "running") {
491 // Archer has 'thread-id="all"' here
493 } else if (asyncClass == "shlibs-updated") {
494 // MAC announces updated libs
495 } else if (asyncClass == "shlibs-added") {
496 // MAC announces added libs
497 // {shlib-info={num="2", name="libmathCommon.A_debug.dylib",
498 // kind="-", dyld-addr="0x7f000", reason="dyld", requested-state="Y",
499 // state="Y", path="/usr/lib/system/libmathCommon.A_debug.dylib",
500 // description="/usr/lib/system/libmathCommon.A_debug.dylib",
501 // loaded_addr="0x7f000", slide="0x7f000", prefix=""}}
504 qDebug() << "IGNORED ASYNC OUTPUT "
505 << asyncClass << record.toString();
511 QString data = GdbMi::parseCString(from, to);
512 m_pendingConsoleStreamOutput += data;
513 m_inbuffer = QByteArray(from, to - from);
518 QString data = GdbMi::parseCString(from, to);
519 m_pendingTargetStreamOutput += data;
520 m_inbuffer = QByteArray(from, to - from);
525 QString data = GdbMi::parseCString(from, to);
526 m_pendingLogStreamOutput += data;
527 m_inbuffer = QByteArray(from, to - from);
528 // On Windows, the contents seem to depend on the debugger
529 // version and/or OS version used.
530 if (data.startsWith("warning:"))
531 qq->showApplicationOutput(data);
536 GdbResultRecord record;
538 record.token = token;
540 for (inner = from; inner != to; ++inner)
541 if (*inner < 'a' || *inner > 'z')
544 QByteArray resultClass(from, inner - from);
546 if (resultClass == "done")
547 record.resultClass = GdbResultDone;
548 else if (resultClass == "running")
549 record.resultClass = GdbResultRunning;
550 else if (resultClass == "connected")
551 record.resultClass = GdbResultConnected;
552 else if (resultClass == "error")
553 record.resultClass = GdbResultError;
554 else if (resultClass == "exit")
555 record.resultClass = GdbResultExit;
557 record.resultClass = GdbResultUnknown;
560 skipSpaces(from, to);
561 if (from != to && *from == ',') {
563 record.data.parseTuple_helper(from, to);
564 record.data.m_type = GdbMi::Tuple;
565 record.data.m_name = "data";
567 skipSpaces(from, to);
568 skipTerminator(from, to);
570 //qDebug() << "\nLOG STREAM:" + m_pendingLogStreamOutput;
571 //qDebug() << "\nTARGET STREAM:" + m_pendingTargetStreamOutput;
572 //qDebug() << "\nCONSOLE STREAM:" + m_pendingConsoleStreamOutput;
573 record.data.setStreamOutput("logstreamoutput",
574 m_pendingLogStreamOutput);
575 record.data.setStreamOutput("targetstreamoutput",
576 m_pendingTargetStreamOutput);
577 record.data.setStreamOutput("consolestreamoutput",
578 m_pendingConsoleStreamOutput);
579 QByteArray custom = m_customOutputForToken[token];
580 if (!custom.isEmpty())
581 record.data.setStreamOutput("customvaluecontents",
583 //m_customOutputForToken.remove(token);
584 m_pendingLogStreamOutput.clear();
585 m_pendingTargetStreamOutput.clear();
586 m_pendingConsoleStreamOutput.clear();
588 //dump(oldfrom, from, record.toString());
589 m_inbuffer = QByteArray(from, to - from);
590 handleResultRecord(record);
594 qDebug() << "FIXME: UNKNOWN CODE: " << c << " IN " << m_inbuffer;
595 m_inbuffer = QByteArray(from, to - from);
601 //qDebug() << "##### end response handling ####\n\n\n"
602 // << currentTime() << lastTime.msecsTo(QTime::currentTime());
603 lastTime = QTime::currentTime();
607 static void fixMac(QByteArray &out)
609 // HACK: gdb on Mac mixes MI1 and MI2 syntax. Not nice.
610 // it returns: 9^done,locals={{name="a"},{name="w"}}
611 // instead of: 9^done,locals=[{name="a"},{name="w"}]
612 if (!out.contains("locals={{name"))
615 static const QByteArray termArray("(gdb) ");
616 int pos = out.indexOf(termArray);
620 int pos1 = out.indexOf("={{");
624 int pos2 = out.indexOf("]]");
628 if (pos1 < pos && pos2 < pos) {
635 void GdbEngine::readGdbStandardError()
637 qWarning() << "Unexpected gdb stderr:" << m_gdbProc.readAllStandardError();
640 void GdbEngine::readGdbStandardOutput()
642 // This is the function called whenever the Gdb process created
643 // output. As a rule of thumb, stdout contains _real_ Gdb output
644 // as responses to our command
645 // and "spontaneous" events like messages on loaded shared libraries.
646 // OTOH, stderr contains application output produced by qDebug etc.
647 // There is no organized way to pass application stdout output.
649 QByteArray out = m_gdbProc.readAllStandardOutput();
651 //qDebug() << "\n\n\nPLUGIN OUT: '" << out.data() << "'\n\n\n";
657 m_inbuffer.append(out);
658 //QTC_ASSERT(!m_inbuffer.isEmpty(), return);
660 char c = m_inbuffer[m_inbuffer.size() - 1];
661 static const QByteArray termArray("(gdb) ");
662 if (out.indexOf(termArray) == -1 && c != 10 && c != 13) {
663 //qDebug() << "\n\nBuffer not yet filled, waiting for more data to arrive";
664 //qDebug() << m_inbuffer.data() << m_inbuffer.size();
665 //qDebug() << "\n\n";
669 emit gdbResponseAvailable();
672 void GdbEngine::interruptInferior()
674 qq->notifyInferiorStopRequested();
675 if (m_gdbProc.state() == QProcess::NotRunning) {
676 debugMessage("TRYING TO INTERRUPT INFERIOR WITHOUT RUNNING GDB");
677 qq->notifyInferiorExited();
681 if (q->m_attachedPID > 0) {
682 if (!interruptProcess(q->m_attachedPID))
683 // qq->notifyInferiorStopped();
685 debugMessage(QString("CANNOT INTERRUPT %1").arg(q->m_attachedPID));
690 sendCommand("-exec-interrupt", GdbExecInterrupt);
691 //qq->notifyInferiorStopped();
693 if (!interruptChildProcess(m_gdbProc.pid()))
694 // qq->notifyInferiorStopped();
696 debugMessage(QString("CANNOT STOP INFERIOR"));
700 void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
702 int pid = pid0.toInt();
704 debugMessage(QString("Cannot parse PID from %1").arg(pid0));
707 if (pid == q->m_attachedPID)
709 debugMessage(QString("FOUND PID %1").arg(pid));
710 q->m_attachedPID = pid;
711 qq->notifyInferiorPidChanged(pid);
714 void GdbEngine::sendSynchronizedCommand(const QString & command,
715 int type, const QVariant &cookie, StopNeeded needStop)
717 sendCommand(command, type, cookie, needStop, Synchronized);
720 void GdbEngine::sendCommand(const QString &command, int type,
721 const QVariant &cookie, StopNeeded needStop, Synchronization synchronized)
723 if (m_gdbProc.state() == QProcess::NotRunning) {
724 debugMessage("NO GDB PROCESS RUNNING, CMD IGNORED: " + command);
730 PENDING_DEBUG(" TYPE " << type << " INCREMENTS PENDING TO: "
731 << m_pendingRequests << command);
733 PENDING_DEBUG(" UNKNOWN TYPE " << type << " LEAVES PENDING AT: "
734 << m_pendingRequests << command);
738 cmd.synchronized = synchronized;
739 cmd.command = command;
743 if (needStop && q->status() != DebuggerInferiorStopped
744 && q->status() != DebuggerProcessStartingUp) {
745 // queue the commands that we cannot send at once
746 QTC_ASSERT(q->status() == DebuggerInferiorRunning,
747 qDebug() << "STATUS: " << q->status());
748 q->showStatusMessage(tr("Stopping temporarily."));
749 debugMessage("QUEUING COMMAND " + cmd.command);
750 m_commandsToRunOnTemporaryBreak.append(cmd);
752 } else if (!command.isEmpty()) {
754 m_cookieForToken[currentToken()] = cmd;
755 cmd.command = QString::number(currentToken()) + cmd.command;
756 if (cmd.command.contains("%1"))
757 cmd.command = cmd.command.arg(currentToken());
759 m_gdbProc.write(cmd.command.toLatin1() + "\r\n");
760 //emit gdbInputAvailable(QString(), " " + currentTime());
761 //emit gdbInputAvailable(QString(), "[" + currentTime() + "] " + cmd.command);
762 emit gdbInputAvailable(QString(), cmd.command);
766 void GdbEngine::handleResultRecord(const GdbResultRecord &record)
768 //qDebug() << "TOKEN: " << record.token
769 // << " ACCEPTABLE: " << m_oldestAcceptableToken;
771 //qDebug() << "\nRESULT" << record.token << record.toString();
773 int token = record.token;
777 GdbCookie cmd = m_cookieForToken.take(token);
779 // FIXME: this falsely rejects results from the custom dumper recognition
781 if (record.token < m_oldestAcceptableToken && cmd.type >= 300) {
782 //qDebug() << "### SKIPPING OLD RESULT " << record.toString();
783 //QMessageBox::information(m_mainWindow, tr("Skipped"), "xxx");
788 qDebug() << "# handleOutput, "
789 << "cmd type: " << cmd.type
790 << "cmd synchronized: " << cmd.synchronized
791 << "\n record: " << record.toString();
794 // << "\n data: " << record.data.toString(true);
796 if (cmd.type != GdbInvalidCommand)
797 handleResult(record, cmd.type, cmd.cookie);
799 if (cmd.synchronized) {
801 PENDING_DEBUG(" TYPE " << cmd.type << " DECREMENTS PENDING TO: "
802 << m_pendingRequests << cmd.command);
803 if (m_pendingRequests <= 0) {
804 PENDING_DEBUG(" .... AND TRIGGERS MODEL UPDATE");
808 PENDING_DEBUG(" UNKNOWN TYPE " << cmd.type << " LEAVES PENDING AT: "
809 << m_pendingRequests << cmd.command);
813 void GdbEngine::handleResult(const GdbResultRecord & record, int type,
814 const QVariant & cookie)
821 case GdbExecContinue:
824 handleExecRun(record);
831 handleInfoProc(record);
834 handleInfoThreads(record);
838 handleShowVersion(record);
840 case GdbFileExecAndSymbols:
841 handleFileExecAndSymbols(record);
843 case GdbExecRunToFunction:
844 // that should be "^running". We need to handle the resulting
846 //handleExecRunToFunction(record);
848 case GdbExecInterrupt:
849 qq->notifyInferiorStopped();
851 case GdbExecJumpToLine:
852 handleExecJumpToLine(record);
855 handleQueryPwd(record);
857 case GdbQuerySources:
858 handleQuerySources(record);
860 case GdbAsyncOutput2:
861 handleAsyncOutput2(cookie.value<GdbMi>());
864 handleInfoShared(record);
866 case GdbQueryDataDumper1:
867 handleQueryDataDumper1(record);
869 case GdbQueryDataDumper2:
870 handleQueryDataDumper2(record);
872 case GdbTemporaryContinue:
874 q->showStatusMessage(tr("Continuing after temporary stop."));
878 handleBreakList(record);
881 handleBreakInsert(record, cookie.toInt());
884 handleBreakInsert1(record, cookie.toInt());
887 handleBreakInfo(record, cookie.toInt());
889 case BreakEnablePending:
894 handleBreakIgnore(record, cookie.toInt());
897 handleBreakCondition(record, cookie.toInt());
900 case DisassemblerList:
901 handleDisassemblerList(record, cookie.toString());
905 handleModulesList(record);
908 case RegisterListNames:
909 handleRegisterListNames(record);
911 case RegisterListValues:
912 handleRegisterListValues(record);
915 case StackListFrames:
916 handleStackListFrames(record);
918 case StackListThreads:
919 handleStackListThreads(record, cookie.toInt());
921 case StackSelectThread:
922 handleStackSelectThread(record, cookie.toInt());
924 case StackListLocals:
925 handleStackListLocals(record);
927 case StackListArguments:
928 handleStackListArguments(record);
931 case WatchVarListChildren:
932 handleVarListChildren(record, cookie.value<WatchData>());
935 handleVarCreate(record, cookie.value<WatchData>());
940 case WatchEvaluateExpression:
941 handleEvaluateExpression(record, cookie.value<WatchData>());
944 handleToolTip(record, cookie.toString());
946 case WatchDumpCustomValue1:
947 handleDumpCustomValue1(record, cookie.value<WatchData>());
949 case WatchDumpCustomValue2:
950 handleDumpCustomValue2(record, cookie.value<WatchData>());
952 case WatchDumpCustomSetup:
953 handleDumpCustomSetup(record);
957 debugMessage(QString("FIXME: GdbEngine::handleResult: "
958 "should not happen %1").arg(type));
963 void GdbEngine::executeDebuggerCommand(const QString &command)
965 //createGdbProcessIfNeeded();
966 if (m_gdbProc.state() == QProcess::NotRunning) {
967 debugMessage("NO GDB PROCESS RUNNING, PLAIN CMD IGNORED: " + command);
972 cmd.command = command;
975 emit gdbInputAvailable(QString(), cmd.command);
976 m_gdbProc.write(cmd.command.toLatin1() + "\r\n");
979 void GdbEngine::handleQueryPwd(const GdbResultRecord &record)
981 // FIXME: remove this special case as soon as 'pwd'
982 // is supported by MI
983 //qDebug() << "PWD OUTPUT:" << record.toString();
984 // Gdb responses _unless_ we get an error first.
985 if (record.resultClass == GdbResultDone) {
987 // "5^done,{logstreamoutput="pwd ",consolestreamoutput
988 // ="Working directory /home/apoenitz/dev/work/test1. "}
989 m_pwd = record.data.findChild("consolestreamoutput").data();
990 int pos = m_pwd.indexOf("Working directory");
991 m_pwd = m_pwd.mid(pos + 18);
992 m_pwd = m_pwd.trimmed();
993 if (m_pwd.endsWith('.'))
997 // ~"Working directory C:\\Users\\Thomas\\Documents\\WBTest3\\debug.\n"
998 m_pwd = record.data.findChild("consolestreamoutput").data();
999 m_pwd = m_pwd.trimmed();
1001 debugMessage("PWD RESULT: " + m_pwd);
1005 void GdbEngine::handleQuerySources(const GdbResultRecord &record)
1007 if (record.resultClass == GdbResultDone) {
1008 QMap<QString, QString> oldShortToFull = m_shortToFullName;
1009 m_shortToFullName.clear();
1010 m_fullToShortName.clear();
1011 // "^done,files=[{file="../../../../bin/gdbmacros/gdbmacros.cpp",
1012 // fullname="/data5/dev/ide/main/bin/gdbmacros/gdbmacros.cpp"},
1013 GdbMi files = record.data.findChild("files");
1014 foreach (const GdbMi &item, files.children()) {
1015 QString fileName = item.findChild("file").data();
1016 GdbMi fullName = item.findChild("fullname");
1017 QString full = fullName.data();
1019 full = QDir::cleanPath(full);
1021 if (fullName.isValid() && QFileInfo(full).isReadable()) {
1022 //qDebug() << "STORING 2: " << fileName << full;
1023 m_shortToFullName[fileName] = full;
1024 m_fullToShortName[full] = fileName;
1027 if (m_shortToFullName != oldShortToFull)
1028 qq->sourceFileWindow()->setSourceFiles(m_shortToFullName);
1032 void GdbEngine::handleInfoThreads(const GdbResultRecord &record)
1034 if (record.resultClass == GdbResultDone) {
1035 // FIXME: use something more robust
1036 // WIN: * 3 Thread 2312.0x4d0 0x7c91120f in ?? ()
1037 // LINUX: * 1 Thread 0x7f466273c6f0 (LWP 21455) 0x0000000000404542 in ...
1038 QRegExp re(QLatin1String("Thread (\\d+)\\.0x.* in"));
1039 QString data = record.data.findChild("consolestreamoutput").data();
1040 if (re.indexIn(data) != -1)
1041 maybeHandleInferiorPidChanged(re.cap(1));
1045 void GdbEngine::handleInfoProc(const GdbResultRecord &record)
1047 if (record.resultClass == GdbResultDone) {
1048 #if defined(Q_OS_MAC)
1049 //^done,process-id="85075"
1050 maybeHandleInferiorPidChanged(record.data.findChild("process-id").data());
1053 #if defined(Q_OS_LINUX) || defined(Q_OS_WIN)
1054 // FIXME: use something more robust
1055 QRegExp re(QLatin1String("process (\\d+)"));
1056 QString data = record.data.findChild("consolestreamoutput").data();
1057 if (re.indexIn(data) != -1)
1058 maybeHandleInferiorPidChanged(re.cap(1));
1063 void GdbEngine::handleInfoShared(const GdbResultRecord &record)
1065 if (record.resultClass == GdbResultDone) {
1066 // let the modules handler do the parsing
1067 handleModulesList(record);
1071 void GdbEngine::handleExecJumpToLine(const GdbResultRecord &record)
1073 // FIXME: remove this special case as soon as 'jump'
1074 // is supported by MI
1075 // "&"jump /home/apoenitz/dev/work/test1/test1.cpp:242"
1076 // ~"Continuing at 0x4058f3."
1077 // ~"run1 (argc=1, argv=0x7fffb213a478) at test1.cpp:242"
1080 qq->notifyInferiorStopped();
1081 q->showStatusMessage(tr("Jumped. Stopped."));
1082 QString output = record.data.findChild("logstreamoutput").data();
1083 if (!output.isEmpty())
1085 QString fileAndLine = output.section(' ', 1, 1);
1086 QString file = fileAndLine.section(':', 0, 0);
1087 int line = fileAndLine.section(':', 1, 1).toInt();
1088 q->gotoLocation(file, line, true);
1091 void GdbEngine::handleExecRunToFunction(const GdbResultRecord &record)
1093 // FIXME: remove this special case as soon as there's a real
1094 // reason given when the temporary breakpoint is hit.
1095 // reight now we get:
1096 // 14*stopped,thread-id="1",frame={addr="0x0000000000403ce4",
1097 // func="foo",args=[{name="str",value="@0x7fff0f450460"}],
1098 // file="main.cpp",fullname="/tmp/g/main.cpp",line="37"}
1099 qq->notifyInferiorStopped();
1100 q->showStatusMessage(tr("Run to Function finished. Stopped."));
1101 GdbMi frame = record.data.findChild("frame");
1102 QString file = frame.findChild("fullname").data();
1103 int line = frame.findChild("line").data().toInt();
1104 qDebug() << "HIT: " << file << line << " IN " << frame.toString()
1105 << " -- " << record.toString();
1106 q->gotoLocation(file, line, true);
1109 static bool isExitedReason(const QString &reason)
1111 return reason == QLatin1String("exited-normally") // inferior exited normally
1112 || reason == QLatin1String("exited-signalled") // inferior exited because of a signal
1113 //|| reason == QLatin1String("signal-received") // inferior received signal
1114 || reason == QLatin1String("exited"); // inferior exited
1117 static bool isStoppedReason(const QString &reason)
1119 return reason == QLatin1String("function-finished") // -exec-finish
1120 || reason == QLatin1String("signal-received") // handled as "isExitedReason"
1121 || reason == QLatin1String("breakpoint-hit") // -exec-continue
1122 || reason == QLatin1String("end-stepping-range") // -exec-next, -exec-step
1123 || reason == QLatin1String("location-reached") // -exec-until
1124 || reason == QLatin1String("access-watchpoint-trigger")
1125 || reason == QLatin1String("read-watchpoint-trigger")
1132 void GdbEngine::handleAsyncOutput(const GdbMi &data)
1134 const QString reason = data.findChild("reason").data();
1136 if (isExitedReason(reason)) {
1137 qq->notifyInferiorExited();
1138 QString msg = "Program exited normally";
1139 if (reason == "exited") {
1140 msg = "Program exited with exit code "
1141 + data.findChild("exit-code").toString();
1142 } else if (reason == "exited-signalled") {
1143 msg = "Program exited after receiving signal "
1144 + data.findChild("signal-name").toString();
1145 } else if (reason == "signal-received") {
1146 msg = "Program exited after receiving signal "
1147 + data.findChild("signal-name").toString();
1149 q->showStatusMessage(msg);
1150 // FIXME: shouldn't this use a statis change?
1151 debugMessage("CALLING PARENT EXITDEBUGGER");
1157 //MAC: bool isFirstStop = data.findChild("bkptno").data() == "1";
1158 //!MAC: startSymbolName == data.findChild("frame").findChild("func")
1159 if (m_waitingForFirstBreakpointToBeHit) {
1160 // If the executable dies already that early we might get something
1161 // like stdout:49*stopped,reason="exited",exit-code="0177"
1162 // This is handled now above.
1164 qq->notifyInferiorStopped();
1165 m_waitingForFirstBreakpointToBeHit = false;
1167 // that's the "early stop"
1169 #if defined(Q_OS_WIN)
1170 sendCommand("info thread", GdbInfoThreads);
1172 #if defined(Q_OS_LINUX)
1173 sendCommand("info proc", GdbInfoProc);
1175 #if defined(Q_OS_MAC)
1176 sendCommand("info pid", GdbInfoProc);
1178 reloadSourceFiles();
1179 tryLoadCustomDumpers();
1182 // intentionally after tryLoadCustomDumpers(),
1183 // otherwise we'd interupt solib loading.
1184 if (qq->wantsAllPluginBreakpoints()) {
1185 sendCommand("set auto-solib-add on");
1186 sendCommand("set stop-on-solib-events 0");
1187 sendCommand("sharedlibrary .*");
1188 } else if (qq->wantsSelectedPluginBreakpoints()) {
1189 sendCommand("set auto-solib-add on");
1190 sendCommand("set stop-on-solib-events 1");
1191 sendCommand("sharedlibrary "+qq->selectedPluginBreakpointsPattern());
1192 } else if (qq->wantsNoPluginBreakpoints()) {
1193 // should be like that already
1194 sendCommand("set auto-solib-add off");
1195 sendCommand("set stop-on-solib-events 0");
1198 // nicer to see a bit of the world we live in
1200 // this will "continue" if done
1201 m_waitingForBreakpointSynchronizationToContinue = true;
1202 //QTimer::singleShot(0, this, SLOT(attemptBreakpointSynchronization()));
1203 attemptBreakpointSynchronization();
1207 if (!m_commandsToRunOnTemporaryBreak.isEmpty()) {
1208 QTC_ASSERT(q->status() == DebuggerInferiorStopRequested,
1209 qDebug() << "STATUS: " << q->status())
1210 qq->notifyInferiorStopped();
1211 q->showStatusMessage(tr("Temporarily stopped."));
1213 foreach (const GdbCookie &cmd, m_commandsToRunOnTemporaryBreak) {
1214 debugMessage(QString("RUNNING QUEUED COMMAND %1 %2")
1215 .arg(cmd.command).arg(cmd.type));
1216 sendCommand(cmd.command, cmd.type, cmd.cookie);
1218 sendCommand("p temporaryStop", GdbTemporaryContinue);
1219 m_commandsToRunOnTemporaryBreak.clear();
1220 q->showStatusMessage(tr("Handling queued commands."));
1224 QString msg = data.findChild("consolestreamoutput").data();
1225 if (msg.contains("Stopped due to shared library event") || reason.isEmpty()) {
1226 if (qq->wantsSelectedPluginBreakpoints()) {
1227 debugMessage("SHARED LIBRARY EVENT: " + data.toString());
1228 debugMessage("PATTERN: " + qq->selectedPluginBreakpointsPattern());
1229 sendCommand("sharedlibrary " + qq->selectedPluginBreakpointsPattern());
1231 q->showStatusMessage(tr("Loading %1...").arg(QString(data.toString())));
1234 m_modulesListOutdated = true;
1238 // seen on XP after removing a breakpoint while running
1239 // stdout:945*stopped,reason="signal-received",signal-name="SIGTRAP",
1240 // signal-meaning="Trace/breakpoint trap",thread-id="2",
1241 // frame={addr="0x7c91120f",func="ntdll!DbgUiConnectToDbg",
1242 // args=[],from="C:\\WINDOWS\\system32\\ntdll.dll"}
1243 if (reason == "signal-received"
1244 && data.findChild("signal-name").toString() == "SIGTRAP") {
1249 //tryLoadCustomDumpers();
1251 // jump over well-known frames
1252 static int stepCounter = 0;
1253 if (qq->skipKnownFrames()) {
1254 if (reason == "end-stepping-range" || reason == "function-finished") {
1255 GdbMi frame = data.findChild("frame");
1256 //debugMessage(frame.toString());
1257 m_currentFrame = frame.findChild("addr").data() + '%' +
1258 frame.findChild("func").data() + '%';
1260 QString funcName = frame.findChild("func").data();
1261 QString fileName = frame.findChild("file").data();
1262 if (isLeavableFunction(funcName, fileName)) {
1263 //debugMessage("LEAVING" + funcName);
1269 if (isSkippableFunction(funcName, fileName)) {
1270 //debugMessage("SKIPPING" + funcName);
1276 // qDebug() << "STEPCOUNTER:" << stepCounter;
1281 if (isStoppedReason(reason) || reason.isEmpty()) {
1282 if (m_modulesListOutdated) {
1284 m_modulesListOutdated = false;
1286 // Need another round trip
1287 if (reason == "breakpoint-hit") {
1288 q->showStatusMessage(tr("Stopped at breakpoint"));
1289 GdbMi frame = data.findChild("frame");
1290 //debugMessage("HIT BREAKPOINT: " + frame.toString());
1291 m_currentFrame = frame.findChild("addr").data() + '%' +
1292 frame.findChild("func").data() + '%';
1294 QApplication::alert(q->mainWindow(), 3000);
1295 reloadSourceFiles();
1296 sendCommand("-break-list", BreakList);
1297 QVariant var = QVariant::fromValue<GdbMi>(data);
1298 sendCommand("p 0", GdbAsyncOutput2, var); // dummy
1300 q->showStatusMessage(tr("Stopped: \"%1\"").arg(reason));
1301 handleAsyncOutput2(data);
1306 debugMessage("STOPPED FOR UNKNOWN REASON: " + data.toString());
1307 // Ignore it. Will be handled with full response later in the
1308 // JumpToLine or RunToFunction handlers
1310 // FIXME: remove this special case as soon as there's a real
1311 // reason given when the temporary breakpoint is hit.
1312 // reight now we get:
1313 // 14*stopped,thread-id="1",frame={addr="0x0000000000403ce4",
1314 // func="foo",args=[{name="str",value="@0x7fff0f450460"}],
1315 // file="main.cpp",fullname="/tmp/g/main.cpp",line="37"}
1317 // MAC yields sometimes:
1318 // stdout:3661*stopped,time={wallclock="0.00658",user="0.00142",
1319 // system="0.00136",start="1218810678.805432",end="1218810678.812011"}
1321 qq->notifyInferiorStopped();
1322 q->showStatusMessage(tr("Run to Function finished. Stopped."));
1323 GdbMi frame = data.findChild("frame");
1324 QString file = frame.findChild("fullname").data();
1325 int line = frame.findChild("line").data().toInt();
1326 qDebug() << "HIT: " << file << line << " IN " << frame.toString()
1327 << " -- " << data.toString();
1328 q->gotoLocation(file, line, true);
1333 void GdbEngine::handleAsyncOutput2(const GdbMi &data)
1335 qq->notifyInferiorStopped();
1340 qq->stackHandler()->setCurrentIndex(0);
1341 updateLocals(); // Quick shot
1343 int currentId = data.findChild("thread-id").data().toInt();
1344 sendSynchronizedCommand("-stack-list-frames", StackListFrames);
1345 if (supportsThreads())
1346 sendSynchronizedCommand("-thread-list-ids", StackListThreads, currentId);
1352 //"79*stopped,reason="end-stepping-range",reason="breakpoint-hit",bkptno="1",
1353 //thread-id="1",frame={addr="0x0000000000405d8f",func="run1",
1354 //args=[{name="argc",value="1"},{name="argv",value="0x7fffb7c23058"}],
1355 //file="test1.cpp",fullname="/home/apoenitz/dev/work/test1/test1.cpp",line="261"}"
1356 // Mac: (but only sometimes)
1357 m_address = data.findChild("frame").findChild("addr").data();
1358 qq->reloadDisassembler();
1363 qq->reloadRegisters();
1366 void GdbEngine::handleShowVersion(const GdbResultRecord &response)
1368 //qDebug () << "VERSION 2:" << response.data.findChild("consolestreamoutput").data();
1369 //qDebug () << "VERSION:" << response.toString();
1370 debugMessage("VERSION:" + response.toString());
1371 if (response.resultClass == GdbResultDone) {
1373 m_gdbBuildVersion = -1;
1374 QString msg = response.data.findChild("consolestreamoutput").data();
1375 QRegExp supported("GNU gdb(.*) (\\d+)\\.(\\d+)(\\.(\\d+))?(-(\\d+))?");
1376 if (supported.indexIn(msg) == -1) {
1377 debugMessage("UNSUPPORTED GDB VERSION " + msg);
1378 QStringList list = msg.split("\n");
1379 while (list.size() > 2)
1381 msg = tr("The debugger you are using identifies itself as:")
1382 + "<p><p>" + list.join("<br>") + "<p><p>"
1383 + tr("This version is not officially supported by Qt Creator.\n"
1384 "Debugging will most likely not work well.\n"
1385 "Using gdb 6.7 or later is strongly recommended.");
1387 // ugly, but 'Show again' check box...
1388 static QErrorMessage *err = new QErrorMessage(m_mainWindow);
1389 err->setMinimumSize(400, 300);
1390 err->showMessage(msg);
1392 //QMessageBox::information(m_mainWindow, tr("Warning"), msg);
1395 m_gdbVersion = 10000 * supported.cap(2).toInt()
1396 + 100 * supported.cap(3).toInt()
1397 + 1 * supported.cap(5).toInt();
1398 m_gdbBuildVersion = supported.cap(7).toInt();
1399 debugMessage(QString("GDB VERSION: %1").arg(m_gdbVersion));
1401 //qDebug () << "VERSION 3:" << m_gdbVersion << m_gdbBuildVersion;
1405 void GdbEngine::handleFileExecAndSymbols
1406 (const GdbResultRecord &response)
1408 if (response.resultClass == GdbResultDone) {
1409 //m_breakHandler->clearBreakMarkers();
1410 } else if (response.resultClass == GdbResultError) {
1411 QString msg = response.data.findChild("msg").data();
1412 QMessageBox::critical(q->mainWindow(), tr("Error"),
1413 tr("Starting executable failed:\n") + msg);
1414 QTC_ASSERT(q->status() == DebuggerInferiorRunning, /**/);
1415 interruptInferior();
1419 void GdbEngine::handleExecRun(const GdbResultRecord &response)
1421 if (response.resultClass == GdbResultRunning) {
1422 qq->notifyInferiorRunning();
1423 q->showStatusMessage(tr("Running..."));
1424 } else if (response.resultClass == GdbResultError) {
1425 QString msg = response.data.findChild("msg").data();
1426 if (msg == "Cannot find bounds of current function") {
1427 qq->notifyInferiorStopped();
1428 //q->showStatusMessage(tr("No debug information available. "
1429 // "Leaving function..."));
1432 QMessageBox::critical(q->mainWindow(), tr("Error"),
1433 tr("Starting executable failed:\n") + msg);
1434 QTC_ASSERT(q->status() == DebuggerInferiorRunning, /**/);
1435 interruptInferior();
1440 void GdbEngine::queryFullName(const QString &fileName, QString *full)
1442 *full = fullName(fileName);
1445 QString GdbEngine::shortName(const QString &fullName)
1447 return m_fullToShortName.value(fullName, QString());
1450 QString GdbEngine::fullName(const QString &fileName)
1452 //QString absName = m_manager->currentWorkingDirectory() + "/" + file; ??
1453 if (fileName.isEmpty())
1455 QString full = m_shortToFullName.value(fileName, QString());
1456 //debugMessage("RESOLVING: " + fileName + " " + full);
1457 if (!full.isEmpty())
1459 QFileInfo fi(fileName);
1460 if (!fi.isReadable())
1462 full = fi.absoluteFilePath();
1464 full = QDir::cleanPath(full);
1466 //debugMessage("STORING: " + fileName + " " + full);
1467 m_shortToFullName[fileName] = full;
1468 m_fullToShortName[full] = fileName;
1472 QString GdbEngine::fullName(const QStringList &candidates)
1475 foreach (const QString &fileName, candidates) {
1476 full = fullName(fileName);
1477 if (!full.isEmpty())
1480 foreach (const QString &fileName, candidates) {
1481 if (!fileName.isEmpty())
1487 void GdbEngine::shutdown()
1492 void GdbEngine::exitDebugger()
1494 debugMessage(QString("GDBENGINE EXITDEBUFFER: %1").arg(m_gdbProc.state()));
1495 if (m_gdbProc.state() == QProcess::Starting) {
1496 debugMessage(QString("WAITING FOR GDB STARTUP TO SHUTDOWN: %1")
1497 .arg(m_gdbProc.state()));
1498 m_gdbProc.waitForStarted();
1500 if (m_gdbProc.state() == QProcess::Running) {
1501 debugMessage(QString("WAITING FOR RUNNING GDB TO SHUTDOWN: %1")
1502 .arg(m_gdbProc.state()));
1503 interruptInferior();
1504 sendCommand("kill");
1505 sendCommand("-gdb-exit");
1506 // 20s can easily happen when loading webkit debug information
1507 m_gdbProc.waitForFinished(20000);
1508 if (m_gdbProc.state() != QProcess::Running) {
1509 debugMessage(QString("FORCING TERMINATION: %1")
1510 .arg(m_gdbProc.state()));
1511 m_gdbProc.terminate();
1512 m_gdbProc.waitForFinished(20000);
1515 if (m_gdbProc.state() != QProcess::NotRunning)
1516 debugMessage("PROBLEM STOPPING DEBUGGER");
1518 m_outputCollector.shutdown();
1519 initializeVariables();
1520 //q->settings()->m_debugDumpers = false;
1524 int GdbEngine::currentFrame() const
1526 return qq->stackHandler()->currentIndex();
1530 bool GdbEngine::startDebugger()
1532 QStringList gdbArgs;
1534 QFileInfo fi(q->m_executable);
1535 QString fileName = '"' + fi.absoluteFilePath() + '"';
1537 if (m_gdbProc.state() != QProcess::NotRunning) {
1538 debugMessage("GDB IS ALREADY RUNNING!");
1542 if (!m_outputCollector.listen()) {
1543 QMessageBox::critical(q->mainWindow(), tr("Debugger Startup Failure"),
1544 tr("Cannot set up communication with child process: %1")
1545 .arg(m_outputCollector.errorString()));
1549 gdbArgs.prepend(QLatin1String("--tty=") + m_outputCollector.serverName());
1551 //gdbArgs.prepend(QLatin1String("--quiet"));
1552 gdbArgs.prepend(QLatin1String("mi"));
1553 gdbArgs.prepend(QLatin1String("-i"));
1555 if (!q->m_workingDir.isEmpty())
1556 m_gdbProc.setWorkingDirectory(q->m_workingDir);
1557 if (!q->m_environment.isEmpty())
1558 m_gdbProc.setEnvironment(q->m_environment);
1561 qDebug() << "Command: " << q->settings()->m_gdbCmd;
1562 qDebug() << "WorkingDirectory: " << m_gdbProc.workingDirectory();
1563 qDebug() << "ScriptFile: " << q->settings()->m_scriptFile;
1564 qDebug() << "Environment: " << m_gdbProc.environment();
1565 qDebug() << "Arguments: " << gdbArgs;
1566 qDebug() << "BuildDir: " << q->m_buildDir;
1567 qDebug() << "ExeFile: " << q->m_executable;
1570 q->showStatusMessage(tr("Starting Debugger"));
1571 emit gdbInputAvailable(QString(), q->settings()->m_gdbCmd + ' ' + gdbArgs.join(" "));
1573 m_gdbProc.start(q->settings()->m_gdbCmd, gdbArgs);
1574 m_gdbProc.waitForStarted();
1576 if (m_gdbProc.state() != QProcess::Running) {
1577 QMessageBox::critical(q->mainWindow(), tr("Debugger Startup Failure"),
1578 tr("Cannot start debugger: %1").arg(m_gdbProc.errorString()));
1579 m_outputCollector.shutdown();
1583 q->showStatusMessage(tr("Gdb Running"));
1585 sendCommand("show version", GdbShowVersion);
1586 //sendCommand("-enable-timings");
1587 sendCommand("set print static-members off"); // Seemingly doesn't work.
1588 //sendCommand("define hook-stop\n-thread-list-ids\n-stack-list-frames\nend");
1589 //sendCommand("define hook-stop\nprint 4\nend");
1590 //sendCommand("define hookpost-stop\nprint 5\nend");
1591 //sendCommand("define hook-call\nprint 6\nend");
1592 //sendCommand("define hookpost-call\nprint 7\nend");
1593 //sendCommand("set print object on"); // works with CLI, but not MI
1594 //sendCommand("set step-mode on"); // we can't work with that yes
1595 //sendCommand("set exec-done-display on");
1596 //sendCommand("set print pretty on");
1597 //sendCommand("set confirm off");
1598 //sendCommand("set pagination off");
1599 sendCommand("set breakpoint pending on", BreakEnablePending);
1600 sendCommand("set print elements 10000");
1601 sendCommand("-data-list-register-names", RegisterListNames);
1603 // one of the following is needed to prevent crashes in gdb on code like:
1604 // template <class T> T foo() { return T(0); }
1605 // int main() { return foo<int>(); }
1606 // (gdb) call 'int foo<int>'()
1607 // /build/buildd/gdb-6.8/gdb/valops.c:2069: internal-error:
1608 sendCommand("set overload-resolution off");
1609 //sendCommand("set demangle-style none");
1612 // Stop means reenter debugger if this signal happens (implies print).
1613 // Print means print a message if this signal happens.
1614 // Pass means let program see this signal;
1615 // otherwise program doesn't know.
1616 // Pass and Stop may be combined.
1617 // We need "print" as otherwise we would get no feedback whatsoever
1618 // Custom Dumper crashs which happen regularily for when accessing
1619 // uninitialized variables.
1620 sendCommand("handle SIGSEGV nopass stop print");
1622 // This is useful to kill the inferior whenever gdb dies.
1623 //sendCommand("handle SIGTERM pass nostop print");
1625 sendCommand("set unwindonsignal on");
1626 sendCommand("pwd", GdbQueryPwd);
1627 sendCommand("set width 0");
1628 sendCommand("set height 0");
1631 sendCommand("-gdb-set inferior-auto-start-cfm off");
1632 sendCommand("-gdb-set sharedLibrary load-rules "
1633 "dyld \".*libSystem.*\" all "
1634 "dyld \".*libauto.*\" all "
1635 "dyld \".*AppKit.*\" all "
1636 "dyld \".*PBGDBIntrospectionSupport.*\" all "
1637 "dyld \".*Foundation.*\" all "
1638 "dyld \".*CFDataFormatters.*\" all "
1639 "dyld \".*libobjc.*\" all "
1640 "dyld \".*CarbonDataFormatters.*\" all");
1643 QString scriptFileName = q->settings()->m_scriptFile;
1644 if (!scriptFileName.isEmpty()) {
1645 QFile scriptFile(scriptFileName);
1646 if (scriptFile.open(QIODevice::ReadOnly)) {
1647 sendCommand("source " + scriptFileName);
1649 QMessageBox::warning(q->mainWindow(),
1650 tr("Cannot find debugger initialization script"),
1651 tr("The debugger settings point to a script file at '%1' "
1652 "which is not accessible. If a script file is not needed, "
1653 "consider clearing that entry to avoid this warning. "
1654 ).arg(scriptFileName));
1658 if (q->startMode() == DebuggerManager::AttachExternal) {
1659 sendCommand("attach " + QString::number(q->m_attachedPID));
1661 // StartInternal or StartExternal
1662 emit gdbInputAvailable(QString(), QString());
1663 sendCommand("-file-exec-and-symbols " + fileName, GdbFileExecAndSymbols);
1664 //sendCommand("file " + fileName, GdbFileExecAndSymbols);
1666 sendCommand("sharedlibrary apply-load-rules all");
1668 //setTokenBarrier();
1669 if (!q->m_processArgs.isEmpty())
1670 sendCommand("-exec-arguments " + q->m_processArgs.join(" "));
1672 sendCommand("set auto-solib-add off");
1674 sendCommand("x/2i " + startSymbolName(), GdbStart);
1677 // set all to "pending"
1678 if (q->startMode() == DebuggerManager::AttachExternal)
1679 qq->breakHandler()->removeAllBreakpoints();
1681 qq->breakHandler()->setAllPending();
1686 void GdbEngine::continueInferior()
1690 emit gdbInputAvailable(QString(), QString());
1691 qq->notifyInferiorRunningRequested();
1692 sendCommand("-exec-continue", GdbExecContinue);
1695 void GdbEngine::handleStart(const GdbResultRecord &response)
1697 if (response.resultClass == GdbResultDone) {
1698 // stdout:&"x/2i _start\n"
1699 // stdout:~"0x404540 <_start>:\txor %ebp,%ebp\n"
1700 // stdout:~"0x404542 <_start+2>:\tmov %rdx,%r9\n"
1701 QString msg = response.data.findChild("consolestreamoutput").data();
1703 // this ends up in 'gettimeoftheday' or such on MacOS 10.4
1704 QRegExp needle("0x([0-9a-f]+) <.*\\+.*>:");
1706 QRegExp needle("0x([0-9a-f]+) <" + startSymbolName() + "\\+.*>:");
1708 if (needle.lastIndexIn(msg) != -1) {
1709 //debugMessage("STREAM: " + msg + " " + needle.cap(1));
1710 sendCommand("tbreak *0x" + needle.cap(1));
1711 m_waitingForFirstBreakpointToBeHit = true;
1712 qq->notifyInferiorRunningRequested();
1713 sendCommand("-exec-run");
1715 debugMessage("PARSING START ADDRESS FAILED: " + msg);
1717 } else if (response.resultClass == GdbResultError) {
1718 debugMessage("PARSING START ADDRESS FAILED: " + response.toString());
1722 void GdbEngine::stepExec()
1725 emit gdbInputAvailable(QString(), QString());
1726 qq->notifyInferiorRunningRequested();
1727 sendCommand("-exec-step", GdbExecStep);
1730 void GdbEngine::stepIExec()
1733 qq->notifyInferiorRunningRequested();
1734 sendCommand("-exec-step-instruction", GdbExecStepI);
1737 void GdbEngine::stepOutExec()
1740 qq->notifyInferiorRunningRequested();
1741 sendCommand("-exec-finish", GdbExecFinish);
1744 void GdbEngine::nextExec()
1747 emit gdbInputAvailable(QString(), QString());
1748 qq->notifyInferiorRunningRequested();
1749 sendCommand("-exec-next", GdbExecNext);
1752 void GdbEngine::nextIExec()
1755 qq->notifyInferiorRunningRequested();
1756 sendCommand("-exec-next-instruction", GdbExecNextI);
1759 void GdbEngine::runToLineExec(const QString &fileName, int lineNumber)
1762 qq->notifyInferiorRunningRequested();
1763 sendCommand("-exec-until " + fileName + ":" + QString::number(lineNumber));
1766 void GdbEngine::runToFunctionExec(const QString &functionName)
1769 sendCommand("-break-insert -t " + functionName);
1770 qq->notifyInferiorRunningRequested();
1771 sendCommand("-exec-continue", GdbExecRunToFunction);
1774 void GdbEngine::jumpToLineExec(const QString &fileName, int lineNumber)
1777 // not available everywhere?
1778 //sendCliCommand("tbreak " + fileName + ":" + QString::number(lineNumber));
1779 sendCommand("-break-insert -t " + fileName + ":" + QString::number(lineNumber));
1780 sendCommand("jump " + fileName + ":" + QString::number(lineNumber));
1781 // will produce something like
1782 // &"jump /home/apoenitz/dev/work/test1/test1.cpp:242"
1783 // ~"Continuing at 0x4058f3."
1784 // ~"run1 (argc=1, argv=0x7fffbf1f5538) at test1.cpp:242"
1787 q->gotoLocation(fileName, lineNumber, true);
1789 //sendCommand("jump " + fileName + ":" + QString::number(lineNumber));
1791 q->gotoLocation(fileName, lineNumber, true);
1792 setBreakpoint(fileName, lineNumber);
1793 sendCommand("jump " + fileName + ":" + QString::number(lineNumber));
1798 \fn void GdbEngine::setTokenBarrier()
1799 \brief Sets up internal structures to handle a new debugger turn.
1801 This method is called at the beginnign of all step/next/finish etc.
1805 void GdbEngine::setTokenBarrier()
1807 m_oldestAcceptableToken = currentToken();
1810 void GdbEngine::setDebugDumpers(bool on)
1813 debugMessage("SWITCHING ON DUMPER DEBUGGING");
1814 sendCommand("set unwindonsignal off");
1815 q->breakByFunction("qDumpObjectData440");
1818 debugMessage("SWITCHING OFF DUMPER DEBUGGING");
1819 sendCommand("set unwindonsignal on");
1824 //////////////////////////////////////////////////////////////////////
1826 // Breakpoint specific stuff
1828 //////////////////////////////////////////////////////////////////////
1830 void GdbEngine::breakpointDataFromOutput(BreakpointData *data, const GdbMi &bkpt)
1832 if (!bkpt.isValid())
1836 data->pending = false;
1837 data->bpMultiple = false;
1838 data->bpCondition.clear();
1840 foreach (const GdbMi &child, bkpt.children()) {
1841 if (child.hasName("number")) {
1842 data->bpNumber = child.data();
1843 } else if (child.hasName("func")) {
1844 data->bpFuncName = child.data();
1845 } else if (child.hasName("addr")) {
1846 // <MULTIPLE> happens in constructors. In this case there are
1847 // _two_ fields named "addr" in the response. On Linux that is...
1848 if (child.data() == "<MULTIPLE>")
1849 data->bpMultiple = true;
1851 data->bpAddress = child.data();
1852 } else if (child.hasName("file")) {
1853 files.append(child.data());
1854 } else if (child.hasName("fullname")) {
1855 QString fullName = child.data();
1857 fullName = QDir::cleanPath(fullName);
1859 files.prepend(fullName);
1860 } else if (child.hasName("line")) {
1861 data->bpLineNumber = child.data();
1862 if (child.data().toInt())
1863 data->markerLineNumber = child.data().toInt();
1864 } else if (child.hasName("cond")) {
1865 data->bpCondition = child.data();
1866 // gdb 6.3 likes to "rewrite" conditions. Just accept that fact.
1867 if (data->bpCondition != data->condition && data->conditionsMatch())
1868 data->condition = data->bpCondition;
1870 else if (child.hasName("pending")) {
1871 data->pending = true;
1872 int pos = child.data().lastIndexOf(':');
1874 data->bpLineNumber = child.data().mid(pos + 1);
1875 data->markerLineNumber = child.data().mid(pos + 1).toInt();
1876 files.prepend(child.data().left(pos));
1878 files.prepend(child.data());
1882 // This field is not present. Contents needs to be parsed from
1883 // the plain "ignore" response.
1884 //else if (child.hasName("ignore"))
1885 // data->bpIgnoreCount = child.data();
1887 QString name = fullName(files);
1888 if (data->bpFileName.isEmpty())
1889 data->bpFileName = name;
1890 if (data->markerFileName.isEmpty())
1891 data->markerFileName = name;
1894 void GdbEngine::sendInsertBreakpoint(int index)
1896 const BreakpointData *data = qq->breakHandler()->at(index);
1898 if (data->funcName.isEmpty()) {
1899 where = data->fileName;
1901 // full names do not work on Mac/MI
1902 QFileInfo fi(data->fileName);
1903 where = fi.fileName();
1904 //where = fi.absoluteFilePath();
1907 // full names do not work on Mac/MI
1908 QFileInfo fi(data->fileName);
1909 where = fi.fileName();
1910 //where = m_manager->shortName(data->fileName);
1911 //if (where.isEmpty())
1912 // where = data->fileName;
1914 // we need something like "\"file name.cpp\":100" to
1915 // survive the gdb command line parser with file names intact
1916 where = "\"\\\"" + where + "\\\":" + data->lineNumber + "\"";
1918 where = data->funcName;
1921 // set up fallback in case of pending breakpoints which aren't handled
1922 // by the MI interface
1924 QString cmd = "-break-insert ";
1925 //if (!data->condition.isEmpty())
1926 // cmd += "-c " + data->condition + " ";
1930 QString cmd = "-break-insert -l -1 ";
1931 //if (!data->condition.isEmpty())
1932 // cmd += "-c " + data->condition + " ";
1936 QString cmd = "-break-insert ";
1937 //if (!data->condition.isEmpty())
1938 // cmd += "-c " + data->condition + " ";
1941 debugMessage(QString("Current state: %1").arg(q->status()));
1942 sendCommand(cmd, BreakInsert, index, NeedsStop);
1945 void GdbEngine::handleBreakList(const GdbResultRecord &record)
1947 // 45^done,BreakpointTable={nr_rows="2",nr_cols="6",hdr=[
1948 // {width="3",alignment="-1",col_name="number",colhdr="Num"}, ...
1949 // body=[bkpt={number="1",type="breakpoint",disp="keep",enabled="y",
1950 // addr="0x000000000040109e",func="main",file="app.cpp",
1951 // fullname="/home/apoenitz/dev/work/plugintest/app/app.cpp",
1952 // line="11",times="1"},
1953 // bkpt={number="2",type="breakpoint",disp="keep",enabled="y",
1954 // addr="<PENDING>",pending="plugin.cpp:7",times="0"}] ... }
1956 if (record.resultClass == GdbResultDone) {
1957 GdbMi table = record.data.findChild("BreakpointTable");
1958 if (table.isValid())
1959 handleBreakList(table);
1963 void GdbEngine::handleBreakList(const GdbMi &table)
1965 //qDebug() << "GdbEngine::handleOutput: table: "
1966 // << table.toString();
1967 GdbMi body = table.findChild("body");
1968 //qDebug() << "GdbEngine::handleOutput: body: "
1969 // << body.toString();
1971 if (body.isValid()) {
1973 bkpts = body.children();
1976 bkpts = table.children();
1977 // remove the 'hdr' and artificial items
1978 //qDebug() << "FOUND " << bkpts.size() << " BREAKPOINTS";
1979 for (int i = bkpts.size(); --i >= 0; ) {
1980 int num = bkpts.at(i).findChild("number").data().toInt();
1982 //qDebug() << "REMOVING " << i << bkpts.at(i).toString();
1986 //qDebug() << "LEFT " << bkpts.size() << " BREAKPOINTS";
1989 BreakHandler *handler = qq->breakHandler();
1990 for (int index = 0; index != bkpts.size(); ++index) {
1991 BreakpointData temp(handler);
1992 breakpointDataFromOutput(&temp, bkpts.at(index));
1993 int found = handler->findBreakpoint(temp);
1995 breakpointDataFromOutput(handler->at(found), bkpts.at(index));
1997 //qDebug() << "CANNOT HANDLE RESPONSE " << bkpts.at(index).toString();
2000 attemptBreakpointSynchronization();
2001 handler->updateMarkers();
2005 void GdbEngine::handleBreakIgnore(const GdbResultRecord &record, int index)
2009 // ~"Will stop next time breakpoint 2 is reached.\n"
2013 // ~"Will ignore next 12 crossings of breakpoint 2.\n"
2016 // gdb 6.3 does not produce any console output
2017 BreakHandler *handler = qq->breakHandler();
2018 if (record.resultClass == GdbResultDone && index < handler->size()) {
2019 QString msg = record.data.findChild("consolestreamoutput").data();
2020 BreakpointData *data = handler->at(index);
2021 //if (msg.contains("Will stop next time breakpoint")) {
2022 // data->bpIgnoreCount = "0";
2023 //} else if (msg.contains("Will ignore next")) {
2024 // data->bpIgnoreCount = data->ignoreCount;
2026 // FIXME: this assumes it is doing the right thing...
2027 data->bpIgnoreCount = data->ignoreCount;
2028 attemptBreakpointSynchronization();
2029 handler->updateMarkers();
2033 void GdbEngine::handleBreakCondition(const GdbResultRecord &record, int index)
2035 BreakHandler *handler = qq->breakHandler();
2036 if (record.resultClass == GdbResultDone) {
2037 // we just assume it was successful. otherwise we had to parse
2038 // the output stream data
2039 BreakpointData *data = handler->at(index);
2040 //qDebug() << "HANDLE BREAK CONDITION " << index << data->condition;
2041 data->bpCondition = data->condition;
2042 attemptBreakpointSynchronization();
2043 handler->updateMarkers();
2044 } else if (record.resultClass == GdbResultError) {
2045 QString msg = record.data.findChild("msg").data();
2047 if (1 || msg.startsWith("Error parsing breakpoint condition. "
2048 " Will try again when we hit the breakpoint.")) {
2049 BreakpointData *data = handler->at(index);
2050 //qDebug() << "ERROR BREAK CONDITION " << index << data->condition;
2051 data->bpCondition = data->condition;
2052 attemptBreakpointSynchronization();
2053 handler->updateMarkers();
2058 void GdbEngine::handleBreakInsert(const GdbResultRecord &record, int index)
2060 BreakHandler *handler = qq->breakHandler();
2061 if (record.resultClass == GdbResultDone) {
2062 //qDebug() << "HANDLE BREAK INSERT " << index;
2064 // interesting only on Mac?
2065 BreakpointData *data = handler->at(index);
2066 GdbMi bkpt = record.data.findChild("bkpt");
2067 //qDebug() << "BKPT: " << bkpt.toString() << " DATA" << data->toToolTip();
2068 breakpointDataFromOutput(data, bkpt);
2070 attemptBreakpointSynchronization();
2071 handler->updateMarkers();
2072 } else if (record.resultClass == GdbResultError) {
2073 const BreakpointData *data = handler->at(index);
2075 //QString where = "\"\\\"" + data->fileName + "\\\":"
2076 // + data->lineNumber + "\"";
2077 QString where = "\"" + data->fileName + "\":"
2079 sendCommand("break " + where, BreakInsert1, index);
2082 QFileInfo fi(data->fileName);
2083 QString where = "\"" + fi.fileName() + "\":"
2085 sendCommand("break " + where, BreakInsert1, index);
2088 QFileInfo fi(data->fileName);
2089 QString where = "\"" + fi.fileName() + "\":"
2091 //QString where = m_data->fileName + QLatin1Char(':') + data->lineNumber;
2092 sendCommand("break " + where, BreakInsert1, index);
2097 void GdbEngine::extractDataFromInfoBreak(const QString &output, BreakpointData *data)
2099 data->bpFileName = "<MULTIPLE>";
2101 //qDebug() << output;
2102 if (output.isEmpty())
2104 // "Num Type Disp Enb Address What
2105 // 4 breakpoint keep y <MULTIPLE> 0x00000000004066ad
2106 // 4.1 y 0x00000000004066ad in CTorTester
2107 // at /data5/dev/ide/main/tests/manual/gdbdebugger/simple/app.cpp:124
2109 // everything on a single line on Windows for constructors of classes
2110 // within namespaces.
2111 // Sometimes the path is relative too.
2113 QRegExp re("MULTIPLE.*(0x[0-9a-f]+) in (.*)\\s+at (.*):([\\d]+)([^\\d]|$)");
2114 re.setMinimal(true);
2116 if (re.indexIn(output) != -1) {
2117 data->bpAddress = re.cap(1);
2118 data->bpFuncName = re.cap(2).trimmed();
2119 data->bpLineNumber = re.cap(4);
2120 QString full = fullName(re.cap(3));
2121 data->markerLineNumber = data->bpLineNumber.toInt();
2122 data->markerFileName = full;
2123 data->bpFileName = full;
2124 //qDebug() << "FOUND BREAKPOINT\n" << output
2125 // << re.cap(1) << "\n" << re.cap(2) << "\n"
2126 // << re.cap(3) << "\n" << re.cap(4) << "\n";
2128 qDebug() << "COULD NOT MATCH " << re.pattern() << " AND " << output;
2129 data->bpNumber = "<unavailable>";
2133 void GdbEngine::handleBreakInfo(const GdbResultRecord &record, int bpNumber)
2135 BreakHandler *handler = qq->breakHandler();
2136 if (record.resultClass == GdbResultDone) {
2137 // Old-style output for multiple breakpoints, presumably in a
2139 int found = handler->findBreakpoint(bpNumber);
2141 QString str = record.data.findChild("consolestreamoutput").data();
2142 extractDataFromInfoBreak(str, handler->at(found));
2143 handler->updateMarkers();
2144 attemptBreakpointSynchronization(); // trigger "ready"
2149 void GdbEngine::handleBreakInsert1(const GdbResultRecord &record, int index)
2151 BreakHandler *handler = qq->breakHandler();
2152 if (record.resultClass == GdbResultDone) {
2153 // Pending breakpoints in dylibs on Mac only?
2154 BreakpointData *data = handler->at(index);
2155 GdbMi bkpt = record.data.findChild("bkpt");
2156 breakpointDataFromOutput(data, bkpt);
2157 attemptBreakpointSynchronization(); // trigger "ready"
2158 handler->updateMarkers();
2159 } else if (record.resultClass == GdbResultError) {
2160 qDebug() << "INSERTING BREAKPOINT WITH BASE NAME FAILED. GIVING UP";
2161 BreakpointData *data = handler->at(index);
2162 data->bpNumber = "<unavailable>";
2163 attemptBreakpointSynchronization(); // trigger "ready"
2164 handler->updateMarkers();
2168 void GdbEngine::attemptBreakpointSynchronization()
2170 // Non-lethal check for nested calls
2171 static bool inBreakpointSychronization = false;
2172 QTC_ASSERT(!inBreakpointSychronization, /**/);
2173 inBreakpointSychronization = true;
2175 BreakHandler *handler = qq->breakHandler();
2177 foreach (BreakpointData *data, handler->takeRemovedBreakpoints()) {
2178 QString bpNumber = data->bpNumber;
2179 debugMessage(QString("DELETING BP %1 IN %2").arg(bpNumber)
2180 .arg(data->markerFileName));
2181 if (!bpNumber.trimmed().isEmpty())
2182 sendCommand("-break-delete " + bpNumber, BreakDelete, QVariant(),
2187 bool updateNeeded = false;
2189 for (int index = 0; index != handler->size(); ++index) {
2190 BreakpointData *data = handler->at(index);
2191 // multiple breakpoints?
2192 if (data->bpMultiple && data->bpFileName.isEmpty()) {
2193 sendCommand(QString("info break %1").arg(data->bpNumber),
2194 BreakInfo, data->bpNumber.toInt());
2195 updateNeeded = true;
2200 for (int index = 0; index != handler->size(); ++index) {
2201 BreakpointData *data = handler->at(index);
2202 // unset breakpoints?
2203 if (data->bpNumber.isEmpty()) {
2204 data->bpNumber = " ";
2205 sendInsertBreakpoint(index);
2206 //qDebug() << "UPDATE NEEDED BECAUSE OF UNKNOWN BREAKPOINT";
2207 updateNeeded = true;
2212 if (!updateNeeded) {
2213 for (int index = 0; index != handler->size(); ++index) {
2214 BreakpointData *data = handler->at(index);
2215 // update conditions if needed
2216 if (data->bpNumber.toInt() && data->condition != data->bpCondition
2217 && !data->conditionsMatch()) {
2218 sendCommand(QString("condition %1 %2").arg(data->bpNumber)
2219 .arg(data->condition), BreakCondition, index);
2220 //qDebug() << "UPDATE NEEDED BECAUSE OF CONDITION"
2221 // << data->condition << data->bpCondition;
2222 updateNeeded = true;
2225 // update ignorecount if needed
2226 if (data->bpNumber.toInt() && data->ignoreCount != data->bpIgnoreCount) {
2227 sendCommand(QString("ignore %1 %2").arg(data->bpNumber)
2228 .arg(data->ignoreCount), BreakIgnore, index);
2229 updateNeeded = true;
2235 for (int index = 0; index != handler->size(); ++index) {
2236 // happens sometimes on Mac. Brush over symptoms
2237 BreakpointData *data = handler->at(index);
2238 if (data->markerFileName.startsWith("../")) {
2239 data->markerFileName = fullName(data->markerFileName);
2240 handler->updateMarkers();
2244 if (!updateNeeded && m_waitingForBreakpointSynchronizationToContinue) {
2245 m_waitingForBreakpointSynchronizationToContinue = false;
2246 // we continue the execution
2250 inBreakpointSychronization = false;
2254 //////////////////////////////////////////////////////////////////////
2256 // Disassembler specific stuff
2258 //////////////////////////////////////////////////////////////////////
2260 void GdbEngine::reloadDisassembler()
2262 emit sendCommand("disassemble", DisassemblerList, m_address);
2265 void GdbEngine::handleDisassemblerList(const GdbResultRecord &record,
2266 const QString &cookie)
2268 QList<DisassemblerLine> lines;
2269 static const QString pad = QLatin1String(" ");
2270 int currentLine = -1;
2271 if (record.resultClass == GdbResultDone) {
2272 QString res = record.data.findChild("consolestreamoutput").data();
2273 QTextStream ts(&res, QIODevice::ReadOnly);
2274 while (!ts.atEnd()) {
2275 //0x0000000000405fd8 <_ZN11QTextStreamD1Ev@plt+0>:
2276 // jmpq *2151890(%rip) # 0x6135b0 <_GLOBAL_OFFSET_TABLE_+640>
2277 //0x0000000000405fde <_ZN11QTextStreamD1Ev@plt+6>:
2279 //0x0000000000405fe3 <_ZN11QTextStreamD1Ev@plt+11>:
2280 // jmpq 0x405af8 <_init+24>
2281 //0x0000000000405fe8 <_ZN9QHashData6rehashEi@plt+0>:
2282 // jmpq *2151882(%rip) # 0x6135b8 <_GLOBAL_OFFSET_TABLE_+648>
2283 QString str = ts.readLine();
2284 if (!str.startsWith(QLatin1String("0x"))) {
2285 //qDebug() << "IGNORING DISASSEMBLER" << str;
2288 DisassemblerLine line;
2289 QTextStream ts(&str, QIODevice::ReadOnly);
2290 ts >> line.address >> line.symbol;
2291 line.mnemonic = ts.readLine().trimmed();
2292 if (line.symbol.endsWith(QLatin1Char(':')))
2293 line.symbol.chop(1);
2294 line.addressDisplay = line.address + pad;
2295 if (line.addressDisplay.startsWith(QLatin1String("0x00000000")))
2296 line.addressDisplay.replace(2, 8, QString());
2297 line.symbolDisplay = line.symbol + pad;
2299 if (line.address == cookie)
2300 currentLine = lines.size();
2305 DisassemblerLine line;
2306 line.addressDisplay = tr("<could not retreive module information>");
2310 qq->disassemblerHandler()->setLines(lines);
2311 if (currentLine != -1)
2312 qq->disassemblerHandler()->setCurrentLine(currentLine);
2316 //////////////////////////////////////////////////////////////////////
2318 // Modules specific stuff
2320 //////////////////////////////////////////////////////////////////////
2322 void GdbEngine::loadSymbols(const QString &moduleName)
2324 // FIXME: gdb does not understand quoted names here (tested with 6.8)
2325 sendCommand("sharedlibrary " + dotEscape(moduleName));
2329 void GdbEngine::loadAllSymbols()
2331 sendCommand("sharedlibrary .*");
2335 void GdbEngine::reloadModules()
2337 sendCommand("info shared", ModulesList, QVariant());
2340 void GdbEngine::handleModulesList(const GdbResultRecord &record)
2342 QList<Module> modules;
2343 if (record.resultClass == GdbResultDone) {
2344 QString data = record.data.findChild("consolestreamoutput").data();
2345 QTextStream ts(&data, QIODevice::ReadOnly);
2346 while (!ts.atEnd()) {
2347 QString line = ts.readLine();
2348 if (!line.startsWith("0x"))
2351 QString symbolsRead;
2352 QTextStream ts(&line, QIODevice::ReadOnly);
2353 ts >> module.startAddress >> module.endAddress >> symbolsRead;
2354 module.moduleName = ts.readLine().trimmed();
2355 module.symbolsRead = (symbolsRead == "Yes");
2356 modules.append(module);
2359 qq->modulesHandler()->setModules(modules);
2363 //////////////////////////////////////////////////////////////////////
2365 // Source files specific stuff
2367 //////////////////////////////////////////////////////////////////////
2369 void GdbEngine::reloadSourceFiles()
2371 sendCommand("-file-list-exec-source-files", GdbQuerySources);
2375 //////////////////////////////////////////////////////////////////////
2377 // Stack specific stuff
2379 //////////////////////////////////////////////////////////////////////
2381 void GdbEngine::handleStackSelectThread(const GdbResultRecord &record, int)
2384 //qDebug("FIXME: StackHandler::handleOutput: SelectThread");
2385 q->showStatusMessage(tr("Retrieving data for stack view..."), 3000);
2386 sendCommand("-stack-list-frames", StackListFrames);
2390 void GdbEngine::handleStackListFrames(const GdbResultRecord &record)
2392 QList<StackFrame> stackFrames;
2394 const GdbMi stack = record.data.findChild("stack");
2395 QString dummy = stack.toString();
2396 if (!stack.isValid()) {
2397 qDebug() << "FIXME: stack: " << stack.toString();
2403 for (int i = 0; i != stack.childCount(); ++i) {
2404 //qDebug() << "HANDLING FRAME: " << stack.childAt(i).toString();
2405 const GdbMi frameMi = stack.childAt(i);
2409 files.append(frameMi.findChild("fullname").data());
2410 files.append(frameMi.findChild("file").data());
2411 frame.file = fullName(files);
2412 frame.function = frameMi.findChild("func").data();
2413 frame.from = frameMi.findChild("from").data();
2414 frame.line = frameMi.findChild("line").data().toInt();
2415 frame.address = frameMi.findChild("addr").data();
2417 stackFrames.append(frame);
2420 const bool isBogus =
2421 // Assume this is wrong and points to some strange stl_algobase
2422 // implementation. Happens on Karsten's XP system with Gdb 5.50
2423 (frame.file.endsWith("/bits/stl_algobase.h") && frame.line == 150)
2424 // Also wrong. Happens on Vista with Gdb 5.50
2425 || (frame.function == "operator new" && frame.line == 151);
2427 // immediately leave bogus frames
2428 if (topFrame == -1 && isBogus) {
2429 sendCommand("-exec-finish");
2435 // Initialize top frame to the first valid frame
2436 const bool isValid = !frame.file.isEmpty() && !frame.function.isEmpty();
2437 if (isValid && topFrame == -1)
2441 qq->stackHandler()->setFrames(stackFrames);
2444 if (0 && topFrame != -1) {
2445 // updates of locals already triggered early
2446 const StackFrame &frame = qq->stackHandler()->currentFrame();
2447 bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable();
2449 q->gotoLocation(frame.file, frame.line, true);
2451 qDebug() << "FULL NAME NOT USABLE 0: " << frame.file;
2453 activateFrame(topFrame);
2456 if (topFrame != -1) {
2457 // updates of locals already triggered early
2458 const StackFrame &frame = qq->stackHandler()->currentFrame();
2459 bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable();
2461 q->gotoLocation(frame.file, frame.line, true);
2463 qDebug() << "FULL NAME NOT USABLE 0: " << frame.file << topFrame;
2468 void GdbEngine::selectThread(int index)
2470 //reset location arrow
2473 ThreadsHandler *threadsHandler = qq->threadsHandler();
2474 threadsHandler->setCurrentThread(index);
2476 QList<ThreadData> threads = threadsHandler->threads();
2477 QTC_ASSERT(index < threads.size(), return);
2478 int id = threads.at(index).id;
2479 q->showStatusMessage(tr("Retrieving data for stack view..."), 10000);
2480 sendCommand(QLatin1String("-thread-select ") + QString::number(id),
2484 void GdbEngine::activateFrame(int frameIndex)
2486 if (q->status() != DebuggerInferiorStopped)
2489 StackHandler *stackHandler = qq->stackHandler();
2490 int oldIndex = stackHandler->currentIndex();
2491 //qDebug() << "ACTIVATE FRAME: " << frameIndex << oldIndex
2492 // << stackHandler->currentIndex();
2494 QTC_ASSERT(frameIndex < stackHandler->stackSize(), return);
2496 if (oldIndex != frameIndex) {
2497 // Assuming this always succeeds saves a roundtrip.
2498 // Otherwise the lines below would need to get triggered
2499 // after a response to this -stack-select-frame here.
2500 sendCommand("-stack-select-frame " + QString::number(frameIndex));
2502 stackHandler->setCurrentIndex(frameIndex);
2506 const StackFrame &frame = stackHandler->currentFrame();
2508 bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable();
2510 q->gotoLocation(frame.file, frame.line, true);
2512 qDebug() << "FULL NAME NOT USABLE: " << frame.file;
2515 void GdbEngine::handleStackListThreads(const GdbResultRecord &record, int id)
2517 // "72^done,{thread-ids={thread-id="2",thread-id="1"},number-of-threads="2"}
2518 const QList<GdbMi> items = record.data.findChild("thread-ids").children();
2519 QList<ThreadData> threads;
2520 int currentIndex = -1;
2521 for (int index = 0, n = items.size(); index != n; ++index) {
2523 thread.id = items.at(index).data().toInt();
2524 threads.append(thread);
2525 if (thread.id == id) {
2526 //qDebug() << "SETTING INDEX TO: " << index << " ID: "<< id << "RECOD: "<< record.toString();
2527 currentIndex = index;
2530 ThreadsHandler *threadsHandler = qq->threadsHandler();
2531 threadsHandler->setThreads(threads);
2532 threadsHandler->setCurrentThread(currentIndex);
2536 //////////////////////////////////////////////////////////////////////
2538 // Register specific stuff
2540 //////////////////////////////////////////////////////////////////////
2542 void GdbEngine::reloadRegisters()
2544 QString format = qq->registerHandler()->model()->property(PROPERTY_REGISTER_FORMAT).toString();
2545 sendCommand("-data-list-register-values " + format, RegisterListValues);
2548 void GdbEngine::handleRegisterListNames(const GdbResultRecord &record)
2550 if (record.resultClass != GdbResultDone)
2553 QList<Register> registers;
2554 foreach (const GdbMi &item, record.data.findChild("register-names").children())
2555 registers.append(Register(item.data()));
2557 qq->registerHandler()->setRegisters(registers);
2560 void GdbEngine::handleRegisterListValues(const GdbResultRecord &record)
2562 if (record.resultClass != GdbResultDone)
2565 QList<Register> registers = qq->registerHandler()->registers();
2567 // 24^done,register-values=[{number="0",value="0xf423f"},...]
2568 foreach (const GdbMi &item, record.data.findChild("register-values").children()) {
2569 int index = item.findChild("number").data().toInt();
2570 if (index < registers.size()) {
2571 Register ® = registers[index];
2572 QString value = item.findChild("value").data();
2573 reg.changed = (value != reg.value);
2578 qq->registerHandler()->setRegisters(registers);
2582 //////////////////////////////////////////////////////////////////////
2584 // Thread specific stuff
2586 //////////////////////////////////////////////////////////////////////
2588 bool GdbEngine::supportsThreads() const
2590 // 6.3 crashes happily on -thread-list-ids. So don't use it.
2591 // The test below is a semi-random pick, 6.8 works fine
2592 return m_gdbVersion > 60500;
2595 //////////////////////////////////////////////////////////////////////
2597 // Tooltip specific stuff
2599 //////////////////////////////////////////////////////////////////////
2601 static WatchData m_toolTip;
2602 static QString m_toolTipExpression;
2603 static QPoint m_toolTipPos;
2604 static QMap<QString, WatchData> m_toolTipCache;
2606 static bool hasLetterOrNumber(const QString &exp)
2608 for (int i = exp.size(); --i >= 0; )
2609 if (exp[i].isLetterOrNumber() || exp[i] == '_')
2614 static bool hasSideEffects(const QString &exp)
2617 return exp.contains("-=")
2618 || exp.contains("+=")
2619 || exp.contains("/=")
2620 || exp.contains("*=")
2621 || exp.contains("&=")
2622 || exp.contains("|=")
2623 || exp.contains("^=")
2624 || exp.contains("--")
2625 || exp.contains("++");
2628 static bool isKeyWord(const QString &exp)
2630 // FIXME: incomplete
2631 return exp == QLatin1String("class")
2632 || exp == QLatin1String("const")
2633 || exp == QLatin1String("do")
2634 || exp == QLatin1String("if")
2635 || exp == QLatin1String("return")
2636 || exp == QLatin1String("struct")
2637 || exp == QLatin1String("template")
2638 || exp == QLatin1String("void")
2639 || exp == QLatin1String("volatile")
2640 || exp == QLatin1String("while");
2643 void GdbEngine::setToolTipExpression(const QPoint &pos, const QString &exp0)
2645 //qDebug() << "SET TOOLTIP EXP" << pos << exp0;
2646 if (q->status() != DebuggerInferiorStopped) {
2647 //qDebug() << "SUPPRESSING DEBUGGER TOOLTIP, INFERIOR NOT STOPPED";
2651 if (q->settings()->m_debugDumpers) {
2652 // minimize interference
2657 m_toolTipExpression = exp0;
2660 if (m_toolTip.isTypePending()) {
2661 qDebug() << "suppressing duplicated tooltip creation";
2665 if (m_toolTipCache.contains(exp)) {
2666 const WatchData & data = m_toolTipCache[exp];
2667 // FIXME: qq->watchHandler()->collapseChildren(data.iname);
2672 QToolTip::hideText();
2673 if (exp.isEmpty() || exp.startsWith("#")) {
2674 QToolTip::hideText();
2678 if (!hasLetterOrNumber(exp)) {
2679 QToolTip::showText(m_toolTipPos,
2680 "'" + exp + "' contains no identifier");
2687 if (exp.startsWith('"') && exp.endsWith('"')) {
2688 QToolTip::showText(m_toolTipPos, "String literal " + exp);
2692 if (exp.startsWith("++") || exp.startsWith("--"))
2695 if (exp.endsWith("++") || exp.endsWith("--"))
2698 if (exp.startsWith("<") || exp.startsWith("["))
2701 if (hasSideEffects(exp)) {
2702 QToolTip::showText(m_toolTipPos,
2703 "Cowardly refusing to evaluate expression '" + exp
2704 + "' with potential side effects");
2708 // Gdb crashes when creating a variable object with the name
2709 // of the type of 'this'
2711 for (int i = 0; i != m_currentLocals.childCount(); ++i) {
2712 if (m_currentLocals.childAt(i).exp == "this") {
2713 qDebug() << "THIS IN ROW " << i;
2714 if (m_currentLocals.childAt(i).type.startsWith(exp)) {
2715 QToolTip::showText(m_toolTipPos,
2716 exp + ": type of current 'this'");
2717 qDebug() << " TOOLTIP CRASH SUPPRESSED";
2725 //if (m_manager->status() != DebuggerInferiorStopped)
2728 // FIXME: 'exp' can contain illegal characters
2729 m_toolTip = WatchData();
2730 //m_toolTip.level = 0;
2731 // m_toolTip.row = 0;
2732 // m_toolTip.parentIndex = 2;
2733 m_toolTip.exp = exp;
2734 m_toolTip.name = exp;
2735 m_toolTip.iname = tooltipIName;
2736 insertData(m_toolTip);
2737 updateWatchModel2();
2741 //////////////////////////////////////////////////////////////////////
2743 // Watch specific stuff
2745 //////////////////////////////////////////////////////////////////////
2747 static const QString strNotInScope = QLatin1String("<not in scope>");
2749 static bool isPointerType(const QString &type)
2751 return type.endsWith("*") || type.endsWith("* const");
2754 static bool isAccessSpecifier(const QString &str)
2756 static const QStringList items =
2757 QStringList() << "private" << "protected" << "public";
2758 return items.contains(str);
2761 static bool startsWithDigit(const QString &str)
2763 return !str.isEmpty() && str[0] >= '0' && str[0] <= '9';
2766 QString stripPointerType(QString type)
2768 if (type.endsWith("*"))
2770 if (type.endsWith("* const"))
2772 if (type.endsWith(' '))
2777 static QString gdbQuoteTypes(const QString &type)
2779 // gdb does not understand sizeof(Core::IFile*).
2780 // "sizeof('Core::IFile*')" is also not acceptable,
2781 // it needs to be "sizeof('Core::IFile'*)"
2783 // We never will have a perfect solution here (even if we had a full blown
2784 // C++ parser as we do not have information on what is a type and what is
2785 // a vriable name. So "a<b>::c" could either be two comparisons of values
2786 // 'a', 'b' and '::c', or a nested type 'c' in a template 'a<b>'. We
2787 // assume here it is the latter.
2790 // (*('myns::QPointer<myns::QObject>*'*)0x684060)" is not acceptable
2791 // (*('myns::QPointer<myns::QObject>'**)0x684060)" is acceptable
2792 if (isPointerType(type))
2793 return gdbQuoteTypes(stripPointerType(type)) + "*";
2797 int templateLevel = 0;
2798 for (int i = 0; i != type.size(); ++i) {
2799 QChar c = type.at(i);
2800 if (c.isLetterOrNumber() || c == '_' || c == ':' || c == ' ') {
2802 } else if (c == '<') {
2805 } else if (c == '<') {
2808 } else if (templateLevel > 0) {
2811 if (accu.contains(':') || accu.contains('<'))
2812 result += '\'' + accu + '\'';
2819 if (accu.contains(':') || accu.contains('<'))
2820 result += '\'' + accu + '\'';
2823 //qDebug() << "GDB_QUOTING" << type << " TO " << result;
2828 static void setWatchDataValue(WatchData &data, const GdbMi &mi,
2834 case 0: // unencoded 8 bit data
2837 case 1: // base64 encoded 8 bit data
2838 ba = QByteArray::fromBase64(mi.data());
2839 ba = '"' + ba + '"';
2841 case 2: // base64 encoded 16 bit data
2842 ba = QByteArray::fromBase64(mi.data());
2843 ba = QString::fromUtf16((ushort *)ba.data(), ba.size() / 2).toUtf8();
2844 ba = '"' + ba + '"';
2846 case 3: // base64 encoded 32 bit data
2847 ba = QByteArray::fromBase64(mi.data());
2848 ba = QString::fromUcs4((uint *)ba.data(), ba.size() / 4).toUtf8();
2849 ba = '"' + ba + '"';
2854 data.setValueNeeded();
2858 static void setWatchDataEditValue(WatchData &data, const GdbMi &mi)
2861 data.editvalue = mi.data();
2864 static void setWatchDataValueToolTip(WatchData &data, const GdbMi &mi)
2867 data.setValueToolTip(mi.data());
2870 static void setWatchDataChildCount(WatchData &data, const GdbMi &mi)
2873 data.childCount = mi.data().toInt();
2874 data.setChildCountUnneeded();
2875 if (data.childCount == 0)
2876 data.setChildrenUnneeded();
2878 data.childCount = -1;
2882 static void setWatchDataValueDisabled(WatchData &data, const GdbMi &mi)
2884 if (mi.data() == "true")
2885 data.valuedisabled = true;
2886 else if (mi.data() == "false")
2887 data.valuedisabled = false;
2890 static void setWatchDataExpression(WatchData &data, const GdbMi &mi)
2893 data.exp = "(" + mi.data() + ")";
2896 static void setWatchDataAddress(WatchData &data, const GdbMi &mi)
2899 data.addr = mi.data();
2900 if (data.exp.isEmpty())
2901 data.exp = "(*(" + gdbQuoteTypes(data.type) + "*)" + data.addr + ")";
2905 static bool extractTemplate(const QString &type, QString *tmplate, QString *inner)
2907 // Input "Template<Inner1,Inner2,...>::Foo" will return "Template::Foo" in
2908 // 'tmplate' and "Inner1@Inner2@..." etc in 'inner'. Result indicates
2909 // whether parsing was successful
2911 bool skipSpace = false;
2912 for (int i = 0; i != type.size(); ++i) {
2914 if (c == ' ' && skipSpace) {
2916 } else if (c == '<') {
2917 *(level == 0 ? tmplate : inner) += c;
2919 } else if (c == '>') {
2921 *(level == 0 ? tmplate : inner) += c;
2922 } else if (c == ',') {
2923 *inner += (level == 1) ? '@' : ',';
2926 *(level == 0 ? tmplate : inner) += c;
2929 *tmplate = tmplate->trimmed();
2930 *tmplate = tmplate->remove("<>");
2931 *inner = inner->trimmed();
2932 //qDebug() << "EXTRACT TEMPLATE: " << *tmplate << *inner << " FROM " << type;
2933 return !inner->isEmpty();
2936 static QString extractTypeFromPTypeOutput(const QString &str)
2938 int pos0 = str.indexOf('=');
2939 int pos1 = str.indexOf('{');
2940 int pos2 = str.lastIndexOf('}');
2942 if (pos0 != -1 && pos1 != -1 && pos2 != -1)
2943 res = str.mid(pos0 + 2, pos1 - 1 - pos0)
2944 + " ... " + str.right(str.size() - pos2);
2945 return res.simplified();
2948 static bool isIntOrFloatType(const QString &type)
2950 static const QStringList types = QStringList()
2951 << "char" << "int" << "short" << "float" << "double" << "long"
2952 << "bool" << "signed char" << "unsigned" << "unsigned char"
2953 << "unsigned int" << "unsigned long" << "long long"
2954 << "unsigned long long";
2955 return types.contains(type);
2958 static QString sizeofTypeExpression(const QString &type)
2960 if (type.endsWith('*'))
2961 return "sizeof(void*)";
2962 if (type.endsWith('>'))
2963 return "sizeof(" + type + ")";
2964 return "sizeof(" + gdbQuoteTypes(type) + ")";
2967 void GdbEngine::setUseCustomDumpers(bool on)
2969 //qDebug() << "SWITCHING ON/OFF DUMPER DEBUGGING:" << on;
2971 // FIXME: a bit too harsh, but otherwise the treeview sometimes look funny
2972 //m_expandedINames.clear();
2976 bool GdbEngine::isCustomValueDumperAvailable(const QString &type) const
2978 DebuggerSettings *s = q->settings();
2979 if (!s->m_useCustomDumpers)
2981 if (s->m_debugDumpers && qq->stackHandler()->isDebuggingDumpers())
2983 if (m_dataDumperState != DataDumperAvailable)
2987 if (m_availableSimpleDumpers.contains(type))
2993 if (!extractTemplate(type, &tmplate, &inner))
2995 return m_availableSimpleDumpers.contains(tmplate);
2998 void GdbEngine::runCustomDumper(const WatchData & data0, bool dumpChildren)
3000 WatchData data = data0;
3001 QTC_ASSERT(!data.exp.isEmpty(), return);
3004 bool isTemplate = extractTemplate(data.type, &tmplate, &inner);
3005 QStringList inners = inner.split('@');
3006 if (inners.at(0).isEmpty())
3008 for (int i = 0; i != inners.size(); ++i)
3009 inners[i] = inners[i].simplified();
3011 QString outertype = isTemplate ? tmplate : data.type;
3012 // adjust the data extract
3013 if (outertype == m_namespace + "QWidget")
3014 outertype = m_namespace + "QObject";
3016 QString extraArgs[4];
3021 int extraArgCount = 0;
3023 // "generic" template dumpers: passing sizeof(argument)
3024 // gives already most information the dumpers need
3025 foreach (const QString &arg, inners)
3026 extraArgs[extraArgCount++] = sizeofTypeExpression(arg);
3028 // in rare cases we need more or less:
3029 if (outertype == m_namespace + "QObject") {
3030 extraArgs[0] = "(char*)&((('"
3031 + m_namespace + "QObjectPrivate'*)&"
3032 + data.exp + ")->children)-(char*)&" + data.exp;
3033 } else if (outertype == m_namespace + "QVector") {
3034 extraArgs[1] = "(char*)&(("
3035 + data.exp + ").d->array)-(char*)" + data.exp + ".d";
3036 } else if (outertype == m_namespace + "QObjectSlot"
3037 || outertype == m_namespace + "QObjectSignal") {
3038 // we need the number out of something like
3039 // iname="local.ob.slots.[2]deleteLater()"
3040 int lastOpened = data.iname.lastIndexOf('[');
3041 int lastClosed = data.iname.lastIndexOf(']');
3042 QString slotNumber = "-1";
3043 if (lastOpened != -1 && lastClosed != -1)
3044 slotNumber = data.iname.mid(lastOpened + 1, lastClosed - lastOpened - 1);
3045 extraArgs[0] = slotNumber;
3046 } else if (outertype == m_namespace + "QMap" || outertype == m_namespace + "QMultiMap") {
3048 if (m_qtVersion >= (4 << 16) + (5 << 8) + 0) {
3049 nodetype = m_namespace + "QMapNode";
3050 nodetype += data.type.mid(outertype.size());
3052 // FIXME: doesn't work for QMultiMap
3053 nodetype = data.type + "::Node";
3055 //qDebug() << "OUTERTYPE: " << outertype << " NODETYPE: " << nodetype
3056 // << "QT VERSION" << m_qtVersion << ((4 << 16) + (5 << 8) + 0);
3057 extraArgs[2] = sizeofTypeExpression(nodetype);
3058 extraArgs[3] = "(size_t)&(('" + nodetype + "'*)0)->value";
3059 } else if (outertype == m_namespace + "QMapNode") {
3060 extraArgs[2] = sizeofTypeExpression(data.type);
3061 extraArgs[3] = "(size_t)&(('" + data.type + "'*)0)->value";
3062 } else if (outertype == "std::vector") {
3063 //qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
3064 if (inners.at(0) == "bool") {
3065 outertype = "std::vector::bool";
3067 //extraArgs[extraArgCount++] = sizeofTypeExpression(data.type);
3068 //extraArgs[extraArgCount++] = "(size_t)&(('" + data.type + "'*)0)->value";
3070 } else if (outertype == "std::deque") {
3071 // remove 'std::allocator<...>':
3073 } else if (outertype == "std::stack") {
3074 // remove 'std::allocator<...>':
3076 } else if (outertype == "std::map") {
3077 // We don't want the comparator and the allocator confuse gdb.
3078 // But we need the offset of the second item in the value pair.
3079 // We read the type of the pair from the allocator argument because
3080 // that gets the constness "right" (in the sense that gdb can
3082 QString pairType = inners.at(3);
3083 // remove 'std::allocator<...>':
3084 pairType = pairType.mid(15, pairType.size() - 15 - 2);
3085 extraArgs[2] = "(size_t)&(('" + pairType + "'*)0)->second";
3087 } else if (outertype == "std::basic_string") {
3088 //qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
3089 if (inners.at(0) == "char") {
3090 outertype = "std::string";
3091 } else if (inners.at(0) == "wchar_t") {
3092 outertype = "std::wstring";
3100 //int protocol = (data.iname.startsWith("watch") && data.type == "QImage") ? 3 : 2;
3101 //int protocol = data.iname.startsWith("watch") ? 3 : 2;
3103 //int protocol = isDisplayedIName(data.iname) ? 3 : 2;
3106 if (data.addr.startsWith("0x"))
3107 addr = "(void*)" + data.addr;
3109 addr = "&(" + data.exp + ")";
3112 params.append(outertype);
3113 params.append('\0');
3114 params.append(data.iname);
3115 params.append('\0');
3116 params.append(data.exp);
3117 params.append('\0');
3118 params.append(inner);
3119 params.append('\0');
3120 params.append(data.iname);
3121 params.append('\0');
3123 sendWatchParameters(params);
3125 QString cmd ="call "
3126 + QString("qDumpObjectData440(")
3127 + QString::number(protocol)
3128 + ',' + "%1+1" // placeholder for token
3130 + ',' + (dumpChildren ? "1" : "0")
3131 + ',' + extraArgs[0]
3132 + ',' + extraArgs[1]
3133 + ',' + extraArgs[2]
3134 + ',' + extraArgs[3] + ')';
3136 //qDebug() << "CMD: " << cmd;
3140 sendSynchronizedCommand(cmd, WatchDumpCustomValue1, var);
3142 q->showStatusMessage(
3143 tr("Retrieving data for watch view (%1 requests pending)...")
3144 .arg(m_pendingRequests + 1), 10000);
3146 // retrieve response
3147 sendSynchronizedCommand("p (char*)qDumpOutBuffer", WatchDumpCustomValue2, var);
3150 void GdbEngine::createGdbVariable(const WatchData &data)
3152 sendSynchronizedCommand("-var-delete \"" + data.iname + '"');
3153 QString exp = data.exp;
3154 if (exp.isEmpty() && data.addr.startsWith("0x"))
3155 exp = "*(" + gdbQuoteTypes(data.type) + "*)" + data.addr;
3156 QVariant val = QVariant::fromValue<WatchData>(data);
3157 sendSynchronizedCommand("-var-create \"" + data.iname + '"' + " * "
3158 + '"' + exp + '"', WatchVarCreate, val);
3161 void GdbEngine::updateSubItem(const WatchData &data0)
3163 WatchData data = data0;
3165 qDebug() << "UPDATE SUBITEM: " << data.toString();
3167 QTC_ASSERT(data.isValid(), return);
3169 // in any case we need the type first
3170 if (data.isTypeNeeded()) {
3171 // This should only happen if we don't have a variable yet.
3172 // Let's play safe, though.
3173 if (!data.variable.isEmpty()) {
3174 // Update: It does so for out-of-scope watchers.
3176 qDebug() << "FIXME: GdbEngine::updateSubItem: "
3177 << data.toString() << "should not happen";
3179 data.setType("<out of scope>");
3180 data.setValue("<out of scope>");
3181 data.setChildCount(0);
3186 // The WatchVarCreate handler will receive type information
3187 // and re-insert a WatchData item with correct type, so
3188 // we will not re-enter this bit.
3189 // FIXME: Concurrency issues?
3190 createGdbVariable(data);
3194 // we should have a type now. this is relied upon further below
3195 QTC_ASSERT(!data.type.isEmpty(), return);
3197 // a common case that can be easily solved
3198 if (data.isChildrenNeeded() && isPointerType(data.type)
3199 && !isCustomValueDumperAvailable(data.type)) {
3200 // We sometimes know what kind of children pointers have
3202 qDebug() << "IT'S A POINTER";
3206 data1.iname = data.iname + ".*";
3207 data1.name = "*" + data.name;
3208 data1.exp = "(*(" + data.exp + "))";
3209 data1.type = stripPointerType(data.type);
3210 data1.setValueNeeded();
3212 data.setChildrenUnneeded();
3215 // Try automatic dereferentiation
3216 data.exp = "*(" + data.exp + ")";
3217 data.type = data.type + "."; // FIXME: fragile HACK to avoid recursion
3223 if (data.isValueNeeded() && isCustomValueDumperAvailable(data.type)) {
3225 qDebug() << "UPDATE SUBITEM: CUSTOMVALUE";
3227 runCustomDumper(data, qq->watchHandler()->isExpandedIName(data.iname));
3232 if (data.isValueNeeded() && data.exp.isEmpty()) {
3234 qDebug() << "UPDATE SUBITEM: NO EXPRESSION?";
3236 data.setError("<no expression given>");
3242 if (data.isValueNeeded() && data.variable.isEmpty()) {
3244 qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR VALUE";
3246 createGdbVariable(data);
3247 // the WatchVarCreate handler will re-insert a WatchData
3248 // item, with valueNeeded() set.
3252 if (data.isValueNeeded()) {
3253 QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
3255 qDebug() << "UPDATE SUBITEM: VALUE";
3257 QString cmd = "-var-evaluate-expression \"" + data.iname + "\"";
3258 sendSynchronizedCommand(cmd, WatchEvaluateExpression,
3259 QVariant::fromValue(data));
3263 if (data.isChildrenNeeded() && isCustomValueDumperAvailable(data.type)) {
3265 qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN";
3267 runCustomDumper(data, true);
3271 if (data.isChildrenNeeded() && data.variable.isEmpty()) {
3273 qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDREN";
3275 createGdbVariable(data);
3276 // the WatchVarCreate handler will re-insert a WatchData
3277 // item, with childrenNeeded() set.
3281 if (data.isChildrenNeeded()) {
3282 QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
3283 QString cmd = "-var-list-children --all-values \"" + data.variable + "\"";
3284 sendSynchronizedCommand(cmd, WatchVarListChildren, QVariant::fromValue(data));
3288 if (data.isChildCountNeeded() && isCustomValueDumperAvailable(data.type)) {
3290 qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN";
3292 runCustomDumper(data, qq->watchHandler()->isExpandedIName(data.iname));
3296 if (data.isChildCountNeeded() && data.variable.isEmpty()) {
3298 qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDCOUNT";
3300 createGdbVariable(data);
3301 // the WatchVarCreate handler will re-insert a WatchData
3302 // item, with childrenNeeded() set.
3306 if (data.isChildCountNeeded()) {
3307 QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
3308 QString cmd = "-var-list-children --all-values \"" + data.variable + "\"";
3309 sendCommand(cmd, WatchVarListChildren, QVariant::fromValue(data));
3313 qDebug() << "FIXME: UPDATE SUBITEM: " << data.toString();
3314 QTC_ASSERT(false, return);
3317 void GdbEngine::updateWatchModel()
3319 m_pendingRequests = 0;
3320 PENDING_DEBUG("EXTERNAL TRIGGERING UPDATE WATCH MODEL");
3321 updateWatchModel2();
3324 void GdbEngine::updateWatchModel2()
3326 PENDING_DEBUG("UPDATE WATCH MODEL");
3327 QList<WatchData> incomplete = qq->watchHandler()->takeCurrentIncompletes();
3328 //QTC_ASSERT(incomplete.isEmpty(), /**/);
3329 if (!incomplete.isEmpty()) {
3331 qDebug() << "##############################################";
3332 qDebug() << "UPDATE MODEL, FOUND INCOMPLETES:";
3333 foreach (const WatchData &data, incomplete)
3334 qDebug() << data.toString();
3337 // Bump requests to avoid model rebuilding during the nested
3338 // updateWatchModel runs.
3339 ++m_pendingRequests;
3340 foreach (const WatchData &data, incomplete)
3341 updateSubItem(data);
3342 PENDING_DEBUG("INTERNAL TRIGGERING UPDATE WATCH MODEL");
3343 updateWatchModel2();
3344 --m_pendingRequests;
3349 if (m_pendingRequests > 0) {
3350 PENDING_DEBUG("UPDATE MODEL, PENDING: " << m_pendingRequests);
3354 PENDING_DEBUG("REBUILDING MODEL");
3355 emit gdbInputAvailable(QString(),
3356 "[" + currentTime() + "] <Rebuild Watchmodel>");
3357 q->showStatusMessage(tr("Finished retrieving data."), 400);
3358 qq->watchHandler()->rebuildModel();
3360 if (!m_toolTipExpression.isEmpty()) {
3361 WatchData *data = qq->watchHandler()->findData(tooltipIName);
3363 //m_toolTipCache[data->exp] = *data;
3364 QToolTip::showText(m_toolTipPos,
3365 "(" + data->type + ") " + data->exp + " = " + data->value);
3367 QToolTip::showText(m_toolTipPos,
3368 "Cannot evaluate expression: " + m_toolTipExpression);
3373 void GdbEngine::handleQueryDataDumper1(const GdbResultRecord &record)
3378 void GdbEngine::handleQueryDataDumper2(const GdbResultRecord &record)
3380 //qDebug() << "DATA DUMPER TRIAL:" << record.toString();
3381 GdbMi output = record.data.findChild("consolestreamoutput");
3382 QByteArray out = output.data();
3383 out = out.mid(out.indexOf('"') + 2); // + 1 is success marker
3384 out = out.left(out.lastIndexOf('"'));
3385 //out.replace('\'', '"');
3386 out.replace("\\", "");
3387 out = "dummy={" + out + "}";
3388 //qDebug() << "OUTPUT: " << out;
3391 contents.fromString(out);
3392 GdbMi simple = contents.findChild("dumpers");
3393 m_namespace = contents.findChild("namespace").data();
3394 GdbMi qtversion = contents.findChild("qtversion");
3395 if (qtversion.children().size() == 3) {
3396 m_qtVersion = (qtversion.childAt(0).data().toInt() << 16)
3397 + (qtversion.childAt(1).data().toInt() << 8)
3398 + qtversion.childAt(2).data().toInt();
3399 //qDebug() << "FOUND QT VERSION: " << qtversion.toString() << m_qtVersion;
3404 //qDebug() << "CONTENTS: " << contents.toString();
3405 //qDebug() << "SIMPLE DUMPERS: " << simple.toString();
3406 m_availableSimpleDumpers.clear();
3407 foreach (const GdbMi &item, simple.children())
3408 m_availableSimpleDumpers.append(item.data());
3409 if (m_availableSimpleDumpers.isEmpty()) {
3410 m_dataDumperState = DataDumperUnavailable;
3411 QMessageBox::warning(q->mainWindow(),
3412 tr("Cannot find special data dumpers"),
3413 tr("The debugged binary does not contain information needed for "
3414 "nice display of Qt data types.\n\n"
3415 "You might want to try including the file\n\n"
3416 ".../share/qtcreator/gdbmacros/gdbmacros.cpp\n\n"
3417 "into your project directly.")
3420 m_dataDumperState = DataDumperAvailable;
3422 //qDebug() << "DATA DUMPERS AVAILABLE" << m_availableSimpleDumpers;
3425 void GdbEngine::sendWatchParameters(const QByteArray ¶ms0)
3427 QByteArray params = params0;
3428 params.append('\0');
3430 sprintf(buf, "set {char[%d]} qDumpInBuffer = {", params.size());
3432 encoded.append(buf);
3433 for (int i = 0; i != params.size(); ++i) {
3434 sprintf(buf, "%d,", int(params[i]));
3435 encoded.append(buf);
3437 encoded[encoded.size() - 1] = '}';
3439 sendCommand(encoded);
3442 void GdbEngine::handleVarAssign()
3444 // everything might have changed, force re-evaluation
3445 // FIXME: Speed this up by re-using variables and only
3446 // marking values as 'unknown'
3450 void GdbEngine::setWatchDataType(WatchData &data, const GdbMi &mi)
3453 if (!data.framekey.isEmpty())
3454 m_varToType[data.framekey] = mi.data();
3455 data.setType(mi.data());
3456 } else if (data.type.isEmpty()) {
3457 data.setTypeNeeded();
3461 void GdbEngine::handleVarCreate(const GdbResultRecord &record,
3462 const WatchData &data0)
3464 WatchData data = data0;
3465 // happens e.g. when we already issued a var-evaluate command
3466 if (!data.isValid())
3468 //qDebug() << "HANDLE VARIABLE CREATION: " << data.toString();
3469 if (record.resultClass == GdbResultDone) {
3470 data.variable = data.iname;
3471 setWatchDataType(data, record.data.findChild("type"));
3472 if (isCustomValueDumperAvailable(data.type)) {
3473 // we do not trust gdb if we have a custom dumper
3474 if (record.data.findChild("children").isValid())
3475 data.setChildrenUnneeded();
3476 else if (qq->watchHandler()->isExpandedIName(data.iname))
3477 data.setChildrenNeeded();
3480 if (record.data.findChild("children").isValid())
3481 data.setChildrenUnneeded();
3482 else if (qq->watchHandler()->isExpandedIName(data.iname))
3483 data.setChildrenNeeded();
3484 setWatchDataChildCount(data, record.data.findChild("numchild"));
3485 //if (data.isValueNeeded() && data.childCount > 0)
3486 // data.setValue(QByteArray());
3489 } else if (record.resultClass == GdbResultError) {
3490 data.setError(record.data.findChild("msg").data());
3491 if (data.isWatcher()) {
3492 data.value = strNotInScope;
3494 data.setAllUnneeded();
3495 data.setChildCount(0);
3496 data.valuedisabled = true;
3502 void GdbEngine::handleEvaluateExpression(const GdbResultRecord &record,
3503 const WatchData &data0)
3505 WatchData data = data0;
3506 QTC_ASSERT(data.isValid(), qDebug() << "HUH?");
3507 if (record.resultClass == GdbResultDone) {
3509 // data.name = record.data.findChild("value").data();
3511 setWatchDataValue(data, record.data.findChild("value"));
3512 } else if (record.resultClass == GdbResultError) {
3513 data.setError(record.data.findChild("msg").data());
3515 //qDebug() << "HANDLE EVALUATE EXPRESSION: " << data.toString();
3517 //updateWatchModel2();
3520 void GdbEngine::handleDumpCustomSetup(const GdbResultRecord &record)
3522 //qDebug() << "CUSTOM SETUP RESULT: " << record.toString();
3523 if (record.resultClass == GdbResultDone) {
3524 } else if (record.resultClass == GdbResultError) {
3525 QString msg = record.data.findChild("msg").data();
3526 //qDebug() << "CUSTOM DUMPER SETUP ERROR MESSAGE: " << msg;
3527 q->showStatusMessage(tr("Custom dumper setup: %1").arg(msg), 10000);
3531 void GdbEngine::handleDumpCustomValue1(const GdbResultRecord &record,
3532 const WatchData &data0)
3534 WatchData data = data0;
3535 QTC_ASSERT(data.isValid(), return);
3536 if (record.resultClass == GdbResultDone) {
3537 // ignore this case, data will follow
3538 } else if (record.resultClass == GdbResultError) {
3539 // Record an extra result, as the socket result will be lost
3541 //--m_pendingRequests;
3542 QString msg = record.data.findChild("msg").data();
3543 //qDebug() << "CUSTOM DUMPER ERROR MESSAGE: " << msg;
3545 // Make debugging of dumpers easier
3546 if (q->settings()->m_debugDumpers
3547 && msg.startsWith("The program being debugged stopped while")
3548 && msg.contains("qDumpObjectData440")) {
3550 sendCommand("p 0", GdbAsyncOutput2); // dummy
3554 //if (msg.startsWith("The program being debugged was sig"))
3555 // msg = strNotInScope;
3556 //if (msg.startsWith("The program being debugged stopped while"))
3557 // msg = strNotInScope;
3558 //data.setError(msg);
3563 void GdbEngine::handleDumpCustomValue2(const GdbResultRecord &record,
3564 const WatchData &data0)
3566 WatchData data = data0;
3567 QTC_ASSERT(data.isValid(), return);
3568 //qDebug() << "CUSTOM VALUE RESULT: " << record.toString();
3569 //qDebug() << "FOR DATA: " << data.toString() << record.resultClass;
3570 if (record.resultClass != GdbResultDone) {
3571 qDebug() << "STRANGE CUSTOM DUMPER RESULT DATA: " << data.toString();
3575 GdbMi output = record.data.findChild("consolestreamoutput");
3576 QByteArray out = output.data();
3578 int markerPos = out.indexOf('"') + 1; // position of 'success marker'
3579 if (markerPos == -1 || out.at(markerPos) == 'f') { // 't' or 'f'
3580 // custom dumper produced no output
3581 data.setError(strNotInScope);
3586 out = out.mid(markerPos + 1);
3587 out = out.left(out.lastIndexOf('"'));
3588 out.replace("\\", "");
3589 out = "dummy={" + out + "}";
3592 contents.fromString(out);
3593 //qDebug() << "CONTENTS" << contents.toString(true);
3594 if (!contents.isValid()) {
3595 data.setError(strNotInScope);
3600 setWatchDataType(data, contents.findChild("type"));
3601 setWatchDataValue(data, contents.findChild("value"),
3602 contents.findChild("valueencoded").data().toInt());
3603 setWatchDataAddress(data, contents.findChild("addr"));
3604 setWatchDataChildCount(data, contents.findChild("numchild"));
3605 setWatchDataValueToolTip(data, contents.findChild("valuetooltip"));
3606 setWatchDataValueDisabled(data, contents.findChild("valuedisabled"));
3607 setWatchDataEditValue(data, contents.findChild("editvalue"));
3608 if (qq->watchHandler()->isDisplayedIName(data.iname)) {
3609 GdbMi editvalue = contents.findChild("editvalue");
3610 if (editvalue.isValid()) {
3611 setWatchDataEditValue(data, editvalue);
3612 qq->watchHandler()->showEditValue(data);
3615 if (!qq->watchHandler()->isExpandedIName(data.iname))
3616 data.setChildrenUnneeded();
3617 GdbMi children = contents.findChild("children");
3618 if (children.isValid() || !qq->watchHandler()->isExpandedIName(data.iname))
3619 data.setChildrenUnneeded();
3620 data.setValueUnneeded();
3622 // try not to repeat data too often
3623 WatchData childtemplate;
3624 setWatchDataType(childtemplate, contents.findChild("childtype"));
3625 setWatchDataChildCount(childtemplate, contents.findChild("childnumchild"));
3626 //qDebug() << "DATA: " << data.toString();
3628 foreach (GdbMi item, children.children()) {
3629 WatchData data1 = childtemplate;
3630 data1.name = item.findChild("name").data();
3631 data1.iname = data.iname + "." + data1.name;
3632 if (!data1.name.isEmpty() && data1.name.at(0).isDigit())
3633 data1.name = '[' + data1.name + ']';
3634 QString key = item.findChild("key").data();
3635 if (!key.isEmpty()) {
3636 if (item.findChild("keyencoded").data()[0] == '1') {
3637 key = '"' + QByteArray::fromBase64(key.toUtf8()) + '"';
3638 if (key.size() > 13) {
3643 //data1.name += " (" + key + ")";
3646 setWatchDataType(data1, item.findChild("type"));
3647 setWatchDataExpression(data1, item.findChild("exp"));
3648 setWatchDataChildCount(data1, item.findChild("numchild"));
3649 setWatchDataValue(data1, item.findChild("value"),
3650 item.findChild("valueencoded").data().toInt());
3651 setWatchDataAddress(data1, item.findChild("addr"));
3652 setWatchDataValueToolTip(data1, item.findChild("valuetooltip"));
3653 setWatchDataValueDisabled(data1, item.findChild("valuedisabled"));
3654 if (!qq->watchHandler()->isExpandedIName(data1.iname))
3655 data1.setChildrenUnneeded();
3656 //qDebug() << "HANDLE CUSTOM SUBCONTENTS:" << data1.toString();
3661 void GdbEngine::updateLocals()
3665 m_pendingRequests = 0;
3667 PENDING_DEBUG("\nRESET PENDING");
3668 m_toolTipCache.clear();
3669 m_toolTipExpression.clear();
3670 qq->watchHandler()->reinitializeWatchers();
3672 int level = currentFrame();
3673 // '2' is 'list with type and value'
3674 QString cmd = QString("-stack-list-arguments 2 %1 %2").arg(level).arg(level);
3675 sendSynchronizedCommand(cmd, StackListArguments); // stage 1/2
3676 // '2' is 'list with type and value'
3677 sendSynchronizedCommand("-stack-list-locals 2", StackListLocals); // stage 2/2
3679 //tryLoadCustomDumpers();
3682 void GdbEngine::handleStackListArguments(const GdbResultRecord &record)
3687 // 12^done,stack-args=
3688 // [frame={level="0",args=[
3689 // {name="argc",type="int",value="1"},
3690 // {name="argv",type="char **",value="(char **) 0x7..."}]}]
3692 // 78^done,stack-args=
3693 // {frame={level="0",args={
3695 // {exp="this",value="0x38a2fab0",name="var21",numchild="3",
3696 // type="CurrentDocumentFind * const",typecode="PTR",
3697 // dynamic_type="",in_scope="true",block_start_addr="0x3938e946",
3698 // block_end_addr="0x3938eb2d"},
3700 // {exp="before",value="@0xbfffb9f8: {d = 0x3a7f2a70}",
3701 // name="var22",numchild="1",type="const QString ...} }}}
3703 // In both cases, iterating over the children of stack-args/frame/args
3705 m_currentFunctionArgs.clear();
3706 if (record.resultClass == GdbResultDone) {
3707 const GdbMi list = record.data.findChild("stack-args");
3708 const GdbMi frame = list.findChild("frame");
3709 const GdbMi args = frame.findChild("args");
3710 m_currentFunctionArgs = args.children();
3711 } else if (record.resultClass == GdbResultError) {
3712 qDebug() << "FIXME: GdbEngine::handleStackListArguments: should not happen";
3716 void GdbEngine::handleStackListLocals(const GdbResultRecord &record)
3720 // There could be shadowed variables
3721 QList<GdbMi> locals = record.data.findChild("locals").children();
3722 locals += m_currentFunctionArgs;
3727 void GdbEngine::setLocals(const QList<GdbMi> &locals)
3729 //qDebug() << m_varToType;
3730 QMap<QString, int> seen;
3732 foreach (const GdbMi &item, locals) {
3733 // Local variables of inlined code are reported as
3734 // 26^done,locals={varobj={exp="this",value="",name="var4",exp="this",
3735 // numchild="1",type="const QtSharedPointer::Basic<CPlusPlus::..."
3736 // We do not want these at all. Current hypotheses is that those
3737 // "spurious" locals have _two_ "exp" field. Try to filter them:
3740 foreach (const GdbMi &child, item.children())
3741 numExps += int(child.name() == "exp");
3744 QString name = item.findChild("exp").data();
3746 QString name = item.findChild("name").data();
3748 int n = seen.value(name);
3752 data.iname = "local." + name + QString::number(n + 1);
3753 data.name = name + QString(" <shadowed %1>").arg(n);
3754 //data.setValue("<shadowed>");
3755 setWatchDataValue(data, item.findChild("value"));
3756 data.setType("<shadowed>");
3757 data.setChildCount(0);
3762 data.iname = "local." + name;
3765 data.framekey = m_currentFrame + data.name;
3766 setWatchDataType(data, item.findChild("type"));
3767 // set value only directly if it is simple enough, otherwise
3768 // pass through the insertData() machinery
3769 if (isIntOrFloatType(data.type) || isPointerType(data.type))
3770 setWatchDataValue(data, item.findChild("value"));
3771 if (!qq->watchHandler()->isExpandedIName(data.iname))
3772 data.setChildrenUnneeded();
3773 if (isPointerType(data.type) || data.name == "this")
3774 data.setChildCount(1);
3775 if (0 && m_varToType.contains(data.framekey)) {
3776 qDebug() << "RE-USING " << m_varToType.value(data.framekey);
3777 data.setType(m_varToType.value(data.framekey));
3784 void GdbEngine::insertData(const WatchData &data0)
3786 //qDebug() << "INSERT DATA" << data0.toString();
3787 WatchData data = data0;
3788 if (data.value.startsWith("mi_cmd_var_create:")) {
3789 qDebug() << "BOGUS VALUE: " << data.toString();
3792 qq->watchHandler()->insertData(data);
3795 void GdbEngine::handleTypeContents(const QString &output)
3797 // output.startsWith("type = ") == true
3799 // "type = class QString {"
3800 // "type = class QStringList : public QList<QString> {"
3803 if (output.startsWith("type = class")) {
3804 int posBrace = output.indexOf('{');
3805 QString head = output.mid(13, posBrace - 13 - 1);
3806 int posColon = head.indexOf(": public");
3808 posColon = head.indexOf(": protected");
3810 posColon = head.indexOf(": private");
3811 if (posColon == -1) {
3813 tip = "class " + className + " { ... }";
3815 className = head.left(posColon - 1);
3816 tip = "class " + head + " { ... }";
3818 //qDebug() << "posColon: " << posColon;
3819 //qDebug() << "posBrace: " << posBrace;
3820 //qDebug() << "head: " << head;
3822 className = output.mid(7);
3825 //qDebug() << "output: " << output.left(100) + "...";
3826 //qDebug() << "className: " << className;
3827 //qDebug() << "tip: " << tip;
3828 //m_toolTip.type = className;
3829 m_toolTip.type.clear();
3830 m_toolTip.value = tip;
3833 void GdbEngine::handleVarListChildrenHelper(const GdbMi &item,
3834 const WatchData &parent)
3836 //qDebug() << "VAR_LIST_CHILDREN: PARENT 2" << parent.toString();
3837 //qDebug() << "VAR_LIST_CHILDREN: APPENDEE " << data.toString();
3838 QByteArray exp = item.findChild("exp").data();
3839 QByteArray name = item.findChild("name").data();
3840 if (isAccessSpecifier(exp)) {
3841 // suppress 'private'/'protected'/'public' level
3843 data.variable = name;
3844 data.iname = parent.iname;
3845 //data.iname = data.variable;
3846 data.exp = parent.exp;
3847 data.setTypeUnneeded();
3848 data.setValueUnneeded();
3849 data.setChildCountUnneeded();
3850 data.setChildrenUnneeded();
3851 //qDebug() << "DATA" << data.toString();
3852 QString cmd = "-var-list-children --all-values \"" + data.variable + "\"";
3853 //iname += '.' + exp;
3854 sendSynchronizedCommand(cmd, WatchVarListChildren, QVariant::fromValue(data));
3855 } else if (item.findChild("numchild").data() == "0") {
3856 // happens for structs without data, e.g. interfaces.
3858 data.iname = parent.iname + '.' + exp;
3860 data.variable = name;
3861 setWatchDataType(data, item.findChild("type"));
3862 setWatchDataValue(data, item.findChild("value"));
3863 setWatchDataAddress(data, item.findChild("addr"));
3864 data.setChildCount(0);
3866 } else if (parent.iname.endsWith('.')) {
3867 // Happens with anonymous unions
3870 QString cmd = "-var-list-children --all-values \"" + data.variable + "\"";
3871 sendSynchronizedCommand(cmd, WatchVarListChildren, QVariant::fromValue(data));
3872 } else if (exp == "staticMetaObject") {
3873 // && item.findChild("type").data() == "const QMetaObject")
3874 // FIXME: Namespaces?
3875 // { do nothing } FIXME: make coinfigurable?
3876 // special "clever" hack to avoid clutter in the GUI.
3877 // I am not sure this is a good idea...
3880 data.iname = parent.iname + '.' + exp;
3881 data.variable = name;
3882 setWatchDataType(data, item.findChild("type"));
3883 setWatchDataValue(data, item.findChild("value"));
3884 setWatchDataAddress(data, item.findChild("addr"));
3885 setWatchDataChildCount(data, item.findChild("numchild"));
3886 if (!qq->watchHandler()->isExpandedIName(data.iname))
3887 data.setChildrenUnneeded();
3890 if (isPointerType(parent.type) && data.type == exp) {
3891 data.exp = "*(" + parent.exp + ")";
3892 data.name = "*" + parent.name;
3893 } else if (data.type == exp) {
3894 // A type we derive from? gdb crashes when creating variables here
3895 data.exp = parent.exp;
3896 } else if (exp.startsWith("*")) {
3898 data.exp = "*(" + parent.exp + ")";
3899 } else if (startsWithDigit(exp)) {
3900 // An array. No variables needed?
3901 data.name = "[" + data.name + "]";
3902 data.exp = parent.exp + "[" + exp + "]";
3903 } else if (0 && parent.name.endsWith('.')) {
3904 // Happens with anonymous unions
3905 data.exp = parent.exp + exp;
3906 //data.name = "<anonymous union>";
3907 } else if (exp.isEmpty()) {
3908 // Happens with anonymous unions
3909 data.exp = parent.exp;
3910 data.name = "<n/a>";
3911 data.iname = parent.iname + ".@";
3912 data.type = "<anonymous union>";
3914 // A structure. Hope there's nothing else...
3915 data.exp = parent.exp + '.' + exp;
3918 if (isCustomValueDumperAvailable(data.type)) {
3919 // we do not trust gdb if we have a custom dumper
3920 data.setValueNeeded();
3921 data.setChildCountNeeded();
3924 //qDebug() << "VAR_LIST_CHILDREN: PARENT 3" << parent.toString();
3925 //qDebug() << "VAR_LIST_CHILDREN: APPENDEE " << data.toString();
3930 void GdbEngine::handleVarListChildren(const GdbResultRecord &record,
3931 const WatchData &data0)
3933 //WatchResultCounter dummy(this, WatchVarListChildren);
3934 WatchData data = data0;
3935 if (!data.isValid())
3937 if (record.resultClass == GdbResultDone) {
3938 //qDebug() << "VAR_LIST_CHILDREN: PARENT " << data.toString();
3939 GdbMi children = record.data.findChild("children");
3941 foreach (const GdbMi &child, children.children())
3942 handleVarListChildrenHelper(child, data);
3944 if (!isAccessSpecifier(data.variable.split('.').takeLast())) {
3945 data.setChildrenUnneeded();
3948 } else if (record.resultClass == GdbResultError) {
3949 data.setError(record.data.findChild("msg").data());
3951 data.setError("Unknown error: " + record.toString());
3955 void GdbEngine::handleToolTip(const GdbResultRecord &record,
3956 const QString &what)
3958 //qDebug() << "HANDLE TOOLTIP: " << what << m_toolTip.toString();
3959 // << "record: " << record.toString();
3960 if (record.resultClass == GdbResultError) {
3961 QString msg = record.data.findChild("msg").data();
3962 if (what == "create") {
3963 sendCommand("ptype " + m_toolTip.exp, WatchToolTip, "ptype");
3966 if (what == "evaluate") {
3967 if (msg.startsWith("Cannot look up value of a typedef")) {
3968 m_toolTip.value = m_toolTip.exp + " is a typedef.";
3972 } else if (record.resultClass == GdbResultDone) {
3973 if (what == "create") {
3974 setWatchDataType(m_toolTip, record.data.findChild("type"));
3975 setWatchDataChildCount(m_toolTip, record.data.findChild("numchild"));
3976 if (isCustomValueDumperAvailable(m_toolTip.type))
3977 runCustomDumper(m_toolTip, false);
3979 q->showStatusMessage(tr("Retrieving data for tooltip..."), 10000);
3980 sendCommand("-data-evaluate-expression " + m_toolTip.exp,
3981 WatchToolTip, "evaluate");
3982 //sendToolTipCommand("-var-evaluate-expression tooltip")
3985 if (what == "evaluate") {
3986 m_toolTip.value = m_toolTip.type + ' ' + m_toolTip.exp
3987 + " = " + record.data.findChild("value").data();
3990 if (what == "ptype") {
3991 GdbMi mi = record.data.findChild("consolestreamoutput");
3992 m_toolTip.value = extractTypeFromPTypeOutput(mi.data());
3997 m_toolTip.iname = tooltipIName;
3998 m_toolTip.setChildrenUnneeded();
3999 m_toolTip.setChildCountUnneeded();
4000 insertData(m_toolTip);
4001 qDebug() << "DATA INSERTED";
4002 QTimer::singleShot(0, this, SLOT(updateWatchModel2()));
4003 qDebug() << "HANDLE TOOLTIP END";
4007 void GdbEngine::handleChangedItem(QStandardItem *item)
4009 // HACK: Just store the item for the slot
4010 // handleChangedItem(QWidget *widget) below.
4011 QModelIndex index = item->index().sibling(item->index().row(), 0);
4012 //WatchData data = m_currentSet.takeData(iname);
4013 //m_editedData = inameFromItem(m_model.itemFromIndex(index)).exp;
4014 //qDebug() << "HANDLE CHANGED EXPRESSION: " << m_editedData;
4018 void GdbEngine::assignValueInDebugger(const QString &expression, const QString &value)
4020 sendCommand("-var-delete assign");
4021 sendCommand("-var-create assign * " + expression);
4022 sendCommand("-var-assign assign " + value, WatchVarAssign);
4025 void GdbEngine::tryLoadCustomDumpers()
4027 if (m_dataDumperState != DataDumperUninitialized)
4030 PENDING_DEBUG("TRY LOAD CUSTOM DUMPERS");
4031 m_dataDumperState = DataDumperUnavailable;
4033 #if defined(Q_OS_LINUX)
4034 QString lib = q->m_buildDir + "/qtc-gdbmacros/libgdbmacros.so";
4035 if (QFileInfo(lib).exists()) {
4036 m_dataDumperState = DataDumperLoadTried;
4037 //sendCommand("p dlopen");
4038 QString flag = QString::number(RTLD_NOW);
4039 sendCommand("sharedlibrary libc"); // for malloc
4040 sendCommand("sharedlibrary libdl"); // for dlopen
4041 sendCommand("call (void)dlopen(\"" + lib + "\", " + flag + ")",
4042 WatchDumpCustomSetup);
4043 // some older systems like CentOS 4.6 prefer this:
4044 sendCommand("call (void)__dlopen(\"" + lib + "\", " + flag + ")",
4045 WatchDumpCustomSetup);
4046 sendCommand("sharedlibrary " + dotEscape(lib));
4049 #if defined(Q_OS_MAC)
4050 QString lib = q->m_buildDir + "/qtc-gdbmacros/libgdbmacros.dylib";
4051 if (QFileInfo(lib).exists()) {
4052 m_dataDumperState = DataDumperLoadTried;
4053 //sendCommand("sharedlibrary libc"); // for malloc
4054 //sendCommand("sharedlibrary libdl"); // for dlopen
4055 QString flag = QString::number(RTLD_NOW);
4056 sendCommand("call (void)dlopen(\"" + lib + "\", " + flag + ")",
4057 WatchDumpCustomSetup);
4058 //sendCommand("sharedlibrary " + dotEscape(lib));
4061 #if defined(Q_OS_WIN)
4062 QString lib = q->m_buildDir + "/qtc-gdbmacros/debug/gdbmacros.dll";
4063 if (QFileInfo(lib).exists()) {
4064 m_dataDumperState = DataDumperLoadTried;
4065 sendCommand("sharedlibrary .*"); // for LoadLibraryA
4066 //sendCommand("handle SIGSEGV pass stop print");
4067 //sendCommand("set unwindonsignal off");
4068 sendCommand("call LoadLibraryA(\"" + lib + "\")",
4069 WatchDumpCustomSetup);
4070 sendCommand("sharedlibrary " + dotEscape(lib));
4074 if (m_dataDumperState == DataDumperLoadTried) {
4075 // retreive list of dumpable classes
4076 sendCommand("call qDumpObjectData440(1,%1+1,0,0,0,0,0,0)",
4077 GdbQueryDataDumper1);
4078 sendCommand("p (char*)qDumpOutBuffer", GdbQueryDataDumper2);
4080 gdbOutputAvailable("", QString("DEBUG HELPER LIBRARY IS NOT USABLE: "
4081 " %1 EXISTS: %2, EXECUTABLE: %3").arg(lib)
4082 .arg(QFileInfo(lib).exists())
4083 .arg(QFileInfo(lib).isExecutable()));
4088 IDebuggerEngine *createGdbEngine(DebuggerManager *parent)
4090 return new GdbEngine(parent);