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,
106 GdbExecRunToFunction,
120 GdbTemporaryContinue,
122 BreakCondition = 200,
132 DisassemblerList = 300,
136 RegisterListNames = 500,
139 StackSelectThread = 600,
145 WatchVarAssign = 700, // data changed by user
146 WatchVarListChildren,
148 WatchEvaluateExpression,
150 WatchDumpCustomSetup,
151 WatchDumpCustomValue1, // waiting for gdb ack
152 WatchDumpCustomValue2, // waiting for actual data
153 WatchDumpCustomEditValue,
156 QString dotEscape(QString str)
158 str.replace(' ', '.');
159 str.replace('\\', '.');
160 str.replace('/', '.');
164 QString currentTime()
166 return QTime::currentTime().toString("hh:mm:ss.zzz");
169 static int ¤tToken()
171 static int token = 0;
175 static bool isSkippableFunction(const QString &funcName, const QString &fileName)
177 if (fileName.endsWith("kernel/qobject.cpp"))
179 if (fileName.endsWith("kernel/moc_qobject.cpp"))
181 if (fileName.endsWith("kernel/qmetaobject.cpp"))
183 if (fileName.endsWith(".moc"))
186 if (funcName.endsWith("::qt_metacall"))
192 static bool isLeavableFunction(const QString &funcName, const QString &fileName)
194 if (funcName.endsWith("QObjectPrivate::setCurrentSender"))
196 if (fileName.endsWith("kernel/qmetaobject.cpp")
197 && funcName.endsWith("QMetaObject::methodOffset"))
199 if (fileName.endsWith("kernel/qobject.h"))
201 if (fileName.endsWith("kernel/qobject.cpp")
202 && funcName.endsWith("QObjectConnectionListVector::at"))
204 if (fileName.endsWith("kernel/qobject.cpp")
205 && funcName.endsWith("~QObject"))
207 if (fileName.endsWith("thread/qmutex.cpp"))
209 if (fileName.endsWith("thread/qthread.cpp"))
211 if (fileName.endsWith("thread/qthread_unix.cpp"))
213 if (fileName.endsWith("thread/qmutex.h"))
215 if (fileName.contains("thread/qbasicatomic"))
217 if (fileName.contains("thread/qorderedmutexlocker_p"))
219 if (fileName.contains("arch/qatomic"))
221 if (fileName.endsWith("tools/qvector.h"))
223 if (fileName.endsWith("tools/qlist.h"))
225 if (fileName.endsWith("tools/qhash.h"))
227 if (fileName.endsWith("tools/qmap.h"))
229 if (fileName.endsWith("tools/qstring.h"))
231 if (fileName.endsWith("global/qglobal.h"))
237 static QString startSymbolName()
240 return "WinMainCRTStartup";
252 ///////////////////////////////////////////////////////////////////////
256 ///////////////////////////////////////////////////////////////////////
258 GdbEngine::GdbEngine(DebuggerManager *parent)
261 qq = parent->engineInterface();
262 initializeVariables();
263 initializeConnections();
266 GdbEngine::~GdbEngine()
268 // prevent sending error messages afterwards
269 m_gdbProc.disconnect(this);
272 void GdbEngine::initializeConnections()
274 // Gdb Process interaction
275 connect(&m_gdbProc, SIGNAL(error(QProcess::ProcessError)), this,
276 SLOT(gdbProcError(QProcess::ProcessError)));
277 connect(&m_gdbProc, SIGNAL(readyReadStandardOutput()), this,
278 SLOT(readGdbStandardOutput()));
279 connect(&m_gdbProc, SIGNAL(readyReadStandardError()), this,
280 SLOT(readGdbStandardError()));
281 connect(&m_gdbProc, SIGNAL(finished(int, QProcess::ExitStatus)), q,
282 SLOT(exitDebugger()));
285 connect(&m_outputCollector, SIGNAL(byteDelivery(QByteArray)),
286 SLOT(readDebugeeOutput(QByteArray)));
287 connect(this, SIGNAL(gdbResponseAvailable()),
288 this, SLOT(handleResponse()), Qt::QueuedConnection);
290 connect(this, SIGNAL(gdbOutputAvailable(QString,QString)),
291 q, SLOT(showDebuggerOutput(QString,QString)),
292 Qt::QueuedConnection);
293 connect(this, SIGNAL(gdbInputAvailable(QString,QString)),
294 q, SLOT(showDebuggerInput(QString,QString)),
295 Qt::QueuedConnection);
296 connect(this, SIGNAL(applicationOutputAvailable(QString)),
297 q, SLOT(showApplicationOutput(QString)),
298 Qt::QueuedConnection);
301 void GdbEngine::initializeVariables()
303 m_dataDumperState = DataDumperUninitialized;
305 m_gdbBuildVersion = -1;
307 m_fullToShortName.clear();
308 m_shortToFullName.clear();
311 m_modulesListOutdated = true;
312 m_oldestAcceptableToken = -1;
313 m_outputCodec = QTextCodec::codecForLocale();
314 m_pendingRequests = 0;
315 m_waitingForBreakpointSynchronizationToContinue = false;
316 m_waitingForFirstBreakpointToBeHit = false;
317 m_commandsToRunOnTemporaryBreak.clear();
320 void GdbEngine::gdbProcError(QProcess::ProcessError error)
324 case QProcess::FailedToStart:
325 msg = QString(tr("The Gdb process failed to start. Either the "
326 "invoked program '%1' is missing, or you may have insufficient "
327 "permissions to invoke the program.")).arg(q->settings()->m_gdbCmd);
329 case QProcess::Crashed:
330 msg = tr("The Gdb process crashed some time after starting "
333 case QProcess::Timedout:
334 msg = tr("The last waitFor...() function timed out. "
335 "The state of QProcess is unchanged, and you can try calling "
336 "waitFor...() again.");
338 case QProcess::WriteError:
339 msg = tr("An error occurred when attempting to write "
340 "to the Gdb process. For example, the process may not be running, "
341 "or it may have closed its input channel.");
343 case QProcess::ReadError:
344 msg = tr("An error occurred when attempting to read from "
345 "the Gdb process. For example, the process may not be running.");
348 msg = tr("An unknown error in the Gdb process occurred. "
349 "This is the default return value of error().");
352 q->showStatusMessage(msg);
353 QMessageBox::critical(q->mainWindow(), tr("Error"), msg);
354 // act as if it was closed by the core
358 static void skipSpaces(const char *&from, const char *to)
360 while (from != to && QChar(*from).isSpace())
364 static inline bool isNameChar(char c)
366 // could be 'stopped' or 'shlibs-added'
367 return (c >= 'a' && c <= 'z') || c == '-';
371 static void dump(const char *first, const char *middle, const QString & to)
373 QByteArray ba(first, middle - first);
375 // note that qDebug cuts off output after a certain size... (bug?)
376 qDebug("\n>>>>> %s\n%s\n====\n%s\n<<<<<\n",
377 qPrintable(currentTime()),
378 qPrintable(QString(ba).trimmed()),
379 qPrintable(to.trimmed()));
381 //qDebug() << qPrintable(currentTime())
382 // << " Reading response: " << QString(ba).trimmed() << "\n";
386 static void skipTerminator(const char *&from, const char *to)
388 skipSpaces(from, to);
390 if (from[0] == '(' && from[1] == 'g' && from[3] == 'b' && from[4] == ')')
392 skipSpaces(from, to);
395 void GdbEngine::readDebugeeOutput(const QByteArray &data)
397 emit applicationOutputAvailable(m_outputCodec->toUnicode(
398 data.constData(), data.length(), &m_outputCodecState));
401 void GdbEngine::debugMessage(const QString &msg)
403 emit gdbOutputAvailable("debug:", msg);
406 // called asyncronously as response to Gdb stdout output in
407 // gdbResponseAvailable()
408 void GdbEngine::handleResponse()
410 static QTime lastTime;
412 emit gdbOutputAvailable(" ", currentTime());
413 emit gdbOutputAvailable("stdout:", m_inbuffer);
416 qDebug() // << "#### start response handling #### "
418 << lastTime.msecsTo(QTime::currentTime()) << "ms,"
419 << "buf: " << m_inbuffer.left(1500) << "..."
420 //<< "buf: " << m_inbuffer
421 << "size:" << m_inbuffer.size();
423 //qDebug() << "buf: " << m_inbuffer;
426 lastTime = QTime::currentTime();
429 if (m_inbuffer.isEmpty())
432 const char *from = m_inbuffer.constData();
433 // FIXME: check for line ending in '\n(gdb)\n'
434 const char *to = from + m_inbuffer.size();
437 //const char *oldfrom = from;
439 //skipSpaces(from, to);
440 skipTerminator(from, to);
443 // token is a sequence of numbers
444 for (inner = from; inner != to; ++inner)
445 if (*inner < '0' || *inner > '9')
448 token = QString(QByteArray(from, inner - from)).toInt();
450 //qDebug() << "found token " << token;
454 //qDebug() << "Returning: " << toString();
458 // next char decides kind of record
459 const char c = *from++;
460 //qDebug() << "CODE:" << c;
466 QByteArray asyncClass;
467 for (; from != to; ++from) {
468 const char c = *from;
473 //qDebug() << "ASYNCCLASS" << asyncClass;
476 while (from != to && *from == ',') {
479 data.parseResultOrValue(from, to);
480 if (data.isValid()) {
481 //qDebug() << "parsed response: " << data.toString();
482 record.m_children += data;
483 record.m_type = GdbMi::Tuple;
486 //dump(oldfrom, from, record.toString());
487 skipTerminator(from, to);
488 m_inbuffer = QByteArray(from, to - from);
489 if (asyncClass == "stopped") {
490 handleAsyncOutput(record);
491 } else if (asyncClass == "running") {
492 // Archer has 'thread-id="all"' here
494 } else if (asyncClass == "shlibs-updated") {
495 // MAC announces updated libs
496 } else if (asyncClass == "shlibs-added") {
497 // MAC announces added libs
498 // {shlib-info={num="2", name="libmathCommon.A_debug.dylib",
499 // kind="-", dyld-addr="0x7f000", reason="dyld", requested-state="Y",
500 // state="Y", path="/usr/lib/system/libmathCommon.A_debug.dylib",
501 // description="/usr/lib/system/libmathCommon.A_debug.dylib",
502 // loaded_addr="0x7f000", slide="0x7f000", prefix=""}}
505 qDebug() << "IGNORED ASYNC OUTPUT "
506 << asyncClass << record.toString();
512 QByteArray data = GdbMi::parseCString(from, to);
513 m_pendingConsoleStreamOutput += data;
514 m_inbuffer = QByteArray(from, to - from);
519 QByteArray data = GdbMi::parseCString(from, to);
520 m_pendingTargetStreamOutput += data;
521 m_inbuffer = QByteArray(from, to - from);
526 QByteArray data = GdbMi::parseCString(from, to);
527 m_pendingLogStreamOutput += data;
528 m_inbuffer = QByteArray(from, to - from);
529 // On Windows, the contents seem to depend on the debugger
530 // version and/or OS version used.
531 if (data.startsWith("warning:"))
532 qq->showApplicationOutput(data);
537 GdbResultRecord record;
539 record.token = token;
541 for (inner = from; inner != to; ++inner)
542 if (*inner < 'a' || *inner > 'z')
545 QByteArray resultClass(from, inner - from);
547 if (resultClass == "done")
548 record.resultClass = GdbResultDone;
549 else if (resultClass == "running")
550 record.resultClass = GdbResultRunning;
551 else if (resultClass == "connected")
552 record.resultClass = GdbResultConnected;
553 else if (resultClass == "error")
554 record.resultClass = GdbResultError;
555 else if (resultClass == "exit")
556 record.resultClass = GdbResultExit;
558 record.resultClass = GdbResultUnknown;
561 skipSpaces(from, to);
562 if (from != to && *from == ',') {
564 record.data.parseTuple_helper(from, to);
565 record.data.m_type = GdbMi::Tuple;
566 record.data.m_name = "data";
568 skipSpaces(from, to);
569 skipTerminator(from, to);
571 //qDebug() << "\nLOG STREAM:" + m_pendingLogStreamOutput;
572 //qDebug() << "\nTARGET STREAM:" + m_pendingTargetStreamOutput;
573 //qDebug() << "\nCONSOLE STREAM:" + m_pendingConsoleStreamOutput;
574 record.data.setStreamOutput("logstreamoutput",
575 m_pendingLogStreamOutput);
576 record.data.setStreamOutput("targetstreamoutput",
577 m_pendingTargetStreamOutput);
578 record.data.setStreamOutput("consolestreamoutput",
579 m_pendingConsoleStreamOutput);
580 QByteArray custom = m_customOutputForToken[token];
581 if (!custom.isEmpty())
582 record.data.setStreamOutput("customvaluecontents",
584 //m_customOutputForToken.remove(token);
585 m_pendingLogStreamOutput.clear();
586 m_pendingTargetStreamOutput.clear();
587 m_pendingConsoleStreamOutput.clear();
589 //dump(oldfrom, from, record.toString());
590 m_inbuffer = QByteArray(from, to - from);
591 handleResultRecord(record);
595 qDebug() << "FIXME: UNKNOWN CODE: " << c << " IN " << m_inbuffer;
596 m_inbuffer = QByteArray(from, to - from);
602 //qDebug() << "##### end response handling ####\n\n\n"
603 // << currentTime() << lastTime.msecsTo(QTime::currentTime());
604 lastTime = QTime::currentTime();
608 static void fixMac(QByteArray &out)
610 // HACK: gdb on Mac mixes MI1 and MI2 syntax. Not nice.
611 // it returns: 9^done,locals={{name="a"},{name="w"}}
612 // instead of: 9^done,locals=[{name="a"},{name="w"}]
613 if (!out.contains("locals={{name"))
616 static const QByteArray termArray("(gdb) ");
617 int pos = out.indexOf(termArray);
621 int pos1 = out.indexOf("={{");
625 int pos2 = out.indexOf("]]");
629 if (pos1 < pos && pos2 < pos) {
636 void GdbEngine::readGdbStandardError()
638 qWarning() << "Unexpected gdb stderr:" << m_gdbProc.readAllStandardError();
641 void GdbEngine::readGdbStandardOutput()
643 // This is the function called whenever the Gdb process created
644 // output. As a rule of thumb, stdout contains _real_ Gdb output
645 // as responses to our command
646 // and "spontaneous" events like messages on loaded shared libraries.
647 // OTOH, stderr contains application output produced by qDebug etc.
648 // There is no organized way to pass application stdout output.
650 QByteArray out = m_gdbProc.readAllStandardOutput();
652 //qDebug() << "\n\n\nPLUGIN OUT: '" << out.data() << "'\n\n\n";
658 m_inbuffer.append(out);
659 //QTC_ASSERT(!m_inbuffer.isEmpty(), return);
661 char c = m_inbuffer[m_inbuffer.size() - 1];
662 static const QByteArray termArray("(gdb) ");
663 if (out.indexOf(termArray) == -1 && c != 10 && c != 13) {
664 //qDebug() << "\n\nBuffer not yet filled, waiting for more data to arrive";
665 //qDebug() << m_inbuffer.data() << m_inbuffer.size();
666 //qDebug() << "\n\n";
670 emit gdbResponseAvailable();
673 void GdbEngine::interruptInferior()
675 qq->notifyInferiorStopRequested();
676 if (m_gdbProc.state() == QProcess::NotRunning) {
677 debugMessage("TRYING TO INTERRUPT INFERIOR WITHOUT RUNNING GDB");
678 qq->notifyInferiorExited();
682 if (q->m_attachedPID > 0) {
683 if (!interruptProcess(q->m_attachedPID))
684 // qq->notifyInferiorStopped();
686 debugMessage(QString("CANNOT INTERRUPT %1").arg(q->m_attachedPID));
691 sendCommand("-exec-interrupt", GdbExecInterrupt);
692 //qq->notifyInferiorStopped();
694 if (!interruptChildProcess(m_gdbProc.pid()))
695 // qq->notifyInferiorStopped();
697 debugMessage(QString("CANNOT STOP INFERIOR"));
701 void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
703 int pid = pid0.toInt();
705 debugMessage(QString("Cannot parse PID from %1").arg(pid0));
708 if (pid == q->m_attachedPID)
710 debugMessage(QString("FOUND PID %1").arg(pid));
711 q->m_attachedPID = pid;
712 qq->notifyInferiorPidChanged(pid);
715 void GdbEngine::sendSynchronizedCommand(const QString & command,
716 int type, const QVariant &cookie, StopNeeded needStop)
718 sendCommand(command, type, cookie, needStop, Synchronized);
721 void GdbEngine::sendCommand(const QString &command, int type,
722 const QVariant &cookie, StopNeeded needStop, Synchronization synchronized)
724 if (m_gdbProc.state() == QProcess::NotRunning) {
725 debugMessage("NO GDB PROCESS RUNNING, CMD IGNORED: " + command);
731 PENDING_DEBUG(" TYPE " << type << " INCREMENTS PENDING TO: "
732 << m_pendingRequests << command);
734 PENDING_DEBUG(" UNKNOWN TYPE " << type << " LEAVES PENDING AT: "
735 << m_pendingRequests << command);
739 cmd.synchronized = synchronized;
740 cmd.command = command;
744 if (needStop && q->status() != DebuggerInferiorStopped
745 && q->status() != DebuggerProcessStartingUp) {
746 // queue the commands that we cannot send at once
747 QTC_ASSERT(q->status() == DebuggerInferiorRunning,
748 qDebug() << "STATUS: " << q->status());
749 q->showStatusMessage(tr("Stopping temporarily."));
750 debugMessage("QUEUING COMMAND " + cmd.command);
751 m_commandsToRunOnTemporaryBreak.append(cmd);
753 } else if (!command.isEmpty()) {
755 m_cookieForToken[currentToken()] = cmd;
756 cmd.command = QString::number(currentToken()) + cmd.command;
757 if (cmd.command.contains("%1"))
758 cmd.command = cmd.command.arg(currentToken());
760 m_gdbProc.write(cmd.command.toLatin1() + "\r\n");
761 //emit gdbInputAvailable(QString(), " " + currentTime());
762 //emit gdbInputAvailable(QString(), "[" + currentTime() + "] " + cmd.command);
763 emit gdbInputAvailable(QString(), cmd.command);
767 void GdbEngine::handleResultRecord(const GdbResultRecord &record)
769 //qDebug() << "TOKEN: " << record.token
770 // << " ACCEPTABLE: " << m_oldestAcceptableToken;
772 //qDebug() << "\nRESULT" << record.token << record.toString();
774 int token = record.token;
778 GdbCookie cmd = m_cookieForToken.take(token);
780 if (record.token < m_oldestAcceptableToken) {
781 //qDebug() << "### SKIPPING OLD RESULT " << record.toString();
782 //QMessageBox::information(m_mainWindow, tr("Skipped"), "xxx");
787 qDebug() << "# handleOutput, "
788 << "cmd type: " << cmd.type
789 << "cmd synchronized: " << cmd.synchronized
790 << "\n record: " << record.toString();
793 // << "\n data: " << record.data.toString(true);
795 if (cmd.type != GdbInvalidCommand)
796 handleResult(record, cmd.type, cmd.cookie);
798 if (cmd.synchronized) {
800 PENDING_DEBUG(" TYPE " << cmd.type << " DECREMENTS PENDING TO: "
801 << m_pendingRequests << cmd.command);
802 if (m_pendingRequests <= 0) {
803 PENDING_DEBUG(" .... AND TRIGGERS MODEL UPDATE");
807 PENDING_DEBUG(" UNKNOWN TYPE " << cmd.type << " LEAVES PENDING AT: "
808 << m_pendingRequests << cmd.command);
812 void GdbEngine::handleResult(const GdbResultRecord & record, int type,
813 const QVariant & cookie)
820 case GdbExecContinue:
823 handleExecRun(record);
833 handleInfoProc(record);
836 handleInfoThreads(record);
840 handleShowVersion(record);
842 case GdbFileExecAndSymbols:
843 handleFileExecAndSymbols(record);
845 case GdbExecRunToFunction:
846 // that should be "^running". We need to handle the resulting
848 //handleExecRunToFunction(record);
850 case GdbExecInterrupt:
851 qq->notifyInferiorStopped();
853 case GdbExecJumpToLine:
854 handleExecJumpToLine(record);
857 handleQueryPwd(record);
859 case GdbQuerySources:
860 handleQuerySources(record);
862 case GdbAsyncOutput2:
863 handleAsyncOutput2(cookie.value<GdbMi>());
866 handleInfoShared(record);
868 case GdbQueryDataDumper1:
869 handleQueryDataDumper1(record);
871 case GdbQueryDataDumper2:
872 handleQueryDataDumper2(record);
874 case GdbTemporaryContinue:
876 q->showStatusMessage(tr("Continuing after temporary stop."));
880 handleBreakList(record);
883 handleBreakInsert(record, cookie.toInt());
886 handleBreakInsert1(record, cookie.toInt());
889 handleBreakInfo(record, cookie.toInt());
891 case BreakEnablePending:
896 handleBreakIgnore(record, cookie.toInt());
899 handleBreakCondition(record, cookie.toInt());
902 case DisassemblerList:
903 handleDisassemblerList(record, cookie.toString());
907 handleModulesList(record);
910 case RegisterListNames:
911 handleRegisterListNames(record);
913 case RegisterListValues:
914 handleRegisterListValues(record);
917 case StackListFrames:
918 handleStackListFrames(record);
920 case StackListThreads:
921 handleStackListThreads(record, cookie.toInt());
923 case StackSelectThread:
924 handleStackSelectThread(record, cookie.toInt());
926 case StackListLocals:
927 handleStackListLocals(record);
929 case StackListArguments:
930 handleStackListArguments(record);
933 case WatchVarListChildren:
934 handleVarListChildren(record, cookie.value<WatchData>());
937 handleVarCreate(record, cookie.value<WatchData>());
942 case WatchEvaluateExpression:
943 handleEvaluateExpression(record, cookie.value<WatchData>());
946 handleToolTip(record, cookie.toString());
948 case WatchDumpCustomValue1:
949 handleDumpCustomValue1(record, cookie.value<WatchData>());
951 case WatchDumpCustomValue2:
952 handleDumpCustomValue2(record, cookie.value<WatchData>());
954 case WatchDumpCustomSetup:
955 handleDumpCustomSetup(record);
959 debugMessage(QString("FIXME: GdbEngine::handleResult: "
960 "should not happen %1").arg(type));
965 void GdbEngine::executeDebuggerCommand(const QString &command)
967 //createGdbProcessIfNeeded();
968 if (m_gdbProc.state() == QProcess::NotRunning) {
969 debugMessage("NO GDB PROCESS RUNNING, PLAIN CMD IGNORED: " + command);
974 cmd.command = command;
977 emit gdbInputAvailable(QString(), cmd.command);
978 m_gdbProc.write(cmd.command.toLatin1() + "\r\n");
981 void GdbEngine::handleQueryPwd(const GdbResultRecord &record)
983 // FIXME: remove this special case as soon as 'pwd'
984 // is supported by MI
985 //qDebug() << "PWD OUTPUT:" << record.toString();
986 // Gdb responses _unless_ we get an error first.
987 if (record.resultClass == GdbResultDone) {
989 // "5^done,{logstreamoutput="pwd ",consolestreamoutput
990 // ="Working directory /home/apoenitz/dev/work/test1. "}
991 m_pwd = record.data.findChild("consolestreamoutput").data();
992 int pos = m_pwd.indexOf("Working directory");
993 m_pwd = m_pwd.mid(pos + 18);
994 m_pwd = m_pwd.trimmed();
995 if (m_pwd.endsWith('.'))
999 // ~"Working directory C:\\Users\\Thomas\\Documents\\WBTest3\\debug.\n"
1000 m_pwd = record.data.findChild("consolestreamoutput").data();
1001 m_pwd = m_pwd.trimmed();
1003 debugMessage("PWD RESULT: " + m_pwd);
1007 void GdbEngine::handleQuerySources(const GdbResultRecord &record)
1009 if (record.resultClass == GdbResultDone) {
1010 QMap<QString, QString> oldShortToFull = m_shortToFullName;
1011 m_shortToFullName.clear();
1012 m_fullToShortName.clear();
1013 // "^done,files=[{file="../../../../bin/gdbmacros/gdbmacros.cpp",
1014 // fullname="/data5/dev/ide/main/bin/gdbmacros/gdbmacros.cpp"},
1015 GdbMi files = record.data.findChild("files");
1016 foreach (const GdbMi &item, files.children()) {
1017 QString fileName = item.findChild("file").data();
1018 GdbMi fullName = item.findChild("fullname");
1019 QString full = fullName.data();
1021 full = QDir::cleanPath(full);
1023 if (fullName.isValid() && QFileInfo(full).isReadable()) {
1024 //qDebug() << "STORING 2: " << fileName << full;
1025 m_shortToFullName[fileName] = full;
1026 m_fullToShortName[full] = fileName;
1029 if (m_shortToFullName != oldShortToFull)
1030 qq->sourceFileWindow()->setSourceFiles(m_shortToFullName);
1034 void GdbEngine::handleInfoThreads(const GdbResultRecord &record)
1036 if (record.resultClass == GdbResultDone) {
1037 // FIXME: use something more robust
1038 // WIN: * 3 Thread 2312.0x4d0 0x7c91120f in ?? ()
1039 // LINUX: * 1 Thread 0x7f466273c6f0 (LWP 21455) 0x0000000000404542 in ...
1040 QRegExp re(QLatin1String("Thread (\\d+)\\.0x.* in"));
1041 QString data = record.data.findChild("consolestreamoutput").data();
1042 if (re.indexIn(data) != -1)
1043 maybeHandleInferiorPidChanged(re.cap(1));
1047 void GdbEngine::handleInfoProc(const GdbResultRecord &record)
1049 if (record.resultClass == GdbResultDone) {
1050 #if defined(Q_OS_MAC)
1051 //^done,process-id="85075"
1052 maybeHandleInferiorPidChanged(record.data.findChild("process-id").data());
1055 #if defined(Q_OS_LINUX) || defined(Q_OS_WIN)
1056 // FIXME: use something more robust
1057 QRegExp re(QLatin1String("process (\\d+)"));
1058 QString data = record.data.findChild("consolestreamoutput").data();
1059 if (re.indexIn(data) != -1)
1060 maybeHandleInferiorPidChanged(re.cap(1));
1065 void GdbEngine::handleInfoShared(const GdbResultRecord &record)
1067 if (record.resultClass == GdbResultDone) {
1068 // let the modules handler do the parsing
1069 handleModulesList(record);
1073 void GdbEngine::handleExecJumpToLine(const GdbResultRecord &record)
1075 // FIXME: remove this special case as soon as 'jump'
1076 // is supported by MI
1077 // "&"jump /home/apoenitz/dev/work/test1/test1.cpp:242"
1078 // ~"Continuing at 0x4058f3."
1079 // ~"run1 (argc=1, argv=0x7fffb213a478) at test1.cpp:242"
1082 qq->notifyInferiorStopped();
1083 q->showStatusMessage(tr("Jumped. Stopped."));
1084 QString output = record.data.findChild("logstreamoutput").data();
1085 if (!output.isEmpty())
1087 QString fileAndLine = output.section(' ', 1, 1);
1088 QString file = fileAndLine.section(':', 0, 0);
1089 int line = fileAndLine.section(':', 1, 1).toInt();
1090 q->gotoLocation(file, line, true);
1093 void GdbEngine::handleExecRunToFunction(const GdbResultRecord &record)
1095 // FIXME: remove this special case as soon as there's a real
1096 // reason given when the temporary breakpoint is hit.
1097 // reight now we get:
1098 // 14*stopped,thread-id="1",frame={addr="0x0000000000403ce4",
1099 // func="foo",args=[{name="str",value="@0x7fff0f450460"}],
1100 // file="main.cpp",fullname="/tmp/g/main.cpp",line="37"}
1101 qq->notifyInferiorStopped();
1102 q->showStatusMessage(tr("Run to Function finished. Stopped."));
1103 GdbMi frame = record.data.findChild("frame");
1104 QString file = frame.findChild("fullname").data();
1105 int line = frame.findChild("line").data().toInt();
1106 qDebug() << "HIT: " << file << line << " IN " << frame.toString()
1107 << " -- " << record.toString();
1108 q->gotoLocation(file, line, true);
1111 static bool isExitedReason(const QString &reason)
1113 return reason == QLatin1String("exited-normally") // inferior exited normally
1114 || reason == QLatin1String("exited-signalled") // inferior exited because of a signal
1115 //|| reason == QLatin1String("signal-received") // inferior received signal
1116 || reason == QLatin1String("exited"); // inferior exited
1119 static bool isStoppedReason(const QString &reason)
1121 return reason == QLatin1String("function-finished") // -exec-finish
1122 || reason == QLatin1String("signal-received") // handled as "isExitedReason"
1123 || reason == QLatin1String("breakpoint-hit") // -exec-continue
1124 || reason == QLatin1String("end-stepping-range") // -exec-next, -exec-step
1125 || reason == QLatin1String("location-reached") // -exec-until
1126 || reason == QLatin1String("access-watchpoint-trigger")
1127 || reason == QLatin1String("read-watchpoint-trigger")
1134 void GdbEngine::handleAqcuiredInferior()
1136 #if defined(Q_OS_WIN)
1137 sendCommand("info thread", GdbInfoThreads);
1139 #if defined(Q_OS_LINUX)
1140 sendCommand("info proc", GdbInfoProc);
1142 #if defined(Q_OS_MAC)
1143 sendCommand("info pid", GdbInfoProc, QVariant(), NeedsStop);
1145 reloadSourceFiles();
1146 tryLoadCustomDumpers();
1149 // intentionally after tryLoadCustomDumpers(),
1150 // otherwise we'd interupt solib loading.
1151 if (qq->wantsAllPluginBreakpoints()) {
1152 sendCommand("set auto-solib-add on");
1153 sendCommand("set stop-on-solib-events 0");
1154 sendCommand("sharedlibrary .*");
1155 } else if (qq->wantsSelectedPluginBreakpoints()) {
1156 sendCommand("set auto-solib-add on");
1157 sendCommand("set stop-on-solib-events 1");
1158 sendCommand("sharedlibrary " + qq->selectedPluginBreakpointsPattern());
1159 } else if (qq->wantsNoPluginBreakpoints()) {
1160 // should be like that already
1161 sendCommand("set auto-solib-add off");
1162 sendCommand("set stop-on-solib-events 0");
1165 // nicer to see a bit of the world we live in
1167 attemptBreakpointSynchronization();
1170 void GdbEngine::handleAsyncOutput(const GdbMi &data)
1172 const QString reason = data.findChild("reason").data();
1174 if (isExitedReason(reason)) {
1175 qq->notifyInferiorExited();
1176 QString msg = "Program exited normally";
1177 if (reason == "exited") {
1178 msg = "Program exited with exit code "
1179 + data.findChild("exit-code").toString();
1180 } else if (reason == "exited-signalled") {
1181 msg = "Program exited after receiving signal "
1182 + data.findChild("signal-name").toString();
1183 } else if (reason == "signal-received") {
1184 msg = "Program exited after receiving signal "
1185 + data.findChild("signal-name").toString();
1187 q->showStatusMessage(msg);
1188 // FIXME: shouldn't this use a statis change?
1189 debugMessage("CALLING PARENT EXITDEBUGGER");
1195 //MAC: bool isFirstStop = data.findChild("bkptno").data() == "1";
1196 //!MAC: startSymbolName == data.findChild("frame").findChild("func")
1197 if (m_waitingForFirstBreakpointToBeHit) {
1198 // If the executable dies already that early we might get something
1199 // like stdout:49*stopped,reason="exited",exit-code="0177"
1200 // This is handled now above.
1202 qq->notifyInferiorStopped();
1203 m_waitingForFirstBreakpointToBeHit = false;
1205 // this will "continue" if done
1206 m_waitingForBreakpointSynchronizationToContinue = true;
1208 // that's the "early stop"
1209 handleAqcuiredInferior();
1213 if (!m_commandsToRunOnTemporaryBreak.isEmpty()) {
1214 QTC_ASSERT(q->status() == DebuggerInferiorStopRequested,
1215 qDebug() << "STATUS: " << q->status())
1216 qq->notifyInferiorStopped();
1217 q->showStatusMessage(tr("Temporarily stopped."));
1219 foreach (const GdbCookie &cmd, m_commandsToRunOnTemporaryBreak) {
1220 debugMessage(QString("RUNNING QUEUED COMMAND %1 %2")
1221 .arg(cmd.command).arg(cmd.type));
1222 sendCommand(cmd.command, cmd.type, cmd.cookie);
1224 sendCommand("p temporaryStop", GdbTemporaryContinue);
1225 m_commandsToRunOnTemporaryBreak.clear();
1226 q->showStatusMessage(tr("Handling queued commands."));
1230 QString msg = data.findChild("consolestreamoutput").data();
1231 if (msg.contains("Stopped due to shared library event") || reason.isEmpty()) {
1232 if (qq->wantsSelectedPluginBreakpoints()) {
1233 debugMessage("SHARED LIBRARY EVENT: " + data.toString());
1234 debugMessage("PATTERN: " + qq->selectedPluginBreakpointsPattern());
1235 sendCommand("sharedlibrary " + qq->selectedPluginBreakpointsPattern());
1237 q->showStatusMessage(tr("Loading %1...").arg(QString(data.toString())));
1240 m_modulesListOutdated = true;
1244 // seen on XP after removing a breakpoint while running
1245 // stdout:945*stopped,reason="signal-received",signal-name="SIGTRAP",
1246 // signal-meaning="Trace/breakpoint trap",thread-id="2",
1247 // frame={addr="0x7c91120f",func="ntdll!DbgUiConnectToDbg",
1248 // args=[],from="C:\\WINDOWS\\system32\\ntdll.dll"}
1249 if (reason == "signal-received"
1250 && data.findChild("signal-name").toString() == "SIGTRAP") {
1255 //tryLoadCustomDumpers();
1257 // jump over well-known frames
1258 static int stepCounter = 0;
1259 if (qq->skipKnownFrames()) {
1260 if (reason == "end-stepping-range" || reason == "function-finished") {
1261 GdbMi frame = data.findChild("frame");
1262 //debugMessage(frame.toString());
1263 m_currentFrame = frame.findChild("addr").data() + '%' +
1264 frame.findChild("func").data() + '%';
1266 QString funcName = frame.findChild("func").data();
1267 QString fileName = frame.findChild("file").data();
1268 if (isLeavableFunction(funcName, fileName)) {
1269 //debugMessage("LEAVING" + funcName);
1275 if (isSkippableFunction(funcName, fileName)) {
1276 //debugMessage("SKIPPING" + funcName);
1282 // qDebug() << "STEPCOUNTER:" << stepCounter;
1287 if (isStoppedReason(reason) || reason.isEmpty()) {
1288 if (m_modulesListOutdated) {
1290 m_modulesListOutdated = false;
1292 // Need another round trip
1293 if (reason == "breakpoint-hit") {
1294 q->showStatusMessage(tr("Stopped at breakpoint"));
1295 GdbMi frame = data.findChild("frame");
1296 //debugMessage("HIT BREAKPOINT: " + frame.toString());
1297 m_currentFrame = frame.findChild("addr").data() + '%' +
1298 frame.findChild("func").data() + '%';
1300 QApplication::alert(q->mainWindow(), 3000);
1301 reloadSourceFiles();
1302 sendCommand("-break-list", BreakList);
1303 QVariant var = QVariant::fromValue<GdbMi>(data);
1304 sendCommand("p 0", GdbAsyncOutput2, var); // dummy
1306 q->showStatusMessage(tr("Stopped: \"%1\"").arg(reason));
1307 handleAsyncOutput2(data);
1312 debugMessage("STOPPED FOR UNKNOWN REASON: " + data.toString());
1313 // Ignore it. Will be handled with full response later in the
1314 // JumpToLine or RunToFunction handlers
1316 // FIXME: remove this special case as soon as there's a real
1317 // reason given when the temporary breakpoint is hit.
1318 // reight now we get:
1319 // 14*stopped,thread-id="1",frame={addr="0x0000000000403ce4",
1320 // func="foo",args=[{name="str",value="@0x7fff0f450460"}],
1321 // file="main.cpp",fullname="/tmp/g/main.cpp",line="37"}
1323 // MAC yields sometimes:
1324 // stdout:3661*stopped,time={wallclock="0.00658",user="0.00142",
1325 // system="0.00136",start="1218810678.805432",end="1218810678.812011"}
1327 qq->notifyInferiorStopped();
1328 q->showStatusMessage(tr("Run to Function finished. Stopped."));
1329 GdbMi frame = data.findChild("frame");
1330 QString file = frame.findChild("fullname").data();
1331 int line = frame.findChild("line").data().toInt();
1332 qDebug() << "HIT: " << file << line << " IN " << frame.toString()
1333 << " -- " << data.toString();
1334 q->gotoLocation(file, line, true);
1339 void GdbEngine::handleAsyncOutput2(const GdbMi &data)
1341 qq->notifyInferiorStopped();
1346 qq->stackHandler()->setCurrentIndex(0);
1347 updateLocals(); // Quick shot
1349 int currentId = data.findChild("thread-id").data().toInt();
1350 sendSynchronizedCommand("-stack-list-frames", StackListFrames);
1351 if (supportsThreads())
1352 sendSynchronizedCommand("-thread-list-ids", StackListThreads, currentId);
1358 //"79*stopped,reason="end-stepping-range",reason="breakpoint-hit",bkptno="1",
1359 //thread-id="1",frame={addr="0x0000000000405d8f",func="run1",
1360 //args=[{name="argc",value="1"},{name="argv",value="0x7fffb7c23058"}],
1361 //file="test1.cpp",fullname="/home/apoenitz/dev/work/test1/test1.cpp",line="261"}"
1362 // Mac: (but only sometimes)
1363 m_address = data.findChild("frame").findChild("addr").data();
1364 qq->reloadDisassembler();
1369 qq->reloadRegisters();
1372 void GdbEngine::handleShowVersion(const GdbResultRecord &response)
1374 //qDebug () << "VERSION 2:" << response.data.findChild("consolestreamoutput").data();
1375 //qDebug () << "VERSION:" << response.toString();
1376 debugMessage("VERSION:" + response.toString());
1377 if (response.resultClass == GdbResultDone) {
1379 m_gdbBuildVersion = -1;
1380 QString msg = response.data.findChild("consolestreamoutput").data();
1381 QRegExp supported("GNU gdb(.*) (\\d+)\\.(\\d+)(\\.(\\d+))?(-(\\d+))?");
1382 if (supported.indexIn(msg) == -1) {
1383 debugMessage("UNSUPPORTED GDB VERSION " + msg);
1384 QStringList list = msg.split("\n");
1385 while (list.size() > 2)
1387 msg = tr("The debugger you are using identifies itself as:")
1388 + "<p><p>" + list.join("<br>") + "<p><p>"
1389 + tr("This version is not officially supported by Qt Creator.\n"
1390 "Debugging will most likely not work well.\n"
1391 "Using gdb 6.7 or later is strongly recommended.");
1393 // ugly, but 'Show again' check box...
1394 static QErrorMessage *err = new QErrorMessage(m_mainWindow);
1395 err->setMinimumSize(400, 300);
1396 err->showMessage(msg);
1398 //QMessageBox::information(m_mainWindow, tr("Warning"), msg);
1401 m_gdbVersion = 10000 * supported.cap(2).toInt()
1402 + 100 * supported.cap(3).toInt()
1403 + 1 * supported.cap(5).toInt();
1404 m_gdbBuildVersion = supported.cap(7).toInt();
1405 debugMessage(QString("GDB VERSION: %1").arg(m_gdbVersion));
1407 //qDebug () << "VERSION 3:" << m_gdbVersion << m_gdbBuildVersion;
1411 void GdbEngine::handleFileExecAndSymbols
1412 (const GdbResultRecord &response)
1414 if (response.resultClass == GdbResultDone) {
1415 //m_breakHandler->clearBreakMarkers();
1416 } else if (response.resultClass == GdbResultError) {
1417 QString msg = response.data.findChild("msg").data();
1418 QMessageBox::critical(q->mainWindow(), tr("Error"),
1419 tr("Starting executable failed:\n") + msg);
1420 QTC_ASSERT(q->status() == DebuggerInferiorRunning, /**/);
1421 interruptInferior();
1425 void GdbEngine::handleExecRun(const GdbResultRecord &response)
1427 if (response.resultClass == GdbResultRunning) {
1428 qq->notifyInferiorRunning();
1429 q->showStatusMessage(tr("Running..."));
1430 } else if (response.resultClass == GdbResultError) {
1431 QString msg = response.data.findChild("msg").data();
1432 if (msg == "Cannot find bounds of current function") {
1433 qq->notifyInferiorStopped();
1434 //q->showStatusMessage(tr("No debug information available. "
1435 // "Leaving function..."));
1438 QMessageBox::critical(q->mainWindow(), tr("Error"),
1439 tr("Starting executable failed:\n") + msg);
1440 QTC_ASSERT(q->status() == DebuggerInferiorRunning, /**/);
1441 interruptInferior();
1446 void GdbEngine::queryFullName(const QString &fileName, QString *full)
1448 *full = fullName(fileName);
1451 QString GdbEngine::shortName(const QString &fullName)
1453 return m_fullToShortName.value(fullName, QString());
1456 QString GdbEngine::fullName(const QString &fileName)
1458 //QString absName = m_manager->currentWorkingDirectory() + "/" + file; ??
1459 if (fileName.isEmpty())
1461 QString full = m_shortToFullName.value(fileName, QString());
1462 //debugMessage("RESOLVING: " + fileName + " " + full);
1463 if (!full.isEmpty())
1465 QFileInfo fi(fileName);
1466 if (!fi.isReadable())
1468 full = fi.absoluteFilePath();
1470 full = QDir::cleanPath(full);
1472 //debugMessage("STORING: " + fileName + " " + full);
1473 m_shortToFullName[fileName] = full;
1474 m_fullToShortName[full] = fileName;
1478 QString GdbEngine::fullName(const QStringList &candidates)
1481 foreach (const QString &fileName, candidates) {
1482 full = fullName(fileName);
1483 if (!full.isEmpty())
1486 foreach (const QString &fileName, candidates) {
1487 if (!fileName.isEmpty())
1493 void GdbEngine::shutdown()
1498 void GdbEngine::exitDebugger()
1500 debugMessage(QString("GDBENGINE EXITDEBUFFER: %1").arg(m_gdbProc.state()));
1501 if (m_gdbProc.state() == QProcess::Starting) {
1502 debugMessage(QString("WAITING FOR GDB STARTUP TO SHUTDOWN: %1")
1503 .arg(m_gdbProc.state()));
1504 m_gdbProc.waitForStarted();
1506 if (m_gdbProc.state() == QProcess::Running) {
1507 debugMessage(QString("WAITING FOR RUNNING GDB TO SHUTDOWN: %1")
1508 .arg(m_gdbProc.state()));
1509 if (q->status() != DebuggerInferiorStopped
1510 && q->status() != DebuggerProcessStartingUp) {
1511 QTC_ASSERT(q->status() == DebuggerInferiorRunning,
1512 qDebug() << "STATUS ON EXITDEBUGGER: " << q->status());
1513 interruptInferior();
1515 if (q->startMode() == DebuggerManager::AttachExternal)
1516 sendCommand("detach");
1518 sendCommand("kill");
1519 sendCommand("-gdb-exit");
1520 // 20s can easily happen when loading webkit debug information
1521 m_gdbProc.waitForFinished(20000);
1522 if (m_gdbProc.state() != QProcess::Running) {
1523 debugMessage(QString("FORCING TERMINATION: %1")
1524 .arg(m_gdbProc.state()));
1525 m_gdbProc.terminate();
1526 m_gdbProc.waitForFinished(20000);
1529 if (m_gdbProc.state() != QProcess::NotRunning)
1530 debugMessage("PROBLEM STOPPING DEBUGGER");
1532 m_outputCollector.shutdown();
1533 initializeVariables();
1534 //q->settings()->m_debugDumpers = false;
1538 int GdbEngine::currentFrame() const
1540 return qq->stackHandler()->currentIndex();
1544 bool GdbEngine::startDebugger()
1546 QStringList gdbArgs;
1548 QFileInfo fi(q->m_executable);
1549 QString fileName = '"' + fi.absoluteFilePath() + '"';
1551 if (m_gdbProc.state() != QProcess::NotRunning) {
1552 debugMessage("GDB IS ALREADY RUNNING!");
1556 if (!m_outputCollector.listen()) {
1557 QMessageBox::critical(q->mainWindow(), tr("Debugger Startup Failure"),
1558 tr("Cannot set up communication with child process: %1")
1559 .arg(m_outputCollector.errorString()));
1563 gdbArgs.prepend(QLatin1String("--tty=") + m_outputCollector.serverName());
1565 //gdbArgs.prepend(QLatin1String("--quiet"));
1566 gdbArgs.prepend(QLatin1String("mi"));
1567 gdbArgs.prepend(QLatin1String("-i"));
1569 if (!q->m_workingDir.isEmpty())
1570 m_gdbProc.setWorkingDirectory(q->m_workingDir);
1571 if (!q->m_environment.isEmpty())
1572 m_gdbProc.setEnvironment(q->m_environment);
1575 qDebug() << "Command: " << q->settings()->m_gdbCmd;
1576 qDebug() << "WorkingDirectory: " << m_gdbProc.workingDirectory();
1577 qDebug() << "ScriptFile: " << q->settings()->m_scriptFile;
1578 qDebug() << "Environment: " << m_gdbProc.environment();
1579 qDebug() << "Arguments: " << gdbArgs;
1580 qDebug() << "BuildDir: " << q->m_buildDir;
1581 qDebug() << "ExeFile: " << q->m_executable;
1584 q->showStatusMessage(tr("Starting Debugger: ") + q->settings()->m_gdbCmd + ' ' + gdbArgs.join(" "));
1585 m_gdbProc.start(q->settings()->m_gdbCmd, gdbArgs);
1586 m_gdbProc.waitForStarted();
1588 if (m_gdbProc.state() != QProcess::Running) {
1589 QMessageBox::critical(q->mainWindow(), tr("Debugger Startup Failure"),
1590 tr("Cannot start debugger: %1").arg(m_gdbProc.errorString()));
1591 m_outputCollector.shutdown();
1595 q->showStatusMessage(tr("Gdb Running"));
1597 sendCommand("show version", GdbShowVersion);
1598 //sendCommand("-enable-timings");
1599 sendCommand("set print static-members off"); // Seemingly doesn't work.
1600 //sendCommand("define hook-stop\n-thread-list-ids\n-stack-list-frames\nend");
1601 //sendCommand("define hook-stop\nprint 4\nend");
1602 //sendCommand("define hookpost-stop\nprint 5\nend");
1603 //sendCommand("define hook-call\nprint 6\nend");
1604 //sendCommand("define hookpost-call\nprint 7\nend");
1605 //sendCommand("set print object on"); // works with CLI, but not MI
1606 //sendCommand("set step-mode on"); // we can't work with that yes
1607 //sendCommand("set exec-done-display on");
1608 //sendCommand("set print pretty on");
1609 //sendCommand("set confirm off");
1610 //sendCommand("set pagination off");
1611 sendCommand("set breakpoint pending on", BreakEnablePending);
1612 sendCommand("set print elements 10000");
1613 sendCommand("-data-list-register-names", RegisterListNames);
1615 // one of the following is needed to prevent crashes in gdb on code like:
1616 // template <class T> T foo() { return T(0); }
1617 // int main() { return foo<int>(); }
1618 // (gdb) call 'int foo<int>'()
1619 // /build/buildd/gdb-6.8/gdb/valops.c:2069: internal-error:
1620 sendCommand("set overload-resolution off");
1621 //sendCommand("set demangle-style none");
1624 // Stop means reenter debugger if this signal happens (implies print).
1625 // Print means print a message if this signal happens.
1626 // Pass means let program see this signal;
1627 // otherwise program doesn't know.
1628 // Pass and Stop may be combined.
1629 // We need "print" as otherwise we would get no feedback whatsoever
1630 // Custom Dumper crashs which happen regularily for when accessing
1631 // uninitialized variables.
1632 sendCommand("handle SIGSEGV nopass stop print");
1634 // This is useful to kill the inferior whenever gdb dies.
1635 //sendCommand("handle SIGTERM pass nostop print");
1637 sendCommand("set unwindonsignal on");
1638 sendCommand("pwd", GdbQueryPwd);
1639 sendCommand("set width 0");
1640 sendCommand("set height 0");
1643 sendCommand("-gdb-set inferior-auto-start-cfm off");
1644 sendCommand("-gdb-set sharedLibrary load-rules "
1645 "dyld \".*libSystem.*\" all "
1646 "dyld \".*libauto.*\" all "
1647 "dyld \".*AppKit.*\" all "
1648 "dyld \".*PBGDBIntrospectionSupport.*\" all "
1649 "dyld \".*Foundation.*\" all "
1650 "dyld \".*CFDataFormatters.*\" all "
1651 "dyld \".*libobjc.*\" all "
1652 "dyld \".*CarbonDataFormatters.*\" all");
1655 QString scriptFileName = q->settings()->m_scriptFile;
1656 if (!scriptFileName.isEmpty()) {
1657 QFile scriptFile(scriptFileName);
1658 if (scriptFile.open(QIODevice::ReadOnly)) {
1659 sendCommand("source " + scriptFileName);
1661 QMessageBox::warning(q->mainWindow(),
1662 tr("Cannot find debugger initialization script"),
1663 tr("The debugger settings point to a script file at '%1' "
1664 "which is not accessible. If a script file is not needed, "
1665 "consider clearing that entry to avoid this warning. "
1666 ).arg(scriptFileName));
1670 if (q->startMode() == DebuggerManager::AttachExternal) {
1671 sendCommand("attach " + QString::number(q->m_attachedPID), GdbAttached);
1673 // StartInternal or StartExternal
1674 sendCommand("-file-exec-and-symbols " + fileName, GdbFileExecAndSymbols);
1675 //sendCommand("file " + fileName, GdbFileExecAndSymbols);
1677 sendCommand("sharedlibrary apply-load-rules all");
1679 if (!q->m_processArgs.isEmpty())
1680 sendCommand("-exec-arguments " + q->m_processArgs.join(" "));
1682 sendCommand("set auto-solib-add off");
1684 sendCommand("x/2i " + startSymbolName(), GdbStart);
1687 // set all to "pending"
1688 if (q->startMode() == DebuggerManager::AttachExternal)
1689 qq->breakHandler()->removeAllBreakpoints();
1691 qq->breakHandler()->setAllPending();
1696 void GdbEngine::continueInferior()
1700 qq->notifyInferiorRunningRequested();
1701 sendCommand("-exec-continue", GdbExecContinue);
1704 void GdbEngine::handleStart(const GdbResultRecord &response)
1706 if (response.resultClass == GdbResultDone) {
1707 // stdout:&"x/2i _start\n"
1708 // stdout:~"0x404540 <_start>:\txor %ebp,%ebp\n"
1709 // stdout:~"0x404542 <_start+2>:\tmov %rdx,%r9\n"
1710 QString msg = response.data.findChild("consolestreamoutput").data();
1712 // this ends up in 'gettimeoftheday' or such on MacOS 10.4
1713 QRegExp needle("0x([0-9a-f]+) <.*\\+.*>:");
1715 QRegExp needle("0x([0-9a-f]+) <" + startSymbolName() + "\\+.*>:");
1717 if (needle.lastIndexIn(msg) != -1) {
1718 //debugMessage("STREAM: " + msg + " " + needle.cap(1));
1719 sendCommand("tbreak *0x" + needle.cap(1));
1720 m_waitingForFirstBreakpointToBeHit = true;
1721 qq->notifyInferiorRunningRequested();
1722 sendCommand("-exec-run");
1724 debugMessage("PARSING START ADDRESS FAILED: " + msg);
1726 } else if (response.resultClass == GdbResultError) {
1727 debugMessage("PARSING START ADDRESS FAILED: " + response.toString());
1731 void GdbEngine::handleAttach()
1733 qq->notifyInferiorStopped();
1734 q->showStatusMessage(tr("Attached to running process. Stopped."));
1735 handleAqcuiredInferior();
1742 qq->stackHandler()->setCurrentIndex(0);
1743 updateLocals(); // Quick shot
1745 sendSynchronizedCommand("-stack-list-frames", StackListFrames);
1746 if (supportsThreads())
1747 sendSynchronizedCommand("-thread-list-ids", StackListThreads, 0);
1752 // XXX we have no data here ...
1753 //m_address = data.findChild("frame").findChild("addr").data();
1754 //qq->reloadDisassembler();
1759 qq->reloadRegisters();
1762 void GdbEngine::stepExec()
1765 qq->notifyInferiorRunningRequested();
1766 sendCommand("-exec-step", GdbExecStep);
1769 void GdbEngine::stepIExec()
1772 qq->notifyInferiorRunningRequested();
1773 sendCommand("-exec-step-instruction", GdbExecStepI);
1776 void GdbEngine::stepOutExec()
1779 qq->notifyInferiorRunningRequested();
1780 sendCommand("-exec-finish", GdbExecFinish);
1783 void GdbEngine::nextExec()
1786 qq->notifyInferiorRunningRequested();
1787 sendCommand("-exec-next", GdbExecNext);
1790 void GdbEngine::nextIExec()
1793 qq->notifyInferiorRunningRequested();
1794 sendCommand("-exec-next-instruction", GdbExecNextI);
1797 void GdbEngine::runToLineExec(const QString &fileName, int lineNumber)
1800 qq->notifyInferiorRunningRequested();
1801 sendCommand("-exec-until " + fileName + ":" + QString::number(lineNumber));
1804 void GdbEngine::runToFunctionExec(const QString &functionName)
1807 sendCommand("-break-insert -t " + functionName);
1808 qq->notifyInferiorRunningRequested();
1809 sendCommand("-exec-continue", GdbExecRunToFunction);
1812 void GdbEngine::jumpToLineExec(const QString &fileName, int lineNumber)
1815 // not available everywhere?
1816 //sendCliCommand("tbreak " + fileName + ":" + QString::number(lineNumber));
1817 sendCommand("-break-insert -t " + fileName + ":" + QString::number(lineNumber));
1818 sendCommand("jump " + fileName + ":" + QString::number(lineNumber));
1819 // will produce something like
1820 // &"jump /home/apoenitz/dev/work/test1/test1.cpp:242"
1821 // ~"Continuing at 0x4058f3."
1822 // ~"run1 (argc=1, argv=0x7fffbf1f5538) at test1.cpp:242"
1825 q->gotoLocation(fileName, lineNumber, true);
1827 //sendCommand("jump " + fileName + ":" + QString::number(lineNumber));
1829 q->gotoLocation(fileName, lineNumber, true);
1830 setBreakpoint(fileName, lineNumber);
1831 sendCommand("jump " + fileName + ":" + QString::number(lineNumber));
1836 \fn void GdbEngine::setTokenBarrier()
1837 \brief Discard the results of all pending watch-updating commands.
1839 This method is called at the beginning of all step/next/finish etc.
1841 If non-watch-updating commands with call-backs are still in the pipe,
1845 void GdbEngine::setTokenBarrier()
1847 foreach (const GdbCookie &ck, m_cookieForToken)
1848 QTC_ASSERT(ck.synchronized || ck.type == GdbInvalidCommand, return);
1849 emit gdbInputAvailable(QString(), "--- token barrier ---");
1850 m_oldestAcceptableToken = currentToken();
1853 void GdbEngine::setDebugDumpers(bool on)
1856 debugMessage("SWITCHING ON DUMPER DEBUGGING");
1857 sendCommand("set unwindonsignal off");
1858 q->breakByFunction("qDumpObjectData440");
1861 debugMessage("SWITCHING OFF DUMPER DEBUGGING");
1862 sendCommand("set unwindonsignal on");
1867 //////////////////////////////////////////////////////////////////////
1869 // Breakpoint specific stuff
1871 //////////////////////////////////////////////////////////////////////
1873 void GdbEngine::breakpointDataFromOutput(BreakpointData *data, const GdbMi &bkpt)
1875 if (!bkpt.isValid())
1879 data->pending = false;
1880 data->bpMultiple = false;
1881 data->bpCondition.clear();
1883 foreach (const GdbMi &child, bkpt.children()) {
1884 if (child.hasName("number")) {
1885 data->bpNumber = child.data();
1886 } else if (child.hasName("func")) {
1887 data->bpFuncName = child.data();
1888 } else if (child.hasName("addr")) {
1889 // <MULTIPLE> happens in constructors. In this case there are
1890 // _two_ fields named "addr" in the response. On Linux that is...
1891 if (child.data() == "<MULTIPLE>")
1892 data->bpMultiple = true;
1894 data->bpAddress = child.data();
1895 } else if (child.hasName("file")) {
1896 files.append(child.data());
1897 } else if (child.hasName("fullname")) {
1898 QString fullName = child.data();
1900 fullName = QDir::cleanPath(fullName);
1902 files.prepend(fullName);
1903 } else if (child.hasName("line")) {
1904 data->bpLineNumber = child.data();
1905 if (child.data().toInt())
1906 data->markerLineNumber = child.data().toInt();
1907 } else if (child.hasName("cond")) {
1908 data->bpCondition = child.data();
1909 // gdb 6.3 likes to "rewrite" conditions. Just accept that fact.
1910 if (data->bpCondition != data->condition && data->conditionsMatch())
1911 data->condition = data->bpCondition;
1913 else if (child.hasName("pending")) {
1914 data->pending = true;
1915 int pos = child.data().lastIndexOf(':');
1917 data->bpLineNumber = child.data().mid(pos + 1);
1918 data->markerLineNumber = child.data().mid(pos + 1).toInt();
1919 files.prepend(child.data().left(pos));
1921 files.prepend(child.data());
1925 // This field is not present. Contents needs to be parsed from
1926 // the plain "ignore" response.
1927 //else if (child.hasName("ignore"))
1928 // data->bpIgnoreCount = child.data();
1930 QString name = fullName(files);
1931 if (data->bpFileName.isEmpty())
1932 data->bpFileName = name;
1933 if (data->markerFileName.isEmpty())
1934 data->markerFileName = name;
1937 void GdbEngine::sendInsertBreakpoint(int index)
1939 const BreakpointData *data = qq->breakHandler()->at(index);
1941 if (data->funcName.isEmpty()) {
1942 where = data->fileName;
1944 // full names do not work on Mac/MI
1945 QFileInfo fi(data->fileName);
1946 where = fi.fileName();
1947 //where = fi.absoluteFilePath();
1950 // full names do not work on Mac/MI
1951 QFileInfo fi(data->fileName);
1952 where = fi.fileName();
1953 //where = m_manager->shortName(data->fileName);
1954 //if (where.isEmpty())
1955 // where = data->fileName;
1957 // we need something like "\"file name.cpp\":100" to
1958 // survive the gdb command line parser with file names intact
1959 where = "\"\\\"" + where + "\\\":" + data->lineNumber + "\"";
1961 where = data->funcName;
1964 // set up fallback in case of pending breakpoints which aren't handled
1965 // by the MI interface
1967 QString cmd = "-break-insert ";
1968 //if (!data->condition.isEmpty())
1969 // cmd += "-c " + data->condition + " ";
1973 QString cmd = "-break-insert -l -1 ";
1974 //if (!data->condition.isEmpty())
1975 // cmd += "-c " + data->condition + " ";
1979 QString cmd = "-break-insert ";
1980 //if (!data->condition.isEmpty())
1981 // cmd += "-c " + data->condition + " ";
1984 debugMessage(QString("Current state: %1").arg(q->status()));
1985 sendCommand(cmd, BreakInsert, index, NeedsStop);
1988 void GdbEngine::handleBreakList(const GdbResultRecord &record)
1990 // 45^done,BreakpointTable={nr_rows="2",nr_cols="6",hdr=[
1991 // {width="3",alignment="-1",col_name="number",colhdr="Num"}, ...
1992 // body=[bkpt={number="1",type="breakpoint",disp="keep",enabled="y",
1993 // addr="0x000000000040109e",func="main",file="app.cpp",
1994 // fullname="/home/apoenitz/dev/work/plugintest/app/app.cpp",
1995 // line="11",times="1"},
1996 // bkpt={number="2",type="breakpoint",disp="keep",enabled="y",
1997 // addr="<PENDING>",pending="plugin.cpp:7",times="0"}] ... }
1999 if (record.resultClass == GdbResultDone) {
2000 GdbMi table = record.data.findChild("BreakpointTable");
2001 if (table.isValid())
2002 handleBreakList(table);
2006 void GdbEngine::handleBreakList(const GdbMi &table)
2008 //qDebug() << "GdbEngine::handleOutput: table: "
2009 // << table.toString();
2010 GdbMi body = table.findChild("body");
2011 //qDebug() << "GdbEngine::handleOutput: body: "
2012 // << body.toString();
2014 if (body.isValid()) {
2016 bkpts = body.children();
2019 bkpts = table.children();
2020 // remove the 'hdr' and artificial items
2021 //qDebug() << "FOUND " << bkpts.size() << " BREAKPOINTS";
2022 for (int i = bkpts.size(); --i >= 0; ) {
2023 int num = bkpts.at(i).findChild("number").data().toInt();
2025 //qDebug() << "REMOVING " << i << bkpts.at(i).toString();
2029 //qDebug() << "LEFT " << bkpts.size() << " BREAKPOINTS";
2032 BreakHandler *handler = qq->breakHandler();
2033 for (int index = 0; index != bkpts.size(); ++index) {
2034 BreakpointData temp(handler);
2035 breakpointDataFromOutput(&temp, bkpts.at(index));
2036 int found = handler->findBreakpoint(temp);
2038 breakpointDataFromOutput(handler->at(found), bkpts.at(index));
2040 //qDebug() << "CANNOT HANDLE RESPONSE " << bkpts.at(index).toString();
2043 attemptBreakpointSynchronization();
2044 handler->updateMarkers();
2048 void GdbEngine::handleBreakIgnore(const GdbResultRecord &record, int index)
2052 // ~"Will stop next time breakpoint 2 is reached.\n"
2056 // ~"Will ignore next 12 crossings of breakpoint 2.\n"
2059 // gdb 6.3 does not produce any console output
2060 BreakHandler *handler = qq->breakHandler();
2061 if (record.resultClass == GdbResultDone && index < handler->size()) {
2062 QString msg = record.data.findChild("consolestreamoutput").data();
2063 BreakpointData *data = handler->at(index);
2064 //if (msg.contains("Will stop next time breakpoint")) {
2065 // data->bpIgnoreCount = "0";
2066 //} else if (msg.contains("Will ignore next")) {
2067 // data->bpIgnoreCount = data->ignoreCount;
2069 // FIXME: this assumes it is doing the right thing...
2070 data->bpIgnoreCount = data->ignoreCount;
2071 attemptBreakpointSynchronization();
2072 handler->updateMarkers();
2076 void GdbEngine::handleBreakCondition(const GdbResultRecord &record, int index)
2078 BreakHandler *handler = qq->breakHandler();
2079 if (record.resultClass == GdbResultDone) {
2080 // we just assume it was successful. otherwise we had to parse
2081 // the output stream data
2082 BreakpointData *data = handler->at(index);
2083 //qDebug() << "HANDLE BREAK CONDITION " << index << data->condition;
2084 data->bpCondition = data->condition;
2085 attemptBreakpointSynchronization();
2086 handler->updateMarkers();
2087 } else if (record.resultClass == GdbResultError) {
2088 QString msg = record.data.findChild("msg").data();
2090 if (1 || msg.startsWith("Error parsing breakpoint condition. "
2091 " Will try again when we hit the breakpoint.")) {
2092 BreakpointData *data = handler->at(index);
2093 //qDebug() << "ERROR BREAK CONDITION " << index << data->condition;
2094 data->bpCondition = data->condition;
2095 attemptBreakpointSynchronization();
2096 handler->updateMarkers();
2101 void GdbEngine::handleBreakInsert(const GdbResultRecord &record, int index)
2103 BreakHandler *handler = qq->breakHandler();
2104 if (record.resultClass == GdbResultDone) {
2105 //qDebug() << "HANDLE BREAK INSERT " << index;
2107 // interesting only on Mac?
2108 BreakpointData *data = handler->at(index);
2109 GdbMi bkpt = record.data.findChild("bkpt");
2110 //qDebug() << "BKPT: " << bkpt.toString() << " DATA" << data->toToolTip();
2111 breakpointDataFromOutput(data, bkpt);
2113 attemptBreakpointSynchronization();
2114 handler->updateMarkers();
2115 } else if (record.resultClass == GdbResultError) {
2116 const BreakpointData *data = handler->at(index);
2118 //QString where = "\"\\\"" + data->fileName + "\\\":"
2119 // + data->lineNumber + "\"";
2120 QString where = "\"" + data->fileName + "\":"
2122 sendCommand("break " + where, BreakInsert1, index);
2125 QFileInfo fi(data->fileName);
2126 QString where = "\"" + fi.fileName() + "\":"
2128 sendCommand("break " + where, BreakInsert1, index);
2131 QFileInfo fi(data->fileName);
2132 QString where = "\"" + fi.fileName() + "\":"
2134 //QString where = m_data->fileName + QLatin1Char(':') + data->lineNumber;
2135 sendCommand("break " + where, BreakInsert1, index);
2140 void GdbEngine::extractDataFromInfoBreak(const QString &output, BreakpointData *data)
2142 data->bpFileName = "<MULTIPLE>";
2144 //qDebug() << output;
2145 if (output.isEmpty())
2147 // "Num Type Disp Enb Address What
2148 // 4 breakpoint keep y <MULTIPLE> 0x00000000004066ad
2149 // 4.1 y 0x00000000004066ad in CTorTester
2150 // at /data5/dev/ide/main/tests/manual/gdbdebugger/simple/app.cpp:124
2152 // everything on a single line on Windows for constructors of classes
2153 // within namespaces.
2154 // Sometimes the path is relative too.
2156 QRegExp re("MULTIPLE.*(0x[0-9a-f]+) in (.*)\\s+at (.*):([\\d]+)([^\\d]|$)");
2157 re.setMinimal(true);
2159 if (re.indexIn(output) != -1) {
2160 data->bpAddress = re.cap(1);
2161 data->bpFuncName = re.cap(2).trimmed();
2162 data->bpLineNumber = re.cap(4);
2163 QString full = fullName(re.cap(3));
2164 data->markerLineNumber = data->bpLineNumber.toInt();
2165 data->markerFileName = full;
2166 data->bpFileName = full;
2167 //qDebug() << "FOUND BREAKPOINT\n" << output
2168 // << re.cap(1) << "\n" << re.cap(2) << "\n"
2169 // << re.cap(3) << "\n" << re.cap(4) << "\n";
2171 qDebug() << "COULD NOT MATCH " << re.pattern() << " AND " << output;
2172 data->bpNumber = "<unavailable>";
2176 void GdbEngine::handleBreakInfo(const GdbResultRecord &record, int bpNumber)
2178 BreakHandler *handler = qq->breakHandler();
2179 if (record.resultClass == GdbResultDone) {
2180 // Old-style output for multiple breakpoints, presumably in a
2182 int found = handler->findBreakpoint(bpNumber);
2184 QString str = record.data.findChild("consolestreamoutput").data();
2185 extractDataFromInfoBreak(str, handler->at(found));
2186 handler->updateMarkers();
2187 attemptBreakpointSynchronization(); // trigger "ready"
2192 void GdbEngine::handleBreakInsert1(const GdbResultRecord &record, int index)
2194 BreakHandler *handler = qq->breakHandler();
2195 if (record.resultClass == GdbResultDone) {
2196 // Pending breakpoints in dylibs on Mac only?
2197 BreakpointData *data = handler->at(index);
2198 GdbMi bkpt = record.data.findChild("bkpt");
2199 breakpointDataFromOutput(data, bkpt);
2200 attemptBreakpointSynchronization(); // trigger "ready"
2201 handler->updateMarkers();
2202 } else if (record.resultClass == GdbResultError) {
2203 qDebug() << "INSERTING BREAKPOINT WITH BASE NAME FAILED. GIVING UP";
2204 BreakpointData *data = handler->at(index);
2205 data->bpNumber = "<unavailable>";
2206 attemptBreakpointSynchronization(); // trigger "ready"
2207 handler->updateMarkers();
2211 void GdbEngine::attemptBreakpointSynchronization()
2213 // Non-lethal check for nested calls
2214 static bool inBreakpointSychronization = false;
2215 QTC_ASSERT(!inBreakpointSychronization, /**/);
2216 inBreakpointSychronization = true;
2218 BreakHandler *handler = qq->breakHandler();
2220 foreach (BreakpointData *data, handler->takeRemovedBreakpoints()) {
2221 QString bpNumber = data->bpNumber;
2222 debugMessage(QString("DELETING BP %1 IN %2").arg(bpNumber)
2223 .arg(data->markerFileName));
2224 if (!bpNumber.trimmed().isEmpty())
2225 sendCommand("-break-delete " + bpNumber, BreakDelete, QVariant(),
2230 bool updateNeeded = false;
2232 for (int index = 0; index != handler->size(); ++index) {
2233 BreakpointData *data = handler->at(index);
2234 // multiple breakpoints?
2235 if (data->bpMultiple && data->bpFileName.isEmpty()) {
2236 sendCommand(QString("info break %1").arg(data->bpNumber),
2237 BreakInfo, data->bpNumber.toInt());
2238 updateNeeded = true;
2243 for (int index = 0; index != handler->size(); ++index) {
2244 BreakpointData *data = handler->at(index);
2245 // unset breakpoints?
2246 if (data->bpNumber.isEmpty()) {
2247 data->bpNumber = " ";
2248 sendInsertBreakpoint(index);
2249 //qDebug() << "UPDATE NEEDED BECAUSE OF UNKNOWN BREAKPOINT";
2250 updateNeeded = true;
2255 if (!updateNeeded) {
2256 for (int index = 0; index != handler->size(); ++index) {
2257 BreakpointData *data = handler->at(index);
2258 // update conditions if needed
2259 if (data->bpNumber.toInt() && data->condition != data->bpCondition
2260 && !data->conditionsMatch()) {
2261 sendCommand(QString("condition %1 %2").arg(data->bpNumber)
2262 .arg(data->condition), BreakCondition, index);
2263 //qDebug() << "UPDATE NEEDED BECAUSE OF CONDITION"
2264 // << data->condition << data->bpCondition;
2265 updateNeeded = true;
2268 // update ignorecount if needed
2269 if (data->bpNumber.toInt() && data->ignoreCount != data->bpIgnoreCount) {
2270 sendCommand(QString("ignore %1 %2").arg(data->bpNumber)
2271 .arg(data->ignoreCount), BreakIgnore, index);
2272 updateNeeded = true;
2278 for (int index = 0; index != handler->size(); ++index) {
2279 // happens sometimes on Mac. Brush over symptoms
2280 BreakpointData *data = handler->at(index);
2281 if (data->markerFileName.startsWith("../")) {
2282 data->markerFileName = fullName(data->markerFileName);
2283 handler->updateMarkers();
2287 if (!updateNeeded && m_waitingForBreakpointSynchronizationToContinue) {
2288 m_waitingForBreakpointSynchronizationToContinue = false;
2289 // we continue the execution
2293 inBreakpointSychronization = false;
2297 //////////////////////////////////////////////////////////////////////
2299 // Disassembler specific stuff
2301 //////////////////////////////////////////////////////////////////////
2303 void GdbEngine::reloadDisassembler()
2305 emit sendCommand("disassemble", DisassemblerList, m_address);
2308 void GdbEngine::handleDisassemblerList(const GdbResultRecord &record,
2309 const QString &cookie)
2311 QList<DisassemblerLine> lines;
2312 static const QString pad = QLatin1String(" ");
2313 int currentLine = -1;
2314 if (record.resultClass == GdbResultDone) {
2315 QString res = record.data.findChild("consolestreamoutput").data();
2316 QTextStream ts(&res, QIODevice::ReadOnly);
2317 while (!ts.atEnd()) {
2318 //0x0000000000405fd8 <_ZN11QTextStreamD1Ev@plt+0>:
2319 // jmpq *2151890(%rip) # 0x6135b0 <_GLOBAL_OFFSET_TABLE_+640>
2320 //0x0000000000405fde <_ZN11QTextStreamD1Ev@plt+6>:
2322 //0x0000000000405fe3 <_ZN11QTextStreamD1Ev@plt+11>:
2323 // jmpq 0x405af8 <_init+24>
2324 //0x0000000000405fe8 <_ZN9QHashData6rehashEi@plt+0>:
2325 // jmpq *2151882(%rip) # 0x6135b8 <_GLOBAL_OFFSET_TABLE_+648>
2326 QString str = ts.readLine();
2327 if (!str.startsWith(QLatin1String("0x"))) {
2328 //qDebug() << "IGNORING DISASSEMBLER" << str;
2331 DisassemblerLine line;
2332 QTextStream ts(&str, QIODevice::ReadOnly);
2333 ts >> line.address >> line.symbol;
2334 line.mnemonic = ts.readLine().trimmed();
2335 if (line.symbol.endsWith(QLatin1Char(':')))
2336 line.symbol.chop(1);
2337 line.addressDisplay = line.address + pad;
2338 if (line.addressDisplay.startsWith(QLatin1String("0x00000000")))
2339 line.addressDisplay.replace(2, 8, QString());
2340 line.symbolDisplay = line.symbol + pad;
2342 if (line.address == cookie)
2343 currentLine = lines.size();
2348 DisassemblerLine line;
2349 line.addressDisplay = tr("<could not retreive module information>");
2353 qq->disassemblerHandler()->setLines(lines);
2354 if (currentLine != -1)
2355 qq->disassemblerHandler()->setCurrentLine(currentLine);
2359 //////////////////////////////////////////////////////////////////////
2361 // Modules specific stuff
2363 //////////////////////////////////////////////////////////////////////
2365 void GdbEngine::loadSymbols(const QString &moduleName)
2367 // FIXME: gdb does not understand quoted names here (tested with 6.8)
2368 sendCommand("sharedlibrary " + dotEscape(moduleName));
2372 void GdbEngine::loadAllSymbols()
2374 sendCommand("sharedlibrary .*");
2378 void GdbEngine::reloadModules()
2380 sendCommand("info shared", ModulesList, QVariant());
2383 void GdbEngine::handleModulesList(const GdbResultRecord &record)
2385 QList<Module> modules;
2386 if (record.resultClass == GdbResultDone) {
2387 QString data = record.data.findChild("consolestreamoutput").data();
2388 QTextStream ts(&data, QIODevice::ReadOnly);
2389 while (!ts.atEnd()) {
2390 QString line = ts.readLine();
2391 if (!line.startsWith("0x"))
2394 QString symbolsRead;
2395 QTextStream ts(&line, QIODevice::ReadOnly);
2396 ts >> module.startAddress >> module.endAddress >> symbolsRead;
2397 module.moduleName = ts.readLine().trimmed();
2398 module.symbolsRead = (symbolsRead == "Yes");
2399 modules.append(module);
2402 qq->modulesHandler()->setModules(modules);
2406 //////////////////////////////////////////////////////////////////////
2408 // Source files specific stuff
2410 //////////////////////////////////////////////////////////////////////
2412 void GdbEngine::reloadSourceFiles()
2414 sendCommand("-file-list-exec-source-files", GdbQuerySources);
2418 //////////////////////////////////////////////////////////////////////
2420 // Stack specific stuff
2422 //////////////////////////////////////////////////////////////////////
2424 void GdbEngine::handleStackSelectThread(const GdbResultRecord &record, int)
2427 //qDebug("FIXME: StackHandler::handleOutput: SelectThread");
2428 q->showStatusMessage(tr("Retrieving data for stack view..."), 3000);
2429 sendCommand("-stack-list-frames", StackListFrames);
2433 void GdbEngine::handleStackListFrames(const GdbResultRecord &record)
2435 QList<StackFrame> stackFrames;
2437 const GdbMi stack = record.data.findChild("stack");
2438 QString dummy = stack.toString();
2439 if (!stack.isValid()) {
2440 qDebug() << "FIXME: stack: " << stack.toString();
2446 for (int i = 0; i != stack.childCount(); ++i) {
2447 //qDebug() << "HANDLING FRAME: " << stack.childAt(i).toString();
2448 const GdbMi frameMi = stack.childAt(i);
2452 files.append(frameMi.findChild("fullname").data());
2453 files.append(frameMi.findChild("file").data());
2454 frame.file = fullName(files);
2455 frame.function = frameMi.findChild("func").data();
2456 frame.from = frameMi.findChild("from").data();
2457 frame.line = frameMi.findChild("line").data().toInt();
2458 frame.address = frameMi.findChild("addr").data();
2460 stackFrames.append(frame);
2463 const bool isBogus =
2464 // Assume this is wrong and points to some strange stl_algobase
2465 // implementation. Happens on Karsten's XP system with Gdb 5.50
2466 (frame.file.endsWith("/bits/stl_algobase.h") && frame.line == 150)
2467 // Also wrong. Happens on Vista with Gdb 5.50
2468 || (frame.function == "operator new" && frame.line == 151);
2470 // immediately leave bogus frames
2471 if (topFrame == -1 && isBogus) {
2472 sendCommand("-exec-finish");
2478 // Initialize top frame to the first valid frame
2479 const bool isValid = !frame.file.isEmpty() && !frame.function.isEmpty();
2480 if (isValid && topFrame == -1)
2484 qq->stackHandler()->setFrames(stackFrames);
2487 if (0 && topFrame != -1) {
2488 // updates of locals already triggered early
2489 const StackFrame &frame = qq->stackHandler()->currentFrame();
2490 bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable();
2492 q->gotoLocation(frame.file, frame.line, true);
2494 qDebug() << "FULL NAME NOT USABLE 0: " << frame.file;
2496 activateFrame(topFrame);
2499 if (topFrame != -1) {
2500 // updates of locals already triggered early
2501 const StackFrame &frame = qq->stackHandler()->currentFrame();
2502 bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable();
2504 q->gotoLocation(frame.file, frame.line, true);
2506 qDebug() << "FULL NAME NOT USABLE 0: " << frame.file << topFrame;
2511 void GdbEngine::selectThread(int index)
2513 //reset location arrow
2516 ThreadsHandler *threadsHandler = qq->threadsHandler();
2517 threadsHandler->setCurrentThread(index);
2519 QList<ThreadData> threads = threadsHandler->threads();
2520 QTC_ASSERT(index < threads.size(), return);
2521 int id = threads.at(index).id;
2522 q->showStatusMessage(tr("Retrieving data for stack view..."), 10000);
2523 sendCommand(QLatin1String("-thread-select ") + QString::number(id),
2527 void GdbEngine::activateFrame(int frameIndex)
2529 if (q->status() != DebuggerInferiorStopped)
2532 StackHandler *stackHandler = qq->stackHandler();
2533 int oldIndex = stackHandler->currentIndex();
2534 //qDebug() << "ACTIVATE FRAME: " << frameIndex << oldIndex
2535 // << stackHandler->currentIndex();
2537 QTC_ASSERT(frameIndex < stackHandler->stackSize(), return);
2539 if (oldIndex != frameIndex) {
2542 // Assuming this always succeeds saves a roundtrip.
2543 // Otherwise the lines below would need to get triggered
2544 // after a response to this -stack-select-frame here.
2545 sendCommand("-stack-select-frame " + QString::number(frameIndex));
2547 stackHandler->setCurrentIndex(frameIndex);
2551 const StackFrame &frame = stackHandler->currentFrame();
2553 bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable();
2555 q->gotoLocation(frame.file, frame.line, true);
2557 qDebug() << "FULL NAME NOT USABLE: " << frame.file;
2560 void GdbEngine::handleStackListThreads(const GdbResultRecord &record, int id)
2562 // "72^done,{thread-ids={thread-id="2",thread-id="1"},number-of-threads="2"}
2563 const QList<GdbMi> items = record.data.findChild("thread-ids").children();
2564 QList<ThreadData> threads;
2565 int currentIndex = -1;
2566 for (int index = 0, n = items.size(); index != n; ++index) {
2568 thread.id = items.at(index).data().toInt();
2569 threads.append(thread);
2570 if (thread.id == id) {
2571 //qDebug() << "SETTING INDEX TO: " << index << " ID: "<< id << "RECOD: "<< record.toString();
2572 currentIndex = index;
2575 ThreadsHandler *threadsHandler = qq->threadsHandler();
2576 threadsHandler->setThreads(threads);
2577 threadsHandler->setCurrentThread(currentIndex);
2581 //////////////////////////////////////////////////////////////////////
2583 // Register specific stuff
2585 //////////////////////////////////////////////////////////////////////
2587 void GdbEngine::reloadRegisters()
2589 QString format = qq->registerHandler()->model()->property(PROPERTY_REGISTER_FORMAT).toString();
2590 sendCommand("-data-list-register-values " + format, RegisterListValues);
2593 void GdbEngine::handleRegisterListNames(const GdbResultRecord &record)
2595 if (record.resultClass != GdbResultDone)
2598 QList<Register> registers;
2599 foreach (const GdbMi &item, record.data.findChild("register-names").children())
2600 registers.append(Register(item.data()));
2602 qq->registerHandler()->setRegisters(registers);
2605 void GdbEngine::handleRegisterListValues(const GdbResultRecord &record)
2607 if (record.resultClass != GdbResultDone)
2610 QList<Register> registers = qq->registerHandler()->registers();
2612 // 24^done,register-values=[{number="0",value="0xf423f"},...]
2613 foreach (const GdbMi &item, record.data.findChild("register-values").children()) {
2614 int index = item.findChild("number").data().toInt();
2615 if (index < registers.size()) {
2616 Register ® = registers[index];
2617 QString value = item.findChild("value").data();
2618 reg.changed = (value != reg.value);
2623 qq->registerHandler()->setRegisters(registers);
2627 //////////////////////////////////////////////////////////////////////
2629 // Thread specific stuff
2631 //////////////////////////////////////////////////////////////////////
2633 bool GdbEngine::supportsThreads() const
2635 // 6.3 crashes happily on -thread-list-ids. So don't use it.
2636 // The test below is a semi-random pick, 6.8 works fine
2637 return m_gdbVersion > 60500;
2640 //////////////////////////////////////////////////////////////////////
2642 // Tooltip specific stuff
2644 //////////////////////////////////////////////////////////////////////
2646 static WatchData m_toolTip;
2647 static QString m_toolTipExpression;
2648 static QPoint m_toolTipPos;
2649 static QMap<QString, WatchData> m_toolTipCache;
2651 static bool hasLetterOrNumber(const QString &exp)
2653 for (int i = exp.size(); --i >= 0; )
2654 if (exp[i].isLetterOrNumber() || exp[i] == '_')
2659 static bool hasSideEffects(const QString &exp)
2662 return exp.contains("-=")
2663 || exp.contains("+=")
2664 || exp.contains("/=")
2665 || exp.contains("*=")
2666 || exp.contains("&=")
2667 || exp.contains("|=")
2668 || exp.contains("^=")
2669 || exp.contains("--")
2670 || exp.contains("++");
2673 static bool isKeyWord(const QString &exp)
2675 // FIXME: incomplete
2676 return exp == QLatin1String("class")
2677 || exp == QLatin1String("const")
2678 || exp == QLatin1String("do")
2679 || exp == QLatin1String("if")
2680 || exp == QLatin1String("return")
2681 || exp == QLatin1String("struct")
2682 || exp == QLatin1String("template")
2683 || exp == QLatin1String("void")
2684 || exp == QLatin1String("volatile")
2685 || exp == QLatin1String("while");
2688 void GdbEngine::setToolTipExpression(const QPoint &pos, const QString &exp0)
2690 //qDebug() << "SET TOOLTIP EXP" << pos << exp0;
2691 if (q->status() != DebuggerInferiorStopped) {
2692 //qDebug() << "SUPPRESSING DEBUGGER TOOLTIP, INFERIOR NOT STOPPED";
2696 if (q->settings()->m_debugDumpers) {
2697 // minimize interference
2702 m_toolTipExpression = exp0;
2705 if (m_toolTip.isTypePending()) {
2706 qDebug() << "suppressing duplicated tooltip creation";
2710 if (m_toolTipCache.contains(exp)) {
2711 const WatchData & data = m_toolTipCache[exp];
2712 // FIXME: qq->watchHandler()->collapseChildren(data.iname);
2717 QToolTip::hideText();
2718 if (exp.isEmpty() || exp.startsWith("#")) {
2719 QToolTip::hideText();
2723 if (!hasLetterOrNumber(exp)) {
2724 QToolTip::showText(m_toolTipPos,
2725 "'" + exp + "' contains no identifier");
2732 if (exp.startsWith('"') && exp.endsWith('"')) {
2733 QToolTip::showText(m_toolTipPos, "String literal " + exp);
2737 if (exp.startsWith("++") || exp.startsWith("--"))
2740 if (exp.endsWith("++") || exp.endsWith("--"))
2743 if (exp.startsWith("<") || exp.startsWith("["))
2746 if (hasSideEffects(exp)) {
2747 QToolTip::showText(m_toolTipPos,
2748 "Cowardly refusing to evaluate expression '" + exp
2749 + "' with potential side effects");
2753 // Gdb crashes when creating a variable object with the name
2754 // of the type of 'this'
2756 for (int i = 0; i != m_currentLocals.childCount(); ++i) {
2757 if (m_currentLocals.childAt(i).exp == "this") {
2758 qDebug() << "THIS IN ROW " << i;
2759 if (m_currentLocals.childAt(i).type.startsWith(exp)) {
2760 QToolTip::showText(m_toolTipPos,
2761 exp + ": type of current 'this'");
2762 qDebug() << " TOOLTIP CRASH SUPPRESSED";
2770 //if (m_manager->status() != DebuggerInferiorStopped)
2773 // FIXME: 'exp' can contain illegal characters
2774 m_toolTip = WatchData();
2775 //m_toolTip.level = 0;
2776 // m_toolTip.row = 0;
2777 // m_toolTip.parentIndex = 2;
2778 m_toolTip.exp = exp;
2779 m_toolTip.name = exp;
2780 m_toolTip.iname = tooltipIName;
2781 insertData(m_toolTip);
2782 updateWatchModel2();
2786 //////////////////////////////////////////////////////////////////////
2788 // Watch specific stuff
2790 //////////////////////////////////////////////////////////////////////
2792 static const QString strNotInScope = QLatin1String("<not in scope>");
2794 static bool isPointerType(const QString &type)
2796 return type.endsWith("*") || type.endsWith("* const");
2799 static bool isAccessSpecifier(const QString &str)
2801 static const QStringList items =
2802 QStringList() << "private" << "protected" << "public";
2803 return items.contains(str);
2806 static bool startsWithDigit(const QString &str)
2808 return !str.isEmpty() && str[0] >= '0' && str[0] <= '9';
2811 QString stripPointerType(QString type)
2813 if (type.endsWith("*"))
2815 if (type.endsWith("* const"))
2817 if (type.endsWith(' '))
2822 static QString gdbQuoteTypes(const QString &type)
2824 // gdb does not understand sizeof(Core::IFile*).
2825 // "sizeof('Core::IFile*')" is also not acceptable,
2826 // it needs to be "sizeof('Core::IFile'*)"
2828 // We never will have a perfect solution here (even if we had a full blown
2829 // C++ parser as we do not have information on what is a type and what is
2830 // a variable name. So "a<b>::c" could either be two comparisons of values
2831 // 'a', 'b' and '::c', or a nested type 'c' in a template 'a<b>'. We
2832 // assume here it is the latter.
2835 // (*('myns::QPointer<myns::QObject>*'*)0x684060)" is not acceptable
2836 // (*('myns::QPointer<myns::QObject>'**)0x684060)" is acceptable
2837 if (isPointerType(type))
2838 return gdbQuoteTypes(stripPointerType(type)) + "*";
2842 int templateLevel = 0;
2843 for (int i = 0; i != type.size(); ++i) {
2844 QChar c = type.at(i);
2845 if (c.isLetterOrNumber() || c == '_' || c == ':' || c == ' ') {
2847 } else if (c == '<') {
2850 } else if (c == '<') {
2853 } else if (templateLevel > 0) {
2856 if (accu.contains(':') || accu.contains('<'))
2857 result += '\'' + accu + '\'';
2864 if (accu.contains(':') || accu.contains('<'))
2865 result += '\'' + accu + '\'';
2868 //qDebug() << "GDB_QUOTING" << type << " TO " << result;
2873 static void setWatchDataValue(WatchData &data, const GdbMi &mi,
2879 case 0: // unencoded 8 bit data
2882 case 1: // base64 encoded 8 bit data
2883 ba = QByteArray::fromBase64(mi.data());
2884 ba = '"' + ba + '"';
2886 case 2: // base64 encoded 16 bit data
2887 ba = QByteArray::fromBase64(mi.data());
2888 ba = QString::fromUtf16((ushort *)ba.data(), ba.size() / 2).toUtf8();
2889 ba = '"' + ba + '"';
2891 case 3: // base64 encoded 32 bit data
2892 ba = QByteArray::fromBase64(mi.data());
2893 ba = QString::fromUcs4((uint *)ba.data(), ba.size() / 4).toUtf8();
2894 ba = '"' + ba + '"';
2899 data.setValueNeeded();
2903 static void setWatchDataEditValue(WatchData &data, const GdbMi &mi)
2906 data.editvalue = mi.data();
2909 static void setWatchDataValueToolTip(WatchData &data, const GdbMi &mi)
2912 data.setValueToolTip(mi.data());
2915 static void setWatchDataChildCount(WatchData &data, const GdbMi &mi)
2918 data.childCount = mi.data().toInt();
2919 data.setChildCountUnneeded();
2920 if (data.childCount == 0)
2921 data.setChildrenUnneeded();
2923 data.childCount = -1;
2927 static void setWatchDataValueDisabled(WatchData &data, const GdbMi &mi)
2929 if (mi.data() == "true")
2930 data.valuedisabled = true;
2931 else if (mi.data() == "false")
2932 data.valuedisabled = false;
2935 static void setWatchDataExpression(WatchData &data, const GdbMi &mi)
2938 data.exp = "(" + mi.data() + ")";
2941 static void setWatchDataAddress(WatchData &data, const GdbMi &mi)
2944 data.addr = mi.data();
2945 if (data.exp.isEmpty())
2946 data.exp = "(*(" + gdbQuoteTypes(data.type) + "*)" + data.addr + ")";
2950 static bool extractTemplate(const QString &type, QString *tmplate, QString *inner)
2952 // Input "Template<Inner1,Inner2,...>::Foo" will return "Template::Foo" in
2953 // 'tmplate' and "Inner1@Inner2@..." etc in 'inner'. Result indicates
2954 // whether parsing was successful
2956 bool skipSpace = false;
2957 for (int i = 0; i != type.size(); ++i) {
2959 if (c == ' ' && skipSpace) {
2961 } else if (c == '<') {
2962 *(level == 0 ? tmplate : inner) += c;
2964 } else if (c == '>') {
2966 *(level == 0 ? tmplate : inner) += c;
2967 } else if (c == ',') {
2968 *inner += (level == 1) ? '@' : ',';
2971 *(level == 0 ? tmplate : inner) += c;
2974 *tmplate = tmplate->trimmed();
2975 *tmplate = tmplate->remove("<>");
2976 *inner = inner->trimmed();
2977 //qDebug() << "EXTRACT TEMPLATE: " << *tmplate << *inner << " FROM " << type;
2978 return !inner->isEmpty();
2981 static QString extractTypeFromPTypeOutput(const QString &str)
2983 int pos0 = str.indexOf('=');
2984 int pos1 = str.indexOf('{');
2985 int pos2 = str.lastIndexOf('}');
2987 if (pos0 != -1 && pos1 != -1 && pos2 != -1)
2988 res = str.mid(pos0 + 2, pos1 - 1 - pos0)
2989 + " ... " + str.right(str.size() - pos2);
2990 return res.simplified();
2993 static bool isIntOrFloatType(const QString &type)
2995 static const QStringList types = QStringList()
2996 << "char" << "int" << "short" << "float" << "double" << "long"
2997 << "bool" << "signed char" << "unsigned" << "unsigned char"
2998 << "unsigned int" << "unsigned long" << "long long"
2999 << "unsigned long long";
3000 return types.contains(type);
3003 static QString sizeofTypeExpression(const QString &type)
3005 if (type.endsWith('*'))
3006 return "sizeof(void*)";
3007 if (type.endsWith('>'))
3008 return "sizeof(" + type + ")";
3009 return "sizeof(" + gdbQuoteTypes(type) + ")";
3012 void GdbEngine::setUseCustomDumpers(bool on)
3014 //qDebug() << "SWITCHING ON/OFF DUMPER DEBUGGING:" << on;
3016 // FIXME: a bit too harsh, but otherwise the treeview sometimes look funny
3017 //m_expandedINames.clear();
3022 bool GdbEngine::isCustomValueDumperAvailable(const QString &type) const
3024 DebuggerSettings *s = q->settings();
3025 if (!s->m_useCustomDumpers)
3027 if (s->m_debugDumpers && qq->stackHandler()->isDebuggingDumpers())
3029 if (m_dataDumperState != DataDumperAvailable)
3033 if (m_availableSimpleDumpers.contains(type))
3039 if (!extractTemplate(type, &tmplate, &inner))
3041 return m_availableSimpleDumpers.contains(tmplate);
3044 void GdbEngine::runCustomDumper(const WatchData & data0, bool dumpChildren)
3046 WatchData data = data0;
3047 QTC_ASSERT(!data.exp.isEmpty(), return);
3050 bool isTemplate = extractTemplate(data.type, &tmplate, &inner);
3051 QStringList inners = inner.split('@');
3052 if (inners.at(0).isEmpty())
3054 for (int i = 0; i != inners.size(); ++i)
3055 inners[i] = inners[i].simplified();
3057 QString outertype = isTemplate ? tmplate : data.type;
3058 // adjust the data extract
3059 if (outertype == m_namespace + "QWidget")
3060 outertype = m_namespace + "QObject";
3062 QString extraArgs[4];
3067 int extraArgCount = 0;
3069 // "generic" template dumpers: passing sizeof(argument)
3070 // gives already most information the dumpers need
3071 foreach (const QString &arg, inners)
3072 extraArgs[extraArgCount++] = sizeofTypeExpression(arg);
3074 // in rare cases we need more or less:
3075 if (outertype == m_namespace + "QObject") {
3076 extraArgs[0] = "(char*)&((('"
3077 + m_namespace + "QObjectPrivate'*)&"
3078 + data.exp + ")->children)-(char*)&" + data.exp;
3079 } else if (outertype == m_namespace + "QVector") {
3080 extraArgs[1] = "(char*)&(("
3081 + data.exp + ").d->array)-(char*)" + data.exp + ".d";
3082 } else if (outertype == m_namespace + "QObjectSlot"
3083 || outertype == m_namespace + "QObjectSignal") {
3084 // we need the number out of something like
3085 // iname="local.ob.slots.[2]deleteLater()"
3086 int lastOpened = data.iname.lastIndexOf('[');
3087 int lastClosed = data.iname.lastIndexOf(']');
3088 QString slotNumber = "-1";
3089 if (lastOpened != -1 && lastClosed != -1)
3090 slotNumber = data.iname.mid(lastOpened + 1, lastClosed - lastOpened - 1);
3091 extraArgs[0] = slotNumber;
3092 } else if (outertype == m_namespace + "QMap" || outertype == m_namespace + "QMultiMap") {
3094 if (m_qtVersion >= (4 << 16) + (5 << 8) + 0) {
3095 nodetype = m_namespace + "QMapNode";
3096 nodetype += data.type.mid(outertype.size());
3098 // FIXME: doesn't work for QMultiMap
3099 nodetype = data.type + "::Node";
3101 //qDebug() << "OUTERTYPE: " << outertype << " NODETYPE: " << nodetype
3102 // << "QT VERSION" << m_qtVersion << ((4 << 16) + (5 << 8) + 0);
3103 extraArgs[2] = sizeofTypeExpression(nodetype);
3104 extraArgs[3] = "(size_t)&(('" + nodetype + "'*)0)->value";
3105 } else if (outertype == m_namespace + "QMapNode") {
3106 extraArgs[2] = sizeofTypeExpression(data.type);
3107 extraArgs[3] = "(size_t)&(('" + data.type + "'*)0)->value";
3108 } else if (outertype == "std::vector") {
3109 //qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
3110 if (inners.at(0) == "bool") {
3111 outertype = "std::vector::bool";
3113 //extraArgs[extraArgCount++] = sizeofTypeExpression(data.type);
3114 //extraArgs[extraArgCount++] = "(size_t)&(('" + data.type + "'*)0)->value";
3116 } else if (outertype == "std::deque") {
3117 // remove 'std::allocator<...>':
3119 } else if (outertype == "std::stack") {
3120 // remove 'std::allocator<...>':
3122 } else if (outertype == "std::map") {
3123 // We don't want the comparator and the allocator confuse gdb.
3124 // But we need the offset of the second item in the value pair.
3125 // We read the type of the pair from the allocator argument because
3126 // that gets the constness "right" (in the sense that gdb can
3128 QString pairType = inners.at(3);
3129 // remove 'std::allocator<...>':
3130 pairType = pairType.mid(15, pairType.size() - 15 - 2);
3131 extraArgs[2] = "(size_t)&(('" + pairType + "'*)0)->second";
3133 } else if (outertype == "std::basic_string") {
3134 //qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
3135 if (inners.at(0) == "char") {
3136 outertype = "std::string";
3137 } else if (inners.at(0) == "wchar_t") {
3138 outertype = "std::wstring";
3146 //int protocol = (data.iname.startsWith("watch") && data.type == "QImage") ? 3 : 2;
3147 //int protocol = data.iname.startsWith("watch") ? 3 : 2;
3149 //int protocol = isDisplayedIName(data.iname) ? 3 : 2;
3152 if (data.addr.startsWith("0x"))
3153 addr = "(void*)" + data.addr;
3155 addr = "&(" + data.exp + ")";
3158 params.append(outertype.toUtf8());
3159 params.append('\0');
3160 params.append(data.iname.toUtf8());
3161 params.append('\0');
3162 params.append(data.exp.toUtf8());
3163 params.append('\0');
3164 params.append(inner.toUtf8());
3165 params.append('\0');
3166 params.append(data.iname.toUtf8());
3167 params.append('\0');
3169 sendWatchParameters(params);
3171 QString cmd ="call "
3172 + QString("qDumpObjectData440(")
3173 + QString::number(protocol)
3174 + ',' + "%1+1" // placeholder for token
3176 + ',' + (dumpChildren ? "1" : "0")
3177 + ',' + extraArgs[0]
3178 + ',' + extraArgs[1]
3179 + ',' + extraArgs[2]
3180 + ',' + extraArgs[3] + ')';
3182 //qDebug() << "CMD: " << cmd;
3186 sendSynchronizedCommand(cmd, WatchDumpCustomValue1, var);
3188 q->showStatusMessage(
3189 tr("Retrieving data for watch view (%1 requests pending)...")
3190 .arg(m_pendingRequests + 1), 10000);
3192 // retrieve response
3193 sendSynchronizedCommand("p (char*)qDumpOutBuffer", WatchDumpCustomValue2, var);
3196 void GdbEngine::createGdbVariable(const WatchData &data)
3198 sendSynchronizedCommand("-var-delete \"" + data.iname + '"');
3199 QString exp = data.exp;
3200 if (exp.isEmpty() && data.addr.startsWith("0x"))
3201 exp = "*(" + gdbQuoteTypes(data.type) + "*)" + data.addr;
3202 QVariant val = QVariant::fromValue<WatchData>(data);
3203 sendSynchronizedCommand("-var-create \"" + data.iname + '"' + " * "
3204 + '"' + exp + '"', WatchVarCreate, val);
3207 void GdbEngine::updateSubItem(const WatchData &data0)
3209 WatchData data = data0;
3211 qDebug() << "UPDATE SUBITEM: " << data.toString();
3213 QTC_ASSERT(data.isValid(), return);
3215 // in any case we need the type first
3216 if (data.isTypeNeeded()) {
3217 // This should only happen if we don't have a variable yet.
3218 // Let's play safe, though.
3219 if (!data.variable.isEmpty()) {
3220 // Update: It does so for out-of-scope watchers.
3222 qDebug() << "FIXME: GdbEngine::updateSubItem: "
3223 << data.toString() << "should not happen";
3225 data.setType("<out of scope>");
3226 data.setValue("<out of scope>");
3227 data.setChildCount(0);
3232 // The WatchVarCreate handler will receive type information
3233 // and re-insert a WatchData item with correct type, so
3234 // we will not re-enter this bit.
3235 // FIXME: Concurrency issues?
3236 createGdbVariable(data);
3240 // we should have a type now. this is relied upon further below
3241 QTC_ASSERT(!data.type.isEmpty(), return);
3243 // a common case that can be easily solved
3244 if (data.isChildrenNeeded() && isPointerType(data.type)
3245 && !isCustomValueDumperAvailable(data.type)) {
3246 // We sometimes know what kind of children pointers have
3248 qDebug() << "IT'S A POINTER";
3252 data1.iname = data.iname + ".*";
3253 data1.name = "*" + data.name;
3254 data1.exp = "(*(" + data.exp + "))";
3255 data1.type = stripPointerType(data.type);
3256 data1.setValueNeeded();
3258 data.setChildrenUnneeded();
3261 // Try automatic dereferentiation
3262 data.exp = "*(" + data.exp + ")";
3263 data.type = data.type + "."; // FIXME: fragile HACK to avoid recursion
3269 if (data.isValueNeeded() && isCustomValueDumperAvailable(data.type)) {
3271 qDebug() << "UPDATE SUBITEM: CUSTOMVALUE";
3273 runCustomDumper(data, qq->watchHandler()->isExpandedIName(data.iname));
3278 if (data.isValueNeeded() && data.exp.isEmpty()) {
3280 qDebug() << "UPDATE SUBITEM: NO EXPRESSION?";
3282 data.setError("<no expression given>");
3288 if (data.isValueNeeded() && data.variable.isEmpty()) {
3290 qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR VALUE";
3292 createGdbVariable(data);
3293 // the WatchVarCreate handler will re-insert a WatchData
3294 // item, with valueNeeded() set.
3298 if (data.isValueNeeded()) {
3299 QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
3301 qDebug() << "UPDATE SUBITEM: VALUE";
3303 QString cmd = "-var-evaluate-expression \"" + data.iname + "\"";
3304 sendSynchronizedCommand(cmd, WatchEvaluateExpression,
3305 QVariant::fromValue(data));
3309 if (data.isChildrenNeeded() && isCustomValueDumperAvailable(data.type)) {
3311 qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN";
3313 runCustomDumper(data, true);
3317 if (data.isChildrenNeeded() && data.variable.isEmpty()) {
3319 qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDREN";
3321 createGdbVariable(data);
3322 // the WatchVarCreate handler will re-insert a WatchData
3323 // item, with childrenNeeded() set.
3327 if (data.isChildrenNeeded()) {
3328 QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
3329 QString cmd = "-var-list-children --all-values \"" + data.variable + "\"";
3330 sendSynchronizedCommand(cmd, WatchVarListChildren, QVariant::fromValue(data));
3334 if (data.isChildCountNeeded() && isCustomValueDumperAvailable(data.type)) {
3336 qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN";
3338 runCustomDumper(data, qq->watchHandler()->isExpandedIName(data.iname));
3342 if (data.isChildCountNeeded() && data.variable.isEmpty()) {
3344 qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDCOUNT";
3346 createGdbVariable(data);
3347 // the WatchVarCreate handler will re-insert a WatchData
3348 // item, with childrenNeeded() set.
3352 if (data.isChildCountNeeded()) {
3353 QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
3354 QString cmd = "-var-list-children --all-values \"" + data.variable + "\"";
3355 sendCommand(cmd, WatchVarListChildren, QVariant::fromValue(data));
3359 qDebug() << "FIXME: UPDATE SUBITEM: " << data.toString();
3360 QTC_ASSERT(false, return);
3363 void GdbEngine::updateWatchModel()
3365 m_pendingRequests = 0;
3366 PENDING_DEBUG("EXTERNAL TRIGGERING UPDATE WATCH MODEL");
3367 updateWatchModel2();
3370 void GdbEngine::updateWatchModel2()
3372 PENDING_DEBUG("UPDATE WATCH MODEL");
3373 QList<WatchData> incomplete = qq->watchHandler()->takeCurrentIncompletes();
3374 //QTC_ASSERT(incomplete.isEmpty(), /**/);
3375 if (!incomplete.isEmpty()) {
3377 qDebug() << "##############################################";
3378 qDebug() << "UPDATE MODEL, FOUND INCOMPLETES:";
3379 foreach (const WatchData &data, incomplete)
3380 qDebug() << data.toString();
3383 // Bump requests to avoid model rebuilding during the nested
3384 // updateWatchModel runs.
3385 ++m_pendingRequests;
3386 foreach (const WatchData &data, incomplete)
3387 updateSubItem(data);
3388 PENDING_DEBUG("INTERNAL TRIGGERING UPDATE WATCH MODEL");
3389 updateWatchModel2();
3390 --m_pendingRequests;
3395 if (m_pendingRequests > 0) {
3396 PENDING_DEBUG("UPDATE MODEL, PENDING: " << m_pendingRequests);
3400 PENDING_DEBUG("REBUILDING MODEL");
3401 emit gdbInputAvailable(QString(),
3402 "[" + currentTime() + "] <Rebuild Watchmodel>");
3403 q->showStatusMessage(tr("Finished retrieving data."), 400);
3404 qq->watchHandler()->rebuildModel();
3406 if (!m_toolTipExpression.isEmpty()) {
3407 WatchData *data = qq->watchHandler()->findData(tooltipIName);
3409 //m_toolTipCache[data->exp] = *data;
3410 QToolTip::showText(m_toolTipPos,
3411 "(" + data->type + ") " + data->exp + " = " + data->value);
3413 QToolTip::showText(m_toolTipPos,
3414 "Cannot evaluate expression: " + m_toolTipExpression);
3419 void GdbEngine::handleQueryDataDumper1(const GdbResultRecord &record)
3424 void GdbEngine::handleQueryDataDumper2(const GdbResultRecord &record)
3426 //qDebug() << "DATA DUMPER TRIAL:" << record.toString();
3427 GdbMi output = record.data.findChild("consolestreamoutput");
3428 QByteArray out = output.data();
3429 out = out.mid(out.indexOf('"') + 2); // + 1 is success marker
3430 out = out.left(out.lastIndexOf('"'));
3431 //out.replace('\'', '"');
3432 out.replace("\\", "");
3433 out = "dummy={" + out + "}";
3434 //qDebug() << "OUTPUT: " << out;
3437 contents.fromString(out);
3438 GdbMi simple = contents.findChild("dumpers");
3439 m_namespace = contents.findChild("namespace").data();
3440 GdbMi qtversion = contents.findChild("qtversion");
3441 if (qtversion.children().size() == 3) {
3442 m_qtVersion = (qtversion.childAt(0).data().toInt() << 16)
3443 + (qtversion.childAt(1).data().toInt() << 8)
3444 + qtversion.childAt(2).data().toInt();
3445 //qDebug() << "FOUND QT VERSION: " << qtversion.toString() << m_qtVersion;
3450 //qDebug() << "CONTENTS: " << contents.toString();
3451 //qDebug() << "SIMPLE DUMPERS: " << simple.toString();
3452 m_availableSimpleDumpers.clear();
3453 foreach (const GdbMi &item, simple.children())
3454 m_availableSimpleDumpers.append(item.data());
3455 if (m_availableSimpleDumpers.isEmpty()) {
3456 m_dataDumperState = DataDumperUnavailable;
3457 QMessageBox::warning(q->mainWindow(),
3458 tr("Cannot find special data dumpers"),
3459 tr("The debugged binary does not contain information needed for "
3460 "nice display of Qt data types.\n\n"
3461 "You might want to try including the file\n\n"
3462 ".../share/qtcreator/gdbmacros/gdbmacros.cpp\n\n"
3463 "into your project directly.")
3466 m_dataDumperState = DataDumperAvailable;
3468 //qDebug() << "DATA DUMPERS AVAILABLE" << m_availableSimpleDumpers;
3471 void GdbEngine::sendWatchParameters(const QByteArray ¶ms0)
3473 QByteArray params = params0;
3474 params.append('\0');
3476 sprintf(buf, "set {char[%d]} qDumpInBuffer = {", params.size());
3478 encoded.append(buf);
3479 for (int i = 0; i != params.size(); ++i) {
3480 sprintf(buf, "%d,", int(params[i]));
3481 encoded.append(buf);
3483 encoded[encoded.size() - 1] = '}';
3485 sendCommand(encoded);
3488 void GdbEngine::handleVarAssign()
3490 // everything might have changed, force re-evaluation
3491 // FIXME: Speed this up by re-using variables and only
3492 // marking values as 'unknown'
3497 void GdbEngine::setWatchDataType(WatchData &data, const GdbMi &mi)
3500 if (!data.framekey.isEmpty())
3501 m_varToType[data.framekey] = mi.data();
3502 data.setType(mi.data());
3503 } else if (data.type.isEmpty()) {
3504 data.setTypeNeeded();
3508 void GdbEngine::handleVarCreate(const GdbResultRecord &record,
3509 const WatchData &data0)
3511 WatchData data = data0;
3512 // happens e.g. when we already issued a var-evaluate command
3513 if (!data.isValid())
3515 //qDebug() << "HANDLE VARIABLE CREATION: " << data.toString();
3516 if (record.resultClass == GdbResultDone) {
3517 data.variable = data.iname;
3518 setWatchDataType(data, record.data.findChild("type"));
3519 if (isCustomValueDumperAvailable(data.type)) {
3520 // we do not trust gdb if we have a custom dumper
3521 if (record.data.findChild("children").isValid())
3522 data.setChildrenUnneeded();
3523 else if (qq->watchHandler()->isExpandedIName(data.iname))
3524 data.setChildrenNeeded();
3527 if (record.data.findChild("children").isValid())
3528 data.setChildrenUnneeded();
3529 else if (qq->watchHandler()->isExpandedIName(data.iname))
3530 data.setChildrenNeeded();
3531 setWatchDataChildCount(data, record.data.findChild("numchild"));
3532 //if (data.isValueNeeded() && data.childCount > 0)
3533 // data.setValue(QByteArray());
3536 } else if (record.resultClass == GdbResultError) {
3537 data.setError(record.data.findChild("msg").data());
3538 if (data.isWatcher()) {
3539 data.value = strNotInScope;
3541 data.setAllUnneeded();
3542 data.setChildCount(0);
3543 data.valuedisabled = true;
3549 void GdbEngine::handleEvaluateExpression(const GdbResultRecord &record,
3550 const WatchData &data0)
3552 WatchData data = data0;
3553 QTC_ASSERT(data.isValid(), qDebug() << "HUH?");
3554 if (record.resultClass == GdbResultDone) {
3556 // data.name = record.data.findChild("value").data();
3558 setWatchDataValue(data, record.data.findChild("value"));
3559 } else if (record.resultClass == GdbResultError) {
3560 data.setError(record.data.findChild("msg").data());
3562 //qDebug() << "HANDLE EVALUATE EXPRESSION: " << data.toString();
3564 //updateWatchModel2();
3567 void GdbEngine::handleDumpCustomSetup(const GdbResultRecord &record)
3569 //qDebug() << "CUSTOM SETUP RESULT: " << record.toString();
3570 if (record.resultClass == GdbResultDone) {
3571 } else if (record.resultClass == GdbResultError) {
3572 QString msg = record.data.findChild("msg").data();
3573 //qDebug() << "CUSTOM DUMPER SETUP ERROR MESSAGE: " << msg;
3574 q->showStatusMessage(tr("Custom dumper setup: %1").arg(msg), 10000);
3578 void GdbEngine::handleDumpCustomValue1(const GdbResultRecord &record,
3579 const WatchData &data0)
3581 WatchData data = data0;
3582 QTC_ASSERT(data.isValid(), return);
3583 if (record.resultClass == GdbResultDone) {
3584 // ignore this case, data will follow
3585 } else if (record.resultClass == GdbResultError) {
3586 // Record an extra result, as the socket result will be lost
3588 //--m_pendingRequests;
3589 QString msg = record.data.findChild("msg").data();
3590 //qDebug() << "CUSTOM DUMPER ERROR MESSAGE: " << msg;
3592 // Make debugging of dumpers easier
3593 if (q->settings()->m_debugDumpers
3594 && msg.startsWith("The program being debugged stopped while")
3595 && msg.contains("qDumpObjectData440")) {
3597 sendCommand("p 0", GdbAsyncOutput2); // dummy
3601 //if (msg.startsWith("The program being debugged was sig"))
3602 // msg = strNotInScope;
3603 //if (msg.startsWith("The program being debugged stopped while"))
3604 // msg = strNotInScope;
3605 //data.setError(msg);
3610 void GdbEngine::handleDumpCustomValue2(const GdbResultRecord &record,
3611 const WatchData &data0)
3613 WatchData data = data0;
3614 QTC_ASSERT(data.isValid(), return);
3615 //qDebug() << "CUSTOM VALUE RESULT: " << record.toString();
3616 //qDebug() << "FOR DATA: " << data.toString() << record.resultClass;
3617 if (record.resultClass != GdbResultDone) {
3618 qDebug() << "STRANGE CUSTOM DUMPER RESULT DATA: " << data.toString();
3622 GdbMi output = record.data.findChild("consolestreamoutput");
3623 QByteArray out = output.data();
3625 int markerPos = out.indexOf('"') + 1; // position of 'success marker'
3626 if (markerPos == -1 || out.at(markerPos) == 'f') { // 't' or 'f'
3627 // custom dumper produced no output
3628 data.setError(strNotInScope);
3633 out = out.mid(markerPos + 1);
3634 out = out.left(out.lastIndexOf('"'));
3635 out.replace("\\", "");
3636 out = "dummy={" + out + "}";
3639 contents.fromString(out);
3640 //qDebug() << "CONTENTS" << contents.toString(true);
3641 if (!contents.isValid()) {
3642 data.setError(strNotInScope);
3647 setWatchDataType(data, contents.findChild("type"));
3648 setWatchDataValue(data, contents.findChild("value"),
3649 contents.findChild("valueencoded").data().toInt());
3650 setWatchDataAddress(data, contents.findChild("addr"));
3651 setWatchDataChildCount(data, contents.findChild("numchild"));
3652 setWatchDataValueToolTip(data, contents.findChild("valuetooltip"));
3653 setWatchDataValueDisabled(data, contents.findChild("valuedisabled"));
3654 setWatchDataEditValue(data, contents.findChild("editvalue"));
3655 if (qq->watchHandler()->isDisplayedIName(data.iname)) {
3656 GdbMi editvalue = contents.findChild("editvalue");
3657 if (editvalue.isValid()) {
3658 setWatchDataEditValue(data, editvalue);
3659 qq->watchHandler()->showEditValue(data);
3662 if (!qq->watchHandler()->isExpandedIName(data.iname))
3663 data.setChildrenUnneeded();
3664 GdbMi children = contents.findChild("children");
3665 if (children.isValid() || !qq->watchHandler()->isExpandedIName(data.iname))
3666 data.setChildrenUnneeded();
3667 data.setValueUnneeded();
3669 // try not to repeat data too often
3670 WatchData childtemplate;
3671 setWatchDataType(childtemplate, contents.findChild("childtype"));
3672 setWatchDataChildCount(childtemplate, contents.findChild("childnumchild"));
3673 //qDebug() << "DATA: " << data.toString();
3675 foreach (GdbMi item, children.children()) {
3676 WatchData data1 = childtemplate;
3677 data1.name = item.findChild("name").data();
3678 data1.iname = data.iname + "." + data1.name;
3679 if (!data1.name.isEmpty() && data1.name.at(0).isDigit())
3680 data1.name = '[' + data1.name + ']';
3681 QString key = item.findChild("key").data();
3682 if (!key.isEmpty()) {
3683 if (item.findChild("keyencoded").data()[0] == '1') {
3684 key = '"' + QByteArray::fromBase64(key.toUtf8()) + '"';
3685 if (key.size() > 13) {
3690 //data1.name += " (" + key + ")";
3693 setWatchDataType(data1, item.findChild("type"));
3694 setWatchDataExpression(data1, item.findChild("exp"));
3695 setWatchDataChildCount(data1, item.findChild("numchild"));
3696 setWatchDataValue(data1, item.findChild("value"),
3697 item.findChild("valueencoded").data().toInt());
3698 setWatchDataAddress(data1, item.findChild("addr"));
3699 setWatchDataValueToolTip(data1, item.findChild("valuetooltip"));
3700 setWatchDataValueDisabled(data1, item.findChild("valuedisabled"));
3701 if (!qq->watchHandler()->isExpandedIName(data1.iname))
3702 data1.setChildrenUnneeded();
3703 //qDebug() << "HANDLE CUSTOM SUBCONTENTS:" << data1.toString();
3708 void GdbEngine::updateLocals()
3710 m_pendingRequests = 0;
3712 PENDING_DEBUG("\nRESET PENDING");
3713 m_toolTipCache.clear();
3714 m_toolTipExpression.clear();
3715 qq->watchHandler()->reinitializeWatchers();
3717 int level = currentFrame();
3718 // '2' is 'list with type and value'
3719 QString cmd = QString("-stack-list-arguments 2 %1 %2").arg(level).arg(level);
3720 sendSynchronizedCommand(cmd, StackListArguments); // stage 1/2
3721 // '2' is 'list with type and value'
3722 sendSynchronizedCommand("-stack-list-locals 2", StackListLocals); // stage 2/2
3724 //tryLoadCustomDumpers();
3727 void GdbEngine::handleStackListArguments(const GdbResultRecord &record)
3732 // 12^done,stack-args=
3733 // [frame={level="0",args=[
3734 // {name="argc",type="int",value="1"},
3735 // {name="argv",type="char **",value="(char **) 0x7..."}]}]
3737 // 78^done,stack-args=
3738 // {frame={level="0",args={
3740 // {exp="this",value="0x38a2fab0",name="var21",numchild="3",
3741 // type="CurrentDocumentFind * const",typecode="PTR",
3742 // dynamic_type="",in_scope="true",block_start_addr="0x3938e946",
3743 // block_end_addr="0x3938eb2d"},
3745 // {exp="before",value="@0xbfffb9f8: {d = 0x3a7f2a70}",
3746 // name="var22",numchild="1",type="const QString ...} }}}
3748 // In both cases, iterating over the children of stack-args/frame/args
3750 m_currentFunctionArgs.clear();
3751 if (record.resultClass == GdbResultDone) {
3752 const GdbMi list = record.data.findChild("stack-args");
3753 const GdbMi frame = list.findChild("frame");
3754 const GdbMi args = frame.findChild("args");
3755 m_currentFunctionArgs = args.children();
3756 } else if (record.resultClass == GdbResultError) {
3757 qDebug() << "FIXME: GdbEngine::handleStackListArguments: should not happen";
3761 void GdbEngine::handleStackListLocals(const GdbResultRecord &record)
3765 // There could be shadowed variables
3766 QList<GdbMi> locals = record.data.findChild("locals").children();
3767 locals += m_currentFunctionArgs;
3772 void GdbEngine::setLocals(const QList<GdbMi> &locals)
3774 //qDebug() << m_varToType;
3775 QMap<QString, int> seen;
3777 foreach (const GdbMi &item, locals) {
3778 // Local variables of inlined code are reported as
3779 // 26^done,locals={varobj={exp="this",value="",name="var4",exp="this",
3780 // numchild="1",type="const QtSharedPointer::Basic<CPlusPlus::..."
3781 // We do not want these at all. Current hypotheses is that those
3782 // "spurious" locals have _two_ "exp" field. Try to filter them:
3785 foreach (const GdbMi &child, item.children())
3786 numExps += int(child.name() == "exp");
3789 QString name = item.findChild("exp").data();
3791 QString name = item.findChild("name").data();
3793 int n = seen.value(name);
3797 data.iname = "local." + name + QString::number(n + 1);
3798 data.name = name + QString(" <shadowed %1>").arg(n);
3799 //data.setValue("<shadowed>");
3800 setWatchDataValue(data, item.findChild("value"));
3801 data.setType("<shadowed>");
3802 data.setChildCount(0);
3807 data.iname = "local." + name;
3810 data.framekey = m_currentFrame + data.name;
3811 setWatchDataType(data, item.findChild("type"));
3812 // set value only directly if it is simple enough, otherwise
3813 // pass through the insertData() machinery
3814 if (isIntOrFloatType(data.type) || isPointerType(data.type))
3815 setWatchDataValue(data, item.findChild("value"));
3816 if (!qq->watchHandler()->isExpandedIName(data.iname))
3817 data.setChildrenUnneeded();
3818 if (isPointerType(data.type) || data.name == "this")
3819 data.setChildCount(1);
3820 if (0 && m_varToType.contains(data.framekey)) {
3821 qDebug() << "RE-USING " << m_varToType.value(data.framekey);
3822 data.setType(m_varToType.value(data.framekey));
3829 void GdbEngine::insertData(const WatchData &data0)
3831 //qDebug() << "INSERT DATA" << data0.toString();
3832 WatchData data = data0;
3833 if (data.value.startsWith("mi_cmd_var_create:")) {
3834 qDebug() << "BOGUS VALUE: " << data.toString();
3837 qq->watchHandler()->insertData(data);
3840 void GdbEngine::handleTypeContents(const QString &output)
3842 // output.startsWith("type = ") == true
3844 // "type = class QString {"
3845 // "type = class QStringList : public QList<QString> {"
3848 if (output.startsWith("type = class")) {
3849 int posBrace = output.indexOf('{');
3850 QString head = output.mid(13, posBrace - 13 - 1);
3851 int posColon = head.indexOf(": public");
3853 posColon = head.indexOf(": protected");
3855 posColon = head.indexOf(": private");
3856 if (posColon == -1) {
3858 tip = "class " + className + " { ... }";
3860 className = head.left(posColon - 1);
3861 tip = "class " + head + " { ... }";
3863 //qDebug() << "posColon: " << posColon;
3864 //qDebug() << "posBrace: " << posBrace;
3865 //qDebug() << "head: " << head;
3867 className = output.mid(7);
3870 //qDebug() << "output: " << output.left(100) + "...";
3871 //qDebug() << "className: " << className;
3872 //qDebug() << "tip: " << tip;
3873 //m_toolTip.type = className;
3874 m_toolTip.type.clear();
3875 m_toolTip.value = tip;
3878 void GdbEngine::handleVarListChildrenHelper(const GdbMi &item,
3879 const WatchData &parent)
3881 //qDebug() << "VAR_LIST_CHILDREN: PARENT 2" << parent.toString();
3882 //qDebug() << "VAR_LIST_CHILDREN: APPENDEE " << data.toString();
3883 QByteArray exp = item.findChild("exp").data();
3884 QByteArray name = item.findChild("name").data();
3885 if (isAccessSpecifier(exp)) {
3886 // suppress 'private'/'protected'/'public' level
3888 data.variable = name;
3889 data.iname = parent.iname;
3890 //data.iname = data.variable;
3891 data.exp = parent.exp;
3892 data.setTypeUnneeded();
3893 data.setValueUnneeded();
3894 data.setChildCountUnneeded();
3895 data.setChildrenUnneeded();
3896 //qDebug() << "DATA" << data.toString();
3897 QString cmd = "-var-list-children --all-values \"" + data.variable + "\"";
3898 //iname += '.' + exp;
3899 sendSynchronizedCommand(cmd, WatchVarListChildren, QVariant::fromValue(data));
3900 } else if (item.findChild("numchild").data() == "0") {
3901 // happens for structs without data, e.g. interfaces.
3903 data.iname = parent.iname + '.' + exp;
3905 data.variable = name;
3906 setWatchDataType(data, item.findChild("type"));
3907 setWatchDataValue(data, item.findChild("value"));
3908 setWatchDataAddress(data, item.findChild("addr"));
3909 data.setChildCount(0);
3911 } else if (parent.iname.endsWith('.')) {
3912 // Happens with anonymous unions
3915 QString cmd = "-var-list-children --all-values \"" + data.variable + "\"";
3916 sendSynchronizedCommand(cmd, WatchVarListChildren, QVariant::fromValue(data));
3917 } else if (exp == "staticMetaObject") {
3918 // && item.findChild("type").data() == "const QMetaObject")
3919 // FIXME: Namespaces?
3920 // { do nothing } FIXME: make coinfigurable?
3921 // special "clever" hack to avoid clutter in the GUI.
3922 // I am not sure this is a good idea...
3925 data.iname = parent.iname + '.' + exp;
3926 data.variable = name;
3927 setWatchDataType(data, item.findChild("type"));
3928 setWatchDataValue(data, item.findChild("value"));
3929 setWatchDataAddress(data, item.findChild("addr"));
3930 setWatchDataChildCount(data, item.findChild("numchild"));
3931 if (!qq->watchHandler()->isExpandedIName(data.iname))
3932 data.setChildrenUnneeded();
3935 if (isPointerType(parent.type) && data.type == exp) {
3936 data.exp = "*(" + parent.exp + ")";
3937 data.name = "*" + parent.name;
3938 } else if (data.type == exp) {
3939 // A type we derive from? gdb crashes when creating variables here
3940 data.exp = parent.exp;
3941 } else if (exp.startsWith("*")) {
3943 data.exp = "*(" + parent.exp + ")";
3944 } else if (startsWithDigit(exp)) {
3945 // An array. No variables needed?
3946 data.name = "[" + data.name + "]";
3947 data.exp = parent.exp + "[" + exp + "]";
3948 } else if (0 && parent.name.endsWith('.')) {
3949 // Happens with anonymous unions
3950 data.exp = parent.exp + exp;
3951 //data.name = "<anonymous union>";
3952 } else if (exp.isEmpty()) {
3953 // Happens with anonymous unions
3954 data.exp = parent.exp;
3955 data.name = "<n/a>";
3956 data.iname = parent.iname + ".@";
3957 data.type = "<anonymous union>";
3959 // A structure. Hope there's nothing else...
3960 data.exp = parent.exp + '.' + exp;
3963 if (isCustomValueDumperAvailable(data.type)) {
3964 // we do not trust gdb if we have a custom dumper
3965 data.setValueNeeded();
3966 data.setChildCountNeeded();
3969 //qDebug() << "VAR_LIST_CHILDREN: PARENT 3" << parent.toString();
3970 //qDebug() << "VAR_LIST_CHILDREN: APPENDEE " << data.toString();
3975 void GdbEngine::handleVarListChildren(const GdbResultRecord &record,
3976 const WatchData &data0)
3978 //WatchResultCounter dummy(this, WatchVarListChildren);
3979 WatchData data = data0;
3980 if (!data.isValid())
3982 if (record.resultClass == GdbResultDone) {
3983 //qDebug() << "VAR_LIST_CHILDREN: PARENT " << data.toString();
3984 GdbMi children = record.data.findChild("children");
3986 foreach (const GdbMi &child, children.children())
3987 handleVarListChildrenHelper(child, data);
3989 if (!isAccessSpecifier(data.variable.split('.').takeLast())) {
3990 data.setChildrenUnneeded();
3993 } else if (record.resultClass == GdbResultError) {
3994 data.setError(record.data.findChild("msg").data());
3996 data.setError("Unknown error: " + record.toString());
4000 void GdbEngine::handleToolTip(const GdbResultRecord &record,
4001 const QString &what)
4003 //qDebug() << "HANDLE TOOLTIP: " << what << m_toolTip.toString();
4004 // << "record: " << record.toString();
4005 if (record.resultClass == GdbResultError) {
4006 QString msg = record.data.findChild("msg").data();
4007 if (what == "create") {
4008 sendCommand("ptype " + m_toolTip.exp, WatchToolTip, "ptype");
4011 if (what == "evaluate") {
4012 if (msg.startsWith("Cannot look up value of a typedef")) {
4013 m_toolTip.value = m_toolTip.exp + " is a typedef.";
4017 } else if (record.resultClass == GdbResultDone) {
4018 if (what == "create") {
4019 setWatchDataType(m_toolTip, record.data.findChild("type"));
4020 setWatchDataChildCount(m_toolTip, record.data.findChild("numchild"));
4021 if (isCustomValueDumperAvailable(m_toolTip.type))
4022 runCustomDumper(m_toolTip, false);
4024 q->showStatusMessage(tr("Retrieving data for tooltip..."), 10000);
4025 sendCommand("-data-evaluate-expression " + m_toolTip.exp,
4026 WatchToolTip, "evaluate");
4027 //sendToolTipCommand("-var-evaluate-expression tooltip")
4030 if (what == "evaluate") {
4031 m_toolTip.value = m_toolTip.type + ' ' + m_toolTip.exp
4032 + " = " + record.data.findChild("value").data();
4035 if (what == "ptype") {
4036 GdbMi mi = record.data.findChild("consolestreamoutput");
4037 m_toolTip.value = extractTypeFromPTypeOutput(mi.data());
4042 m_toolTip.iname = tooltipIName;
4043 m_toolTip.setChildrenUnneeded();
4044 m_toolTip.setChildCountUnneeded();
4045 insertData(m_toolTip);
4046 qDebug() << "DATA INSERTED";
4047 QTimer::singleShot(0, this, SLOT(updateWatchModel2()));
4048 qDebug() << "HANDLE TOOLTIP END";
4052 void GdbEngine::handleChangedItem(QStandardItem *item)
4054 // HACK: Just store the item for the slot
4055 // handleChangedItem(QWidget *widget) below.
4056 QModelIndex index = item->index().sibling(item->index().row(), 0);
4057 //WatchData data = m_currentSet.takeData(iname);
4058 //m_editedData = inameFromItem(m_model.itemFromIndex(index)).exp;
4059 //qDebug() << "HANDLE CHANGED EXPRESSION: " << m_editedData;
4063 void GdbEngine::assignValueInDebugger(const QString &expression, const QString &value)
4065 sendCommand("-var-delete assign");
4066 sendCommand("-var-create assign * " + expression);
4067 sendCommand("-var-assign assign " + value, WatchVarAssign);
4070 void GdbEngine::tryLoadCustomDumpers()
4072 if (m_dataDumperState != DataDumperUninitialized)
4075 PENDING_DEBUG("TRY LOAD CUSTOM DUMPERS");
4076 m_dataDumperState = DataDumperUnavailable;
4078 #if defined(Q_OS_LINUX)
4079 QString lib = q->m_buildDir + "/qtc-gdbmacros/libgdbmacros.so";
4080 if (QFileInfo(lib).exists()) {
4081 m_dataDumperState = DataDumperLoadTried;
4082 //sendCommand("p dlopen");
4083 QString flag = QString::number(RTLD_NOW);
4084 sendCommand("sharedlibrary libc"); // for malloc
4085 sendCommand("sharedlibrary libdl"); // for dlopen
4086 sendCommand("call (void)dlopen(\"" + lib + "\", " + flag + ")",
4087 WatchDumpCustomSetup);
4088 // some older systems like CentOS 4.6 prefer this:
4089 sendCommand("call (void)__dlopen(\"" + lib + "\", " + flag + ")",
4090 WatchDumpCustomSetup);
4091 sendCommand("sharedlibrary " + dotEscape(lib));
4094 #if defined(Q_OS_MAC)
4095 QString lib = q->m_buildDir + "/qtc-gdbmacros/libgdbmacros.dylib";
4096 if (QFileInfo(lib).exists()) {
4097 m_dataDumperState = DataDumperLoadTried;
4098 //sendCommand("sharedlibrary libc"); // for malloc
4099 //sendCommand("sharedlibrary libdl"); // for dlopen
4100 QString flag = QString::number(RTLD_NOW);
4101 sendCommand("call (void)dlopen(\"" + lib + "\", " + flag + ")",
4102 WatchDumpCustomSetup);
4103 //sendCommand("sharedlibrary " + dotEscape(lib));
4106 #if defined(Q_OS_WIN)
4107 QString lib = q->m_buildDir + "/qtc-gdbmacros/debug/gdbmacros.dll";
4108 if (QFileInfo(lib).exists()) {
4109 m_dataDumperState = DataDumperLoadTried;
4110 sendCommand("sharedlibrary .*"); // for LoadLibraryA
4111 //sendCommand("handle SIGSEGV pass stop print");
4112 //sendCommand("set unwindonsignal off");
4113 sendCommand("call LoadLibraryA(\"" + lib + "\")",
4114 WatchDumpCustomSetup);
4115 sendCommand("sharedlibrary " + dotEscape(lib));
4119 if (m_dataDumperState == DataDumperLoadTried) {
4120 // retreive list of dumpable classes
4121 sendCommand("call qDumpObjectData440(1,%1+1,0,0,0,0,0,0)",
4122 GdbQueryDataDumper1);
4123 sendCommand("p (char*)qDumpOutBuffer", GdbQueryDataDumper2);
4125 gdbOutputAvailable("", QString("DEBUG HELPER LIBRARY IS NOT USABLE: "
4126 " %1 EXISTS: %2, EXECUTABLE: %3").arg(lib)
4127 .arg(QFileInfo(lib).exists())
4128 .arg(QFileInfo(lib).isExecutable()));
4133 IDebuggerEngine *createGdbEngine(DebuggerManager *parent)
4135 return new GdbEngine(parent);