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"))
238 ///////////////////////////////////////////////////////////////////////
242 ///////////////////////////////////////////////////////////////////////
244 GdbEngine::GdbEngine(DebuggerManager *parent)
247 qq = parent->engineInterface();
248 initializeVariables();
249 initializeConnections();
252 GdbEngine::~GdbEngine()
254 // prevent sending error messages afterwards
255 m_gdbProc.disconnect(this);
258 void GdbEngine::initializeConnections()
260 // Gdb Process interaction
261 connect(&m_gdbProc, SIGNAL(error(QProcess::ProcessError)), this,
262 SLOT(gdbProcError(QProcess::ProcessError)));
263 connect(&m_gdbProc, SIGNAL(readyReadStandardOutput()), this,
264 SLOT(readGdbStandardOutput()));
265 connect(&m_gdbProc, SIGNAL(readyReadStandardError()), this,
266 SLOT(readGdbStandardError()));
267 connect(&m_gdbProc, SIGNAL(finished(int, QProcess::ExitStatus)), q,
268 SLOT(exitDebugger()));
271 connect(&m_outputCollector, SIGNAL(byteDelivery(QByteArray)),
272 SLOT(readDebugeeOutput(QByteArray)));
274 connect(this, SIGNAL(gdbOutputAvailable(QString,QString)),
275 q, SLOT(showDebuggerOutput(QString,QString)),
276 Qt::QueuedConnection);
277 connect(this, SIGNAL(gdbInputAvailable(QString,QString)),
278 q, SLOT(showDebuggerInput(QString,QString)),
279 Qt::QueuedConnection);
280 connect(this, SIGNAL(applicationOutputAvailable(QString)),
281 q, SLOT(showApplicationOutput(QString)),
282 Qt::QueuedConnection);
285 void GdbEngine::initializeVariables()
287 m_dataDumperState = DataDumperUninitialized;
289 m_gdbBuildVersion = -1;
291 m_fullToShortName.clear();
292 m_shortToFullName.clear();
295 m_modulesListOutdated = true;
296 m_oldestAcceptableToken = -1;
297 m_outputCodec = QTextCodec::codecForLocale();
298 m_pendingRequests = 0;
299 m_waitingForBreakpointSynchronizationToContinue = false;
300 m_waitingForFirstBreakpointToBeHit = false;
301 m_commandsToRunOnTemporaryBreak.clear();
304 void GdbEngine::gdbProcError(QProcess::ProcessError error)
308 case QProcess::FailedToStart:
309 msg = QString(tr("The Gdb process failed to start. Either the "
310 "invoked program '%1' is missing, or you may have insufficient "
311 "permissions to invoke the program.")).arg(q->settings()->m_gdbCmd);
313 case QProcess::Crashed:
314 msg = tr("The Gdb process crashed some time after starting "
317 case QProcess::Timedout:
318 msg = tr("The last waitFor...() function timed out. "
319 "The state of QProcess is unchanged, and you can try calling "
320 "waitFor...() again.");
322 case QProcess::WriteError:
323 msg = tr("An error occurred when attempting to write "
324 "to the Gdb process. For example, the process may not be running, "
325 "or it may have closed its input channel.");
327 case QProcess::ReadError:
328 msg = tr("An error occurred when attempting to read from "
329 "the Gdb process. For example, the process may not be running.");
332 msg = tr("An unknown error in the Gdb process occurred. "
333 "This is the default return value of error().");
336 q->showStatusMessage(msg);
337 QMessageBox::critical(q->mainWindow(), tr("Error"), msg);
338 // act as if it was closed by the core
342 static inline bool isNameChar(char c)
344 // could be 'stopped' or 'shlibs-added'
345 return (c >= 'a' && c <= 'z') || c == '-';
349 static void dump(const char *first, const char *middle, const QString & to)
351 QByteArray ba(first, middle - first);
353 // note that qDebug cuts off output after a certain size... (bug?)
354 qDebug("\n>>>>> %s\n%s\n====\n%s\n<<<<<\n",
355 qPrintable(currentTime()),
356 qPrintable(QString(ba).trimmed()),
357 qPrintable(to.trimmed()));
359 //qDebug() << qPrintable(currentTime())
360 // << " Reading response: " << QString(ba).trimmed() << "\n";
364 void GdbEngine::readDebugeeOutput(const QByteArray &data)
366 emit applicationOutputAvailable(m_outputCodec->toUnicode(
367 data.constData(), data.length(), &m_outputCodecState));
370 void GdbEngine::debugMessage(const QString &msg)
372 emit gdbOutputAvailable("debug:", msg);
375 void GdbEngine::handleResponse(const QByteArray &buff)
377 static QTime lastTime;
379 emit gdbOutputAvailable(" ", currentTime());
380 emit gdbOutputAvailable("stdout:", buff);
383 qDebug() // << "#### start response handling #### "
385 << lastTime.msecsTo(QTime::currentTime()) << "ms,"
386 << "buf: " << buff.left(1500) << "..."
388 << "size:" << buff.size();
390 //qDebug() << "buf: " << buff;
393 lastTime = QTime::currentTime();
395 if (buff.isEmpty() || buff == "(gdb) ")
398 const char *from = buff.constData();
399 const char *to = from + buff.size();
403 // token is a sequence of numbers
404 for (inner = from; inner != to; ++inner)
405 if (*inner < '0' || *inner > '9')
408 token = QByteArray(from, inner - from).toInt();
410 //qDebug() << "found token " << token;
413 // next char decides kind of record
414 const char c = *from++;
415 //qDebug() << "CODE:" << c;
420 QByteArray asyncClass;
421 for (; from != to; ++from) {
422 const char c = *from;
427 //qDebug() << "ASYNCCLASS" << asyncClass;
432 qDebug() << "MALFORMED ASYNC OUTPUT" << from;
437 data.parseResultOrValue(from, to);
438 if (data.isValid()) {
439 //qDebug() << "parsed response: " << data.toString();
440 record.m_children += data;
441 record.m_type = GdbMi::Tuple;
444 if (asyncClass == "stopped") {
445 handleAsyncOutput(record);
446 } else if (asyncClass == "running") {
447 // Archer has 'thread-id="all"' here
449 } else if (asyncClass == "shlibs-updated") {
450 // MAC announces updated libs
451 } else if (asyncClass == "shlibs-added") {
452 // MAC announces added libs
453 // {shlib-info={num="2", name="libmathCommon.A_debug.dylib",
454 // kind="-", dyld-addr="0x7f000", reason="dyld", requested-state="Y",
455 // state="Y", path="/usr/lib/system/libmathCommon.A_debug.dylib",
456 // description="/usr/lib/system/libmathCommon.A_debug.dylib",
457 // loaded_addr="0x7f000", slide="0x7f000", prefix=""}}
460 qDebug() << "IGNORED ASYNC OUTPUT "
461 << asyncClass << record.toString();
467 m_pendingConsoleStreamOutput += GdbMi::parseCString(from, to);
472 m_pendingTargetStreamOutput += GdbMi::parseCString(from, to);
477 QByteArray data = GdbMi::parseCString(from, to);
478 m_pendingLogStreamOutput += data;
479 // On Windows, the contents seem to depend on the debugger
480 // version and/or OS version used.
481 if (data.startsWith("warning:"))
482 qq->showApplicationOutput(data);
487 GdbResultRecord record;
489 record.token = token;
491 for (inner = from; inner != to; ++inner)
492 if (*inner < 'a' || *inner > 'z')
495 QByteArray resultClass(from, inner - from);
497 if (resultClass == "done")
498 record.resultClass = GdbResultDone;
499 else if (resultClass == "running")
500 record.resultClass = GdbResultRunning;
501 else if (resultClass == "connected")
502 record.resultClass = GdbResultConnected;
503 else if (resultClass == "error")
504 record.resultClass = GdbResultError;
505 else if (resultClass == "exit")
506 record.resultClass = GdbResultExit;
508 record.resultClass = GdbResultUnknown;
513 qDebug() << "MALFORMED RESULT OUTPUT" << from;
517 record.data.parseTuple_helper(from, to);
518 record.data.m_type = GdbMi::Tuple;
519 record.data.m_name = "data";
522 //qDebug() << "\nLOG STREAM:" + m_pendingLogStreamOutput;
523 //qDebug() << "\nTARGET STREAM:" + m_pendingTargetStreamOutput;
524 //qDebug() << "\nCONSOLE STREAM:" + m_pendingConsoleStreamOutput;
525 record.data.setStreamOutput("logstreamoutput",
526 m_pendingLogStreamOutput);
527 record.data.setStreamOutput("targetstreamoutput",
528 m_pendingTargetStreamOutput);
529 record.data.setStreamOutput("consolestreamoutput",
530 m_pendingConsoleStreamOutput);
531 QByteArray custom = m_customOutputForToken[token];
532 if (!custom.isEmpty())
533 record.data.setStreamOutput("customvaluecontents",
535 //m_customOutputForToken.remove(token);
536 m_pendingLogStreamOutput.clear();
537 m_pendingTargetStreamOutput.clear();
538 m_pendingConsoleStreamOutput.clear();
540 handleResultRecord(record);
544 qDebug() << "UNKNOWN RESPONSE TYPE" << c;
550 void GdbEngine::readGdbStandardError()
552 qWarning() << "Unexpected gdb stderr:" << m_gdbProc.readAllStandardError();
555 void GdbEngine::readGdbStandardOutput()
557 m_inbuffer.append(m_gdbProc.readAllStandardOutput());
560 while (newstart < m_inbuffer.size()) {
561 int start = newstart;
562 int end = m_inbuffer.indexOf('\n', start);
564 m_inbuffer.remove(0, start);
570 if (m_inbuffer.at(end - 1) == '\r') {
575 handleResponse(QByteArray::fromRawData(m_inbuffer.constData() + start, end - start));
580 void GdbEngine::interruptInferior()
582 qq->notifyInferiorStopRequested();
583 if (m_gdbProc.state() == QProcess::NotRunning) {
584 debugMessage("TRYING TO INTERRUPT INFERIOR WITHOUT RUNNING GDB");
585 qq->notifyInferiorExited();
589 if (q->m_attachedPID > 0) {
590 if (!interruptProcess(q->m_attachedPID))
591 // qq->notifyInferiorStopped();
593 debugMessage(QString("CANNOT INTERRUPT %1").arg(q->m_attachedPID));
598 sendCommand("-exec-interrupt", GdbExecInterrupt);
599 //qq->notifyInferiorStopped();
601 if (!interruptChildProcess(m_gdbProc.pid()))
602 // qq->notifyInferiorStopped();
604 debugMessage(QString("CANNOT STOP INFERIOR"));
608 void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
610 int pid = pid0.toInt();
612 debugMessage(QString("Cannot parse PID from %1").arg(pid0));
615 if (pid == q->m_attachedPID)
617 debugMessage(QString("FOUND PID %1").arg(pid));
618 q->m_attachedPID = pid;
619 qq->notifyInferiorPidChanged(pid);
622 void GdbEngine::sendSynchronizedCommand(const QString & command,
623 int type, const QVariant &cookie, StopNeeded needStop)
625 sendCommand(command, type, cookie, needStop, Synchronized);
628 void GdbEngine::sendCommand(const QString &command, int type,
629 const QVariant &cookie, StopNeeded needStop, Synchronization synchronized)
631 if (m_gdbProc.state() == QProcess::NotRunning) {
632 debugMessage("NO GDB PROCESS RUNNING, CMD IGNORED: " + command);
638 PENDING_DEBUG(" TYPE " << type << " INCREMENTS PENDING TO: "
639 << m_pendingRequests << command);
641 PENDING_DEBUG(" UNKNOWN TYPE " << type << " LEAVES PENDING AT: "
642 << m_pendingRequests << command);
646 cmd.synchronized = synchronized;
647 cmd.command = command;
651 if (needStop && q->status() != DebuggerInferiorStopped
652 && q->status() != DebuggerProcessStartingUp) {
653 // queue the commands that we cannot send at once
654 QTC_ASSERT(q->status() == DebuggerInferiorRunning,
655 qDebug() << "STATUS: " << q->status());
656 q->showStatusMessage(tr("Stopping temporarily."));
657 debugMessage("QUEUING COMMAND " + cmd.command);
658 m_commandsToRunOnTemporaryBreak.append(cmd);
660 } else if (!command.isEmpty()) {
662 m_cookieForToken[currentToken()] = cmd;
663 cmd.command = QString::number(currentToken()) + cmd.command;
664 if (cmd.command.contains("%1"))
665 cmd.command = cmd.command.arg(currentToken());
667 m_gdbProc.write(cmd.command.toLatin1() + "\r\n");
668 //emit gdbInputAvailable(QString(), " " + currentTime());
669 //emit gdbInputAvailable(QString(), "[" + currentTime() + "] " + cmd.command);
670 emit gdbInputAvailable(QString(), cmd.command);
674 void GdbEngine::handleResultRecord(const GdbResultRecord &record)
676 //qDebug() << "TOKEN: " << record.token
677 // << " ACCEPTABLE: " << m_oldestAcceptableToken;
679 //qDebug() << "\nRESULT" << record.token << record.toString();
681 int token = record.token;
685 GdbCookie cmd = m_cookieForToken.take(token);
687 if (record.token < m_oldestAcceptableToken) {
688 //qDebug() << "### SKIPPING OLD RESULT " << record.toString();
689 //QMessageBox::information(m_mainWindow, tr("Skipped"), "xxx");
694 qDebug() << "# handleOutput, "
695 << "cmd type: " << cmd.type
696 << "cmd synchronized: " << cmd.synchronized
697 << "\n record: " << record.toString();
700 // << "\n data: " << record.data.toString(true);
702 if (cmd.type != GdbInvalidCommand)
703 handleResult(record, cmd.type, cmd.cookie);
705 if (cmd.synchronized) {
707 PENDING_DEBUG(" TYPE " << cmd.type << " DECREMENTS PENDING TO: "
708 << m_pendingRequests << cmd.command);
709 if (m_pendingRequests <= 0) {
710 PENDING_DEBUG(" .... AND TRIGGERS MODEL UPDATE");
714 PENDING_DEBUG(" UNKNOWN TYPE " << cmd.type << " LEAVES PENDING AT: "
715 << m_pendingRequests << cmd.command);
719 void GdbEngine::handleResult(const GdbResultRecord & record, int type,
720 const QVariant & cookie)
727 case GdbExecContinue:
730 handleExecRun(record);
740 handleInfoProc(record);
743 handleInfoThreads(record);
747 handleShowVersion(record);
749 case GdbFileExecAndSymbols:
750 handleFileExecAndSymbols(record);
752 case GdbExecRunToFunction:
753 // that should be "^running". We need to handle the resulting
755 //handleExecRunToFunction(record);
757 case GdbExecInterrupt:
758 qq->notifyInferiorStopped();
760 case GdbExecJumpToLine:
761 handleExecJumpToLine(record);
764 handleQueryPwd(record);
766 case GdbQuerySources:
767 handleQuerySources(record);
769 case GdbAsyncOutput2:
770 handleAsyncOutput2(cookie.value<GdbMi>());
773 handleInfoShared(record);
775 case GdbQueryDataDumper1:
776 handleQueryDataDumper1(record);
778 case GdbQueryDataDumper2:
779 handleQueryDataDumper2(record);
781 case GdbTemporaryContinue:
783 q->showStatusMessage(tr("Continuing after temporary stop."));
787 handleBreakList(record);
790 handleBreakInsert(record, cookie.toInt());
793 handleBreakInsert1(record, cookie.toInt());
796 handleBreakInfo(record, cookie.toInt());
798 case BreakEnablePending:
803 handleBreakIgnore(record, cookie.toInt());
806 handleBreakCondition(record, cookie.toInt());
809 case DisassemblerList:
810 handleDisassemblerList(record, cookie.toString());
814 handleModulesList(record);
817 case RegisterListNames:
818 handleRegisterListNames(record);
820 case RegisterListValues:
821 handleRegisterListValues(record);
824 case StackListFrames:
825 handleStackListFrames(record);
827 case StackListThreads:
828 handleStackListThreads(record, cookie.toInt());
830 case StackSelectThread:
831 handleStackSelectThread(record, cookie.toInt());
833 case StackListLocals:
834 handleStackListLocals(record);
836 case StackListArguments:
837 handleStackListArguments(record);
840 case WatchVarListChildren:
841 handleVarListChildren(record, cookie.value<WatchData>());
844 handleVarCreate(record, cookie.value<WatchData>());
849 case WatchEvaluateExpression:
850 handleEvaluateExpression(record, cookie.value<WatchData>());
853 handleToolTip(record, cookie.toString());
855 case WatchDumpCustomValue1:
856 handleDumpCustomValue1(record, cookie.value<WatchData>());
858 case WatchDumpCustomValue2:
859 handleDumpCustomValue2(record, cookie.value<WatchData>());
861 case WatchDumpCustomSetup:
862 handleDumpCustomSetup(record);
866 debugMessage(QString("FIXME: GdbEngine::handleResult: "
867 "should not happen %1").arg(type));
872 void GdbEngine::executeDebuggerCommand(const QString &command)
874 //createGdbProcessIfNeeded();
875 if (m_gdbProc.state() == QProcess::NotRunning) {
876 debugMessage("NO GDB PROCESS RUNNING, PLAIN CMD IGNORED: " + command);
881 cmd.command = command;
884 emit gdbInputAvailable(QString(), cmd.command);
885 m_gdbProc.write(cmd.command.toLatin1() + "\r\n");
888 void GdbEngine::handleQueryPwd(const GdbResultRecord &record)
890 // FIXME: remove this special case as soon as 'pwd'
891 // is supported by MI
892 //qDebug() << "PWD OUTPUT:" << record.toString();
893 // Gdb responses _unless_ we get an error first.
894 if (record.resultClass == GdbResultDone) {
896 // "5^done,{logstreamoutput="pwd ",consolestreamoutput
897 // ="Working directory /home/apoenitz/dev/work/test1. "}
898 m_pwd = record.data.findChild("consolestreamoutput").data();
899 int pos = m_pwd.indexOf("Working directory");
900 m_pwd = m_pwd.mid(pos + 18);
901 m_pwd = m_pwd.trimmed();
902 if (m_pwd.endsWith('.'))
906 // ~"Working directory C:\\Users\\Thomas\\Documents\\WBTest3\\debug.\n"
907 m_pwd = record.data.findChild("consolestreamoutput").data();
908 m_pwd = m_pwd.trimmed();
910 debugMessage("PWD RESULT: " + m_pwd);
914 void GdbEngine::handleQuerySources(const GdbResultRecord &record)
916 if (record.resultClass == GdbResultDone) {
917 QMap<QString, QString> oldShortToFull = m_shortToFullName;
918 m_shortToFullName.clear();
919 m_fullToShortName.clear();
920 // "^done,files=[{file="../../../../bin/gdbmacros/gdbmacros.cpp",
921 // fullname="/data5/dev/ide/main/bin/gdbmacros/gdbmacros.cpp"},
922 GdbMi files = record.data.findChild("files");
923 foreach (const GdbMi &item, files.children()) {
924 QString fileName = item.findChild("file").data();
925 GdbMi fullName = item.findChild("fullname");
926 QString full = fullName.data();
928 full = QDir::cleanPath(full);
930 if (fullName.isValid() && QFileInfo(full).isReadable()) {
931 //qDebug() << "STORING 2: " << fileName << full;
932 m_shortToFullName[fileName] = full;
933 m_fullToShortName[full] = fileName;
936 if (m_shortToFullName != oldShortToFull)
937 qq->sourceFileWindow()->setSourceFiles(m_shortToFullName);
941 void GdbEngine::handleInfoThreads(const GdbResultRecord &record)
943 if (record.resultClass == GdbResultDone) {
944 // FIXME: use something more robust
945 // WIN: * 3 Thread 2312.0x4d0 0x7c91120f in ?? ()
946 // LINUX: * 1 Thread 0x7f466273c6f0 (LWP 21455) 0x0000000000404542 in ...
947 QRegExp re(QLatin1String("Thread (\\d+)\\.0x.* in"));
948 QString data = record.data.findChild("consolestreamoutput").data();
949 if (re.indexIn(data) != -1)
950 maybeHandleInferiorPidChanged(re.cap(1));
954 void GdbEngine::handleInfoProc(const GdbResultRecord &record)
956 if (record.resultClass == GdbResultDone) {
957 #if defined(Q_OS_MAC)
958 //^done,process-id="85075"
959 maybeHandleInferiorPidChanged(record.data.findChild("process-id").data());
962 #if defined(Q_OS_LINUX) || defined(Q_OS_WIN)
963 // FIXME: use something more robust
964 QRegExp re(QLatin1String("process (\\d+)"));
965 QString data = record.data.findChild("consolestreamoutput").data();
966 if (re.indexIn(data) != -1)
967 maybeHandleInferiorPidChanged(re.cap(1));
972 void GdbEngine::handleInfoShared(const GdbResultRecord &record)
974 if (record.resultClass == GdbResultDone) {
975 // let the modules handler do the parsing
976 handleModulesList(record);
980 void GdbEngine::handleExecJumpToLine(const GdbResultRecord &record)
982 // FIXME: remove this special case as soon as 'jump'
983 // is supported by MI
984 // "&"jump /home/apoenitz/dev/work/test1/test1.cpp:242"
985 // ~"Continuing at 0x4058f3."
986 // ~"run1 (argc=1, argv=0x7fffb213a478) at test1.cpp:242"
989 qq->notifyInferiorStopped();
990 q->showStatusMessage(tr("Jumped. Stopped."));
991 QString output = record.data.findChild("logstreamoutput").data();
992 if (!output.isEmpty())
994 QString fileAndLine = output.section(' ', 1, 1);
995 QString file = fileAndLine.section(':', 0, 0);
996 int line = fileAndLine.section(':', 1, 1).toInt();
997 q->gotoLocation(file, line, true);
1000 void GdbEngine::handleExecRunToFunction(const GdbResultRecord &record)
1002 // FIXME: remove this special case as soon as there's a real
1003 // reason given when the temporary breakpoint is hit.
1004 // reight now we get:
1005 // 14*stopped,thread-id="1",frame={addr="0x0000000000403ce4",
1006 // func="foo",args=[{name="str",value="@0x7fff0f450460"}],
1007 // file="main.cpp",fullname="/tmp/g/main.cpp",line="37"}
1008 qq->notifyInferiorStopped();
1009 q->showStatusMessage(tr("Run to Function finished. Stopped."));
1010 GdbMi frame = record.data.findChild("frame");
1011 QString file = frame.findChild("fullname").data();
1012 int line = frame.findChild("line").data().toInt();
1013 qDebug() << "HIT: " << file << line << " IN " << frame.toString()
1014 << " -- " << record.toString();
1015 q->gotoLocation(file, line, true);
1018 static bool isExitedReason(const QString &reason)
1020 return reason == QLatin1String("exited-normally") // inferior exited normally
1021 || reason == QLatin1String("exited-signalled") // inferior exited because of a signal
1022 //|| reason == QLatin1String("signal-received") // inferior received signal
1023 || reason == QLatin1String("exited"); // inferior exited
1026 static bool isStoppedReason(const QString &reason)
1028 return reason == QLatin1String("function-finished") // -exec-finish
1029 || reason == QLatin1String("signal-received") // handled as "isExitedReason"
1030 || reason == QLatin1String("breakpoint-hit") // -exec-continue
1031 || reason == QLatin1String("end-stepping-range") // -exec-next, -exec-step
1032 || reason == QLatin1String("location-reached") // -exec-until
1033 || reason == QLatin1String("access-watchpoint-trigger")
1034 || reason == QLatin1String("read-watchpoint-trigger")
1041 void GdbEngine::handleAqcuiredInferior()
1043 #if defined(Q_OS_WIN)
1044 sendCommand("info thread", GdbInfoThreads);
1046 #if defined(Q_OS_LINUX)
1047 sendCommand("info proc", GdbInfoProc);
1049 #if defined(Q_OS_MAC)
1050 sendCommand("info pid", GdbInfoProc, QVariant(), NeedsStop);
1052 reloadSourceFiles();
1053 tryLoadCustomDumpers();
1056 // intentionally after tryLoadCustomDumpers(),
1057 // otherwise we'd interupt solib loading.
1058 if (qq->wantsAllPluginBreakpoints()) {
1059 sendCommand("set auto-solib-add on");
1060 sendCommand("set stop-on-solib-events 0");
1061 sendCommand("sharedlibrary .*");
1062 } else if (qq->wantsSelectedPluginBreakpoints()) {
1063 sendCommand("set auto-solib-add on");
1064 sendCommand("set stop-on-solib-events 1");
1065 sendCommand("sharedlibrary " + qq->selectedPluginBreakpointsPattern());
1066 } else if (qq->wantsNoPluginBreakpoints()) {
1067 // should be like that already
1068 sendCommand("set auto-solib-add off");
1069 sendCommand("set stop-on-solib-events 0");
1072 // nicer to see a bit of the world we live in
1074 attemptBreakpointSynchronization();
1077 void GdbEngine::handleAsyncOutput(const GdbMi &data)
1079 const QString reason = data.findChild("reason").data();
1081 if (isExitedReason(reason)) {
1082 qq->notifyInferiorExited();
1083 QString msg = "Program exited normally";
1084 if (reason == "exited") {
1085 msg = "Program exited with exit code "
1086 + data.findChild("exit-code").toString();
1087 } else if (reason == "exited-signalled") {
1088 msg = "Program exited after receiving signal "
1089 + data.findChild("signal-name").toString();
1090 } else if (reason == "signal-received") {
1091 msg = "Program exited after receiving signal "
1092 + data.findChild("signal-name").toString();
1094 q->showStatusMessage(msg);
1095 // FIXME: shouldn't this use a statis change?
1096 debugMessage("CALLING PARENT EXITDEBUGGER");
1102 //MAC: bool isFirstStop = data.findChild("bkptno").data() == "1";
1103 //!MAC: startSymbolName == data.findChild("frame").findChild("func")
1104 if (m_waitingForFirstBreakpointToBeHit) {
1105 // If the executable dies already that early we might get something
1106 // like stdout:49*stopped,reason="exited",exit-code="0177"
1107 // This is handled now above.
1109 qq->notifyInferiorStopped();
1110 m_waitingForFirstBreakpointToBeHit = false;
1112 // this will "continue" if done
1113 m_waitingForBreakpointSynchronizationToContinue = true;
1115 // that's the "early stop"
1116 handleAqcuiredInferior();
1120 if (!m_commandsToRunOnTemporaryBreak.isEmpty()) {
1121 QTC_ASSERT(q->status() == DebuggerInferiorStopRequested,
1122 qDebug() << "STATUS: " << q->status())
1123 qq->notifyInferiorStopped();
1124 q->showStatusMessage(tr("Temporarily stopped."));
1126 foreach (const GdbCookie &cmd, m_commandsToRunOnTemporaryBreak) {
1127 debugMessage(QString("RUNNING QUEUED COMMAND %1 %2")
1128 .arg(cmd.command).arg(cmd.type));
1129 sendCommand(cmd.command, cmd.type, cmd.cookie);
1131 sendCommand("p temporaryStop", GdbTemporaryContinue);
1132 m_commandsToRunOnTemporaryBreak.clear();
1133 q->showStatusMessage(tr("Handling queued commands."));
1137 QString msg = data.findChild("consolestreamoutput").data();
1138 if (msg.contains("Stopped due to shared library event") || reason.isEmpty()) {
1139 if (qq->wantsSelectedPluginBreakpoints()) {
1140 debugMessage("SHARED LIBRARY EVENT: " + data.toString());
1141 debugMessage("PATTERN: " + qq->selectedPluginBreakpointsPattern());
1142 sendCommand("sharedlibrary " + qq->selectedPluginBreakpointsPattern());
1144 q->showStatusMessage(tr("Loading %1...").arg(QString(data.toString())));
1147 m_modulesListOutdated = true;
1151 // seen on XP after removing a breakpoint while running
1152 // stdout:945*stopped,reason="signal-received",signal-name="SIGTRAP",
1153 // signal-meaning="Trace/breakpoint trap",thread-id="2",
1154 // frame={addr="0x7c91120f",func="ntdll!DbgUiConnectToDbg",
1155 // args=[],from="C:\\WINDOWS\\system32\\ntdll.dll"}
1156 if (reason == "signal-received"
1157 && data.findChild("signal-name").toString() == "SIGTRAP") {
1162 //tryLoadCustomDumpers();
1164 // jump over well-known frames
1165 static int stepCounter = 0;
1166 if (qq->skipKnownFrames()) {
1167 if (reason == "end-stepping-range" || reason == "function-finished") {
1168 GdbMi frame = data.findChild("frame");
1169 //debugMessage(frame.toString());
1170 m_currentFrame = frame.findChild("addr").data() + '%' +
1171 frame.findChild("func").data() + '%';
1173 QString funcName = frame.findChild("func").data();
1174 QString fileName = frame.findChild("file").data();
1175 if (isLeavableFunction(funcName, fileName)) {
1176 //debugMessage("LEAVING" + funcName);
1182 if (isSkippableFunction(funcName, fileName)) {
1183 //debugMessage("SKIPPING" + funcName);
1189 // qDebug() << "STEPCOUNTER:" << stepCounter;
1194 if (isStoppedReason(reason) || reason.isEmpty()) {
1195 if (m_modulesListOutdated) {
1197 m_modulesListOutdated = false;
1199 // Need another round trip
1200 if (reason == "breakpoint-hit") {
1201 q->showStatusMessage(tr("Stopped at breakpoint"));
1202 GdbMi frame = data.findChild("frame");
1203 //debugMessage("HIT BREAKPOINT: " + frame.toString());
1204 m_currentFrame = frame.findChild("addr").data() + '%' +
1205 frame.findChild("func").data() + '%';
1207 QApplication::alert(q->mainWindow(), 3000);
1208 reloadSourceFiles();
1209 sendCommand("-break-list", BreakList);
1210 QVariant var = QVariant::fromValue<GdbMi>(data);
1211 sendCommand("p 0", GdbAsyncOutput2, var); // dummy
1213 q->showStatusMessage(tr("Stopped: \"%1\"").arg(reason));
1214 handleAsyncOutput2(data);
1219 debugMessage("STOPPED FOR UNKNOWN REASON: " + data.toString());
1220 // Ignore it. Will be handled with full response later in the
1221 // JumpToLine or RunToFunction handlers
1223 // FIXME: remove this special case as soon as there's a real
1224 // reason given when the temporary breakpoint is hit.
1225 // reight now we get:
1226 // 14*stopped,thread-id="1",frame={addr="0x0000000000403ce4",
1227 // func="foo",args=[{name="str",value="@0x7fff0f450460"}],
1228 // file="main.cpp",fullname="/tmp/g/main.cpp",line="37"}
1230 // MAC yields sometimes:
1231 // stdout:3661*stopped,time={wallclock="0.00658",user="0.00142",
1232 // system="0.00136",start="1218810678.805432",end="1218810678.812011"}
1234 qq->notifyInferiorStopped();
1235 q->showStatusMessage(tr("Run to Function finished. Stopped."));
1236 GdbMi frame = data.findChild("frame");
1237 QString file = frame.findChild("fullname").data();
1238 int line = frame.findChild("line").data().toInt();
1239 qDebug() << "HIT: " << file << line << " IN " << frame.toString()
1240 << " -- " << data.toString();
1241 q->gotoLocation(file, line, true);
1246 void GdbEngine::handleAsyncOutput2(const GdbMi &data)
1248 qq->notifyInferiorStopped();
1253 qq->stackHandler()->setCurrentIndex(0);
1254 updateLocals(); // Quick shot
1256 int currentId = data.findChild("thread-id").data().toInt();
1257 sendSynchronizedCommand("-stack-list-frames", StackListFrames);
1258 if (supportsThreads())
1259 sendSynchronizedCommand("-thread-list-ids", StackListThreads, currentId);
1265 //"79*stopped,reason="end-stepping-range",reason="breakpoint-hit",bkptno="1",
1266 //thread-id="1",frame={addr="0x0000000000405d8f",func="run1",
1267 //args=[{name="argc",value="1"},{name="argv",value="0x7fffb7c23058"}],
1268 //file="test1.cpp",fullname="/home/apoenitz/dev/work/test1/test1.cpp",line="261"}"
1269 // Mac: (but only sometimes)
1270 m_address = data.findChild("frame").findChild("addr").data();
1271 qq->reloadDisassembler();
1276 qq->reloadRegisters();
1279 void GdbEngine::handleShowVersion(const GdbResultRecord &response)
1281 //qDebug () << "VERSION 2:" << response.data.findChild("consolestreamoutput").data();
1282 //qDebug () << "VERSION:" << response.toString();
1283 debugMessage("VERSION:" + response.toString());
1284 if (response.resultClass == GdbResultDone) {
1286 m_gdbBuildVersion = -1;
1287 QString msg = response.data.findChild("consolestreamoutput").data();
1288 QRegExp supported("GNU gdb(.*) (\\d+)\\.(\\d+)(\\.(\\d+))?(-(\\d+))?");
1289 if (supported.indexIn(msg) == -1) {
1290 debugMessage("UNSUPPORTED GDB VERSION " + msg);
1291 QStringList list = msg.split("\n");
1292 while (list.size() > 2)
1294 msg = tr("The debugger you are using identifies itself as:")
1295 + "<p><p>" + list.join("<br>") + "<p><p>"
1296 + tr("This version is not officially supported by Qt Creator.\n"
1297 "Debugging will most likely not work well.\n"
1298 "Using gdb 6.7 or later is strongly recommended.");
1300 // ugly, but 'Show again' check box...
1301 static QErrorMessage *err = new QErrorMessage(m_mainWindow);
1302 err->setMinimumSize(400, 300);
1303 err->showMessage(msg);
1305 //QMessageBox::information(m_mainWindow, tr("Warning"), msg);
1308 m_gdbVersion = 10000 * supported.cap(2).toInt()
1309 + 100 * supported.cap(3).toInt()
1310 + 1 * supported.cap(5).toInt();
1311 m_gdbBuildVersion = supported.cap(7).toInt();
1312 debugMessage(QString("GDB VERSION: %1").arg(m_gdbVersion));
1314 //qDebug () << "VERSION 3:" << m_gdbVersion << m_gdbBuildVersion;
1318 void GdbEngine::handleFileExecAndSymbols
1319 (const GdbResultRecord &response)
1321 if (response.resultClass == GdbResultDone) {
1322 //m_breakHandler->clearBreakMarkers();
1323 } else if (response.resultClass == GdbResultError) {
1324 QString msg = response.data.findChild("msg").data();
1325 QMessageBox::critical(q->mainWindow(), tr("Error"),
1326 tr("Starting executable failed:\n") + msg);
1327 QTC_ASSERT(q->status() == DebuggerInferiorRunning, /**/);
1328 interruptInferior();
1332 void GdbEngine::handleExecRun(const GdbResultRecord &response)
1334 if (response.resultClass == GdbResultRunning) {
1335 qq->notifyInferiorRunning();
1336 q->showStatusMessage(tr("Running..."));
1337 } else if (response.resultClass == GdbResultError) {
1338 QString msg = response.data.findChild("msg").data();
1339 if (msg == "Cannot find bounds of current function") {
1340 qq->notifyInferiorStopped();
1341 //q->showStatusMessage(tr("No debug information available. "
1342 // "Leaving function..."));
1345 QMessageBox::critical(q->mainWindow(), tr("Error"),
1346 tr("Starting executable failed:\n") + msg);
1347 QTC_ASSERT(q->status() == DebuggerInferiorRunning, /**/);
1348 interruptInferior();
1353 void GdbEngine::queryFullName(const QString &fileName, QString *full)
1355 *full = fullName(fileName);
1358 QString GdbEngine::shortName(const QString &fullName)
1360 return m_fullToShortName.value(fullName, QString());
1363 QString GdbEngine::fullName(const QString &fileName)
1365 //QString absName = m_manager->currentWorkingDirectory() + "/" + file; ??
1366 if (fileName.isEmpty())
1368 QString full = m_shortToFullName.value(fileName, QString());
1369 //debugMessage("RESOLVING: " + fileName + " " + full);
1370 if (!full.isEmpty())
1372 QFileInfo fi(fileName);
1373 if (!fi.isReadable())
1375 full = fi.absoluteFilePath();
1377 full = QDir::cleanPath(full);
1379 //debugMessage("STORING: " + fileName + " " + full);
1380 m_shortToFullName[fileName] = full;
1381 m_fullToShortName[full] = fileName;
1385 QString GdbEngine::fullName(const QStringList &candidates)
1388 foreach (const QString &fileName, candidates) {
1389 full = fullName(fileName);
1390 if (!full.isEmpty())
1393 foreach (const QString &fileName, candidates) {
1394 if (!fileName.isEmpty())
1400 void GdbEngine::shutdown()
1405 void GdbEngine::exitDebugger()
1407 debugMessage(QString("GDBENGINE EXITDEBUFFER: %1").arg(m_gdbProc.state()));
1408 if (m_gdbProc.state() == QProcess::Starting) {
1409 debugMessage(QString("WAITING FOR GDB STARTUP TO SHUTDOWN: %1")
1410 .arg(m_gdbProc.state()));
1411 m_gdbProc.waitForStarted();
1413 if (m_gdbProc.state() == QProcess::Running) {
1414 debugMessage(QString("WAITING FOR RUNNING GDB TO SHUTDOWN: %1")
1415 .arg(m_gdbProc.state()));
1416 if (q->status() != DebuggerInferiorStopped
1417 && q->status() != DebuggerProcessStartingUp) {
1418 QTC_ASSERT(q->status() == DebuggerInferiorRunning,
1419 qDebug() << "STATUS ON EXITDEBUGGER: " << q->status());
1420 interruptInferior();
1422 if (q->startMode() == DebuggerManager::AttachExternal)
1423 sendCommand("detach");
1425 sendCommand("kill");
1426 sendCommand("-gdb-exit");
1427 // 20s can easily happen when loading webkit debug information
1428 m_gdbProc.waitForFinished(20000);
1429 if (m_gdbProc.state() != QProcess::Running) {
1430 debugMessage(QString("FORCING TERMINATION: %1")
1431 .arg(m_gdbProc.state()));
1432 m_gdbProc.terminate();
1433 m_gdbProc.waitForFinished(20000);
1436 if (m_gdbProc.state() != QProcess::NotRunning)
1437 debugMessage("PROBLEM STOPPING DEBUGGER");
1439 m_outputCollector.shutdown();
1440 initializeVariables();
1441 //q->settings()->m_debugDumpers = false;
1445 int GdbEngine::currentFrame() const
1447 return qq->stackHandler()->currentIndex();
1451 bool GdbEngine::startDebugger()
1453 QStringList gdbArgs;
1455 QFileInfo fi(q->m_executable);
1456 QString fileName = '"' + fi.absoluteFilePath() + '"';
1458 if (m_gdbProc.state() != QProcess::NotRunning) {
1459 debugMessage("GDB IS ALREADY RUNNING!");
1463 if (!m_outputCollector.listen()) {
1464 QMessageBox::critical(q->mainWindow(), tr("Debugger Startup Failure"),
1465 tr("Cannot set up communication with child process: %1")
1466 .arg(m_outputCollector.errorString()));
1470 gdbArgs.prepend(QLatin1String("--tty=") + m_outputCollector.serverName());
1472 //gdbArgs.prepend(QLatin1String("--quiet"));
1473 gdbArgs.prepend(QLatin1String("mi"));
1474 gdbArgs.prepend(QLatin1String("-i"));
1476 if (!q->m_workingDir.isEmpty())
1477 m_gdbProc.setWorkingDirectory(q->m_workingDir);
1478 if (!q->m_environment.isEmpty())
1479 m_gdbProc.setEnvironment(q->m_environment);
1482 qDebug() << "Command: " << q->settings()->m_gdbCmd;
1483 qDebug() << "WorkingDirectory: " << m_gdbProc.workingDirectory();
1484 qDebug() << "ScriptFile: " << q->settings()->m_scriptFile;
1485 qDebug() << "Environment: " << m_gdbProc.environment();
1486 qDebug() << "Arguments: " << gdbArgs;
1487 qDebug() << "BuildDir: " << q->m_buildDir;
1488 qDebug() << "ExeFile: " << q->m_executable;
1491 q->showStatusMessage(tr("Starting Debugger: ") + q->settings()->m_gdbCmd + ' ' + gdbArgs.join(" "));
1492 m_gdbProc.start(q->settings()->m_gdbCmd, gdbArgs);
1493 if (!m_gdbProc.waitForStarted()) {
1494 QMessageBox::critical(q->mainWindow(), tr("Debugger Startup Failure"),
1495 tr("Cannot start debugger: %1").arg(m_gdbProc.errorString()));
1496 m_outputCollector.shutdown();
1500 q->showStatusMessage(tr("Gdb Running"));
1502 sendCommand("show version", GdbShowVersion);
1503 //sendCommand("-enable-timings");
1504 sendCommand("set print static-members off"); // Seemingly doesn't work.
1505 //sendCommand("define hook-stop\n-thread-list-ids\n-stack-list-frames\nend");
1506 //sendCommand("define hook-stop\nprint 4\nend");
1507 //sendCommand("define hookpost-stop\nprint 5\nend");
1508 //sendCommand("define hook-call\nprint 6\nend");
1509 //sendCommand("define hookpost-call\nprint 7\nend");
1510 //sendCommand("set print object on"); // works with CLI, but not MI
1511 //sendCommand("set step-mode on"); // we can't work with that yes
1512 //sendCommand("set exec-done-display on");
1513 //sendCommand("set print pretty on");
1514 //sendCommand("set confirm off");
1515 //sendCommand("set pagination off");
1516 sendCommand("set breakpoint pending on", BreakEnablePending);
1517 sendCommand("set print elements 10000");
1518 sendCommand("-data-list-register-names", RegisterListNames);
1520 // one of the following is needed to prevent crashes in gdb on code like:
1521 // template <class T> T foo() { return T(0); }
1522 // int main() { return foo<int>(); }
1523 // (gdb) call 'int foo<int>'()
1524 // /build/buildd/gdb-6.8/gdb/valops.c:2069: internal-error:
1525 sendCommand("set overload-resolution off");
1526 //sendCommand("set demangle-style none");
1529 // Stop means reenter debugger if this signal happens (implies print).
1530 // Print means print a message if this signal happens.
1531 // Pass means let program see this signal;
1532 // otherwise program doesn't know.
1533 // Pass and Stop may be combined.
1534 // We need "print" as otherwise we would get no feedback whatsoever
1535 // Custom Dumper crashs which happen regularily for when accessing
1536 // uninitialized variables.
1537 sendCommand("handle SIGSEGV nopass stop print");
1539 // This is useful to kill the inferior whenever gdb dies.
1540 //sendCommand("handle SIGTERM pass nostop print");
1542 sendCommand("set unwindonsignal on");
1543 sendCommand("pwd", GdbQueryPwd);
1544 sendCommand("set width 0");
1545 sendCommand("set height 0");
1548 sendCommand("-gdb-set inferior-auto-start-cfm off");
1549 sendCommand("-gdb-set sharedLibrary load-rules "
1550 "dyld \".*libSystem.*\" all "
1551 "dyld \".*libauto.*\" all "
1552 "dyld \".*AppKit.*\" all "
1553 "dyld \".*PBGDBIntrospectionSupport.*\" all "
1554 "dyld \".*Foundation.*\" all "
1555 "dyld \".*CFDataFormatters.*\" all "
1556 "dyld \".*libobjc.*\" all "
1557 "dyld \".*CarbonDataFormatters.*\" all");
1560 QString scriptFileName = q->settings()->m_scriptFile;
1561 if (!scriptFileName.isEmpty()) {
1562 QFile scriptFile(scriptFileName);
1563 if (scriptFile.open(QIODevice::ReadOnly)) {
1564 sendCommand("source " + scriptFileName);
1566 QMessageBox::warning(q->mainWindow(),
1567 tr("Cannot find debugger initialization script"),
1568 tr("The debugger settings point to a script file at '%1' "
1569 "which is not accessible. If a script file is not needed, "
1570 "consider clearing that entry to avoid this warning. "
1571 ).arg(scriptFileName));
1575 if (q->startMode() == DebuggerManager::AttachExternal) {
1576 sendCommand("attach " + QString::number(q->m_attachedPID), GdbAttached);
1578 // StartInternal or StartExternal
1579 sendCommand("-file-exec-and-symbols " + fileName, GdbFileExecAndSymbols);
1580 //sendCommand("file " + fileName, GdbFileExecAndSymbols);
1582 sendCommand("sharedlibrary apply-load-rules all");
1584 if (!q->m_processArgs.isEmpty())
1585 sendCommand("-exec-arguments " + q->m_processArgs.join(" "));
1587 sendCommand("set auto-solib-add off");
1588 sendCommand("info target", GdbStart);
1590 // On MacOS, breaking in at the entry point wreaks havoc.
1591 sendCommand("tbreak main");
1592 m_waitingForFirstBreakpointToBeHit = true;
1593 qq->notifyInferiorRunningRequested();
1594 sendCommand("-exec-run");
1598 // set all to "pending"
1599 if (q->startMode() == DebuggerManager::AttachExternal)
1600 qq->breakHandler()->removeAllBreakpoints();
1602 qq->breakHandler()->setAllPending();
1607 void GdbEngine::continueInferior()
1611 qq->notifyInferiorRunningRequested();
1612 sendCommand("-exec-continue", GdbExecContinue);
1615 void GdbEngine::handleStart(const GdbResultRecord &response)
1620 if (response.resultClass == GdbResultDone) {
1621 // [some leading stdout here]
1622 // stdout:&" Entry point: 0x80831f0 0x08048134 - 0x08048147 is .interp\n"
1623 // [some trailing stdout here]
1624 QString msg = response.data.findChild("consolestreamoutput").data();
1625 QRegExp needle("\\bEntry point: (0x[0-9a-f]+)\\b");
1626 if (needle.indexIn(msg) != -1) {
1627 //debugMessage("STREAM: " + msg + " " + needle.cap(1));
1628 sendCommand("tbreak *" + needle.cap(1));
1629 m_waitingForFirstBreakpointToBeHit = true;
1630 qq->notifyInferiorRunningRequested();
1631 sendCommand("-exec-run");
1633 debugMessage("PARSING START ADDRESS FAILED: " + msg);
1635 } else if (response.resultClass == GdbResultError) {
1636 debugMessage("FETCHING START ADDRESS FAILED: " + response.toString());
1641 void GdbEngine::handleAttach()
1643 qq->notifyInferiorStopped();
1644 q->showStatusMessage(tr("Attached to running process. Stopped."));
1645 handleAqcuiredInferior();
1652 qq->stackHandler()->setCurrentIndex(0);
1653 updateLocals(); // Quick shot
1655 sendSynchronizedCommand("-stack-list-frames", StackListFrames);
1656 if (supportsThreads())
1657 sendSynchronizedCommand("-thread-list-ids", StackListThreads, 0);
1662 // XXX we have no data here ...
1663 //m_address = data.findChild("frame").findChild("addr").data();
1664 //qq->reloadDisassembler();
1669 qq->reloadRegisters();
1672 void GdbEngine::stepExec()
1675 qq->notifyInferiorRunningRequested();
1676 sendCommand("-exec-step", GdbExecStep);
1679 void GdbEngine::stepIExec()
1682 qq->notifyInferiorRunningRequested();
1683 sendCommand("-exec-step-instruction", GdbExecStepI);
1686 void GdbEngine::stepOutExec()
1689 qq->notifyInferiorRunningRequested();
1690 sendCommand("-exec-finish", GdbExecFinish);
1693 void GdbEngine::nextExec()
1696 qq->notifyInferiorRunningRequested();
1697 sendCommand("-exec-next", GdbExecNext);
1700 void GdbEngine::nextIExec()
1703 qq->notifyInferiorRunningRequested();
1704 sendCommand("-exec-next-instruction", GdbExecNextI);
1707 void GdbEngine::runToLineExec(const QString &fileName, int lineNumber)
1710 qq->notifyInferiorRunningRequested();
1711 sendCommand("-exec-until " + fileName + ":" + QString::number(lineNumber));
1714 void GdbEngine::runToFunctionExec(const QString &functionName)
1717 sendCommand("-break-insert -t " + functionName);
1718 qq->notifyInferiorRunningRequested();
1719 sendCommand("-exec-continue", GdbExecRunToFunction);
1722 void GdbEngine::jumpToLineExec(const QString &fileName, int lineNumber)
1725 // not available everywhere?
1726 //sendCliCommand("tbreak " + fileName + ":" + QString::number(lineNumber));
1727 sendCommand("-break-insert -t " + fileName + ":" + QString::number(lineNumber));
1728 sendCommand("jump " + fileName + ":" + QString::number(lineNumber));
1729 // will produce something like
1730 // &"jump /home/apoenitz/dev/work/test1/test1.cpp:242"
1731 // ~"Continuing at 0x4058f3."
1732 // ~"run1 (argc=1, argv=0x7fffbf1f5538) at test1.cpp:242"
1735 q->gotoLocation(fileName, lineNumber, true);
1737 //sendCommand("jump " + fileName + ":" + QString::number(lineNumber));
1739 q->gotoLocation(fileName, lineNumber, true);
1740 setBreakpoint(fileName, lineNumber);
1741 sendCommand("jump " + fileName + ":" + QString::number(lineNumber));
1746 \fn void GdbEngine::setTokenBarrier()
1747 \brief Discard the results of all pending watch-updating commands.
1749 This method is called at the beginning of all step/next/finish etc.
1751 If non-watch-updating commands with call-backs are still in the pipe,
1755 void GdbEngine::setTokenBarrier()
1757 foreach (const GdbCookie &ck, m_cookieForToken)
1758 QTC_ASSERT(ck.synchronized || ck.type == GdbInvalidCommand, return);
1759 emit gdbInputAvailable(QString(), "--- token barrier ---");
1760 m_oldestAcceptableToken = currentToken();
1763 void GdbEngine::setDebugDumpers(bool on)
1766 debugMessage("SWITCHING ON DUMPER DEBUGGING");
1767 sendCommand("set unwindonsignal off");
1768 q->breakByFunction("qDumpObjectData440");
1771 debugMessage("SWITCHING OFF DUMPER DEBUGGING");
1772 sendCommand("set unwindonsignal on");
1777 //////////////////////////////////////////////////////////////////////
1779 // Breakpoint specific stuff
1781 //////////////////////////////////////////////////////////////////////
1783 void GdbEngine::breakpointDataFromOutput(BreakpointData *data, const GdbMi &bkpt)
1785 if (!bkpt.isValid())
1789 data->pending = false;
1790 data->bpMultiple = false;
1791 data->bpCondition.clear();
1793 foreach (const GdbMi &child, bkpt.children()) {
1794 if (child.hasName("number")) {
1795 data->bpNumber = child.data();
1796 } else if (child.hasName("func")) {
1797 data->bpFuncName = child.data();
1798 } else if (child.hasName("addr")) {
1799 // <MULTIPLE> happens in constructors. In this case there are
1800 // _two_ fields named "addr" in the response. On Linux that is...
1801 if (child.data() == "<MULTIPLE>")
1802 data->bpMultiple = true;
1804 data->bpAddress = child.data();
1805 } else if (child.hasName("file")) {
1806 files.append(child.data());
1807 } else if (child.hasName("fullname")) {
1808 QString fullName = child.data();
1810 fullName = QDir::cleanPath(fullName);
1812 files.prepend(fullName);
1813 } else if (child.hasName("line")) {
1814 data->bpLineNumber = child.data();
1815 if (child.data().toInt())
1816 data->markerLineNumber = child.data().toInt();
1817 } else if (child.hasName("cond")) {
1818 data->bpCondition = child.data();
1819 // gdb 6.3 likes to "rewrite" conditions. Just accept that fact.
1820 if (data->bpCondition != data->condition && data->conditionsMatch())
1821 data->condition = data->bpCondition;
1823 else if (child.hasName("pending")) {
1824 data->pending = true;
1825 int pos = child.data().lastIndexOf(':');
1827 data->bpLineNumber = child.data().mid(pos + 1);
1828 data->markerLineNumber = child.data().mid(pos + 1).toInt();
1829 files.prepend(child.data().left(pos));
1831 files.prepend(child.data());
1835 // This field is not present. Contents needs to be parsed from
1836 // the plain "ignore" response.
1837 //else if (child.hasName("ignore"))
1838 // data->bpIgnoreCount = child.data();
1840 QString name = fullName(files);
1841 if (data->bpFileName.isEmpty())
1842 data->bpFileName = name;
1843 if (data->markerFileName.isEmpty())
1844 data->markerFileName = name;
1847 void GdbEngine::sendInsertBreakpoint(int index)
1849 const BreakpointData *data = qq->breakHandler()->at(index);
1851 if (data->funcName.isEmpty()) {
1852 where = data->fileName;
1854 // full names do not work on Mac/MI
1855 QFileInfo fi(data->fileName);
1856 where = fi.fileName();
1857 //where = fi.absoluteFilePath();
1860 // full names do not work on Mac/MI
1861 QFileInfo fi(data->fileName);
1862 where = fi.fileName();
1863 //where = m_manager->shortName(data->fileName);
1864 //if (where.isEmpty())
1865 // where = data->fileName;
1867 // we need something like "\"file name.cpp\":100" to
1868 // survive the gdb command line parser with file names intact
1869 where = "\"\\\"" + where + "\\\":" + data->lineNumber + "\"";
1871 where = data->funcName;
1874 // set up fallback in case of pending breakpoints which aren't handled
1875 // by the MI interface
1877 QString cmd = "-break-insert ";
1878 //if (!data->condition.isEmpty())
1879 // cmd += "-c " + data->condition + " ";
1883 QString cmd = "-break-insert -l -1 ";
1884 //if (!data->condition.isEmpty())
1885 // cmd += "-c " + data->condition + " ";
1889 QString cmd = "-break-insert ";
1890 //if (!data->condition.isEmpty())
1891 // cmd += "-c " + data->condition + " ";
1894 debugMessage(QString("Current state: %1").arg(q->status()));
1895 sendCommand(cmd, BreakInsert, index, NeedsStop);
1898 void GdbEngine::handleBreakList(const GdbResultRecord &record)
1900 // 45^done,BreakpointTable={nr_rows="2",nr_cols="6",hdr=[
1901 // {width="3",alignment="-1",col_name="number",colhdr="Num"}, ...
1902 // body=[bkpt={number="1",type="breakpoint",disp="keep",enabled="y",
1903 // addr="0x000000000040109e",func="main",file="app.cpp",
1904 // fullname="/home/apoenitz/dev/work/plugintest/app/app.cpp",
1905 // line="11",times="1"},
1906 // bkpt={number="2",type="breakpoint",disp="keep",enabled="y",
1907 // addr="<PENDING>",pending="plugin.cpp:7",times="0"}] ... }
1909 if (record.resultClass == GdbResultDone) {
1910 GdbMi table = record.data.findChild("BreakpointTable");
1911 if (table.isValid())
1912 handleBreakList(table);
1916 void GdbEngine::handleBreakList(const GdbMi &table)
1918 //qDebug() << "GdbEngine::handleOutput: table: "
1919 // << table.toString();
1920 GdbMi body = table.findChild("body");
1921 //qDebug() << "GdbEngine::handleOutput: body: "
1922 // << body.toString();
1924 if (body.isValid()) {
1926 bkpts = body.children();
1929 bkpts = table.children();
1930 // remove the 'hdr' and artificial items
1931 //qDebug() << "FOUND " << bkpts.size() << " BREAKPOINTS";
1932 for (int i = bkpts.size(); --i >= 0; ) {
1933 int num = bkpts.at(i).findChild("number").data().toInt();
1935 //qDebug() << "REMOVING " << i << bkpts.at(i).toString();
1939 //qDebug() << "LEFT " << bkpts.size() << " BREAKPOINTS";
1942 BreakHandler *handler = qq->breakHandler();
1943 for (int index = 0; index != bkpts.size(); ++index) {
1944 BreakpointData temp(handler);
1945 breakpointDataFromOutput(&temp, bkpts.at(index));
1946 int found = handler->findBreakpoint(temp);
1948 breakpointDataFromOutput(handler->at(found), bkpts.at(index));
1950 //qDebug() << "CANNOT HANDLE RESPONSE " << bkpts.at(index).toString();
1953 attemptBreakpointSynchronization();
1954 handler->updateMarkers();
1958 void GdbEngine::handleBreakIgnore(const GdbResultRecord &record, int index)
1962 // ~"Will stop next time breakpoint 2 is reached.\n"
1966 // ~"Will ignore next 12 crossings of breakpoint 2.\n"
1969 // gdb 6.3 does not produce any console output
1970 BreakHandler *handler = qq->breakHandler();
1971 if (record.resultClass == GdbResultDone && index < handler->size()) {
1972 QString msg = record.data.findChild("consolestreamoutput").data();
1973 BreakpointData *data = handler->at(index);
1974 //if (msg.contains("Will stop next time breakpoint")) {
1975 // data->bpIgnoreCount = "0";
1976 //} else if (msg.contains("Will ignore next")) {
1977 // data->bpIgnoreCount = data->ignoreCount;
1979 // FIXME: this assumes it is doing the right thing...
1980 data->bpIgnoreCount = data->ignoreCount;
1981 attemptBreakpointSynchronization();
1982 handler->updateMarkers();
1986 void GdbEngine::handleBreakCondition(const GdbResultRecord &record, int index)
1988 BreakHandler *handler = qq->breakHandler();
1989 if (record.resultClass == GdbResultDone) {
1990 // we just assume it was successful. otherwise we had to parse
1991 // the output stream data
1992 BreakpointData *data = handler->at(index);
1993 //qDebug() << "HANDLE BREAK CONDITION " << index << data->condition;
1994 data->bpCondition = data->condition;
1995 attemptBreakpointSynchronization();
1996 handler->updateMarkers();
1997 } else if (record.resultClass == GdbResultError) {
1998 QString msg = record.data.findChild("msg").data();
2000 if (1 || msg.startsWith("Error parsing breakpoint condition. "
2001 " Will try again when we hit the breakpoint.")) {
2002 BreakpointData *data = handler->at(index);
2003 //qDebug() << "ERROR BREAK CONDITION " << index << data->condition;
2004 data->bpCondition = data->condition;
2005 attemptBreakpointSynchronization();
2006 handler->updateMarkers();
2011 void GdbEngine::handleBreakInsert(const GdbResultRecord &record, int index)
2013 BreakHandler *handler = qq->breakHandler();
2014 if (record.resultClass == GdbResultDone) {
2015 //qDebug() << "HANDLE BREAK INSERT " << index;
2017 // interesting only on Mac?
2018 BreakpointData *data = handler->at(index);
2019 GdbMi bkpt = record.data.findChild("bkpt");
2020 //qDebug() << "BKPT: " << bkpt.toString() << " DATA" << data->toToolTip();
2021 breakpointDataFromOutput(data, bkpt);
2023 attemptBreakpointSynchronization();
2024 handler->updateMarkers();
2025 } else if (record.resultClass == GdbResultError) {
2026 const BreakpointData *data = handler->at(index);
2028 //QString where = "\"\\\"" + data->fileName + "\\\":"
2029 // + data->lineNumber + "\"";
2030 QString where = "\"" + data->fileName + "\":"
2032 sendCommand("break " + where, BreakInsert1, index);
2035 QFileInfo fi(data->fileName);
2036 QString where = "\"" + fi.fileName() + "\":"
2038 sendCommand("break " + where, BreakInsert1, index);
2041 QFileInfo fi(data->fileName);
2042 QString where = "\"" + fi.fileName() + "\":"
2044 //QString where = m_data->fileName + QLatin1Char(':') + data->lineNumber;
2045 sendCommand("break " + where, BreakInsert1, index);
2050 void GdbEngine::extractDataFromInfoBreak(const QString &output, BreakpointData *data)
2052 data->bpFileName = "<MULTIPLE>";
2054 //qDebug() << output;
2055 if (output.isEmpty())
2057 // "Num Type Disp Enb Address What
2058 // 4 breakpoint keep y <MULTIPLE> 0x00000000004066ad
2059 // 4.1 y 0x00000000004066ad in CTorTester
2060 // at /data5/dev/ide/main/tests/manual/gdbdebugger/simple/app.cpp:124
2062 // everything on a single line on Windows for constructors of classes
2063 // within namespaces.
2064 // Sometimes the path is relative too.
2066 QRegExp re("MULTIPLE.*(0x[0-9a-f]+) in (.*)\\s+at (.*):([\\d]+)([^\\d]|$)");
2067 re.setMinimal(true);
2069 if (re.indexIn(output) != -1) {
2070 data->bpAddress = re.cap(1);
2071 data->bpFuncName = re.cap(2).trimmed();
2072 data->bpLineNumber = re.cap(4);
2073 QString full = fullName(re.cap(3));
2074 data->markerLineNumber = data->bpLineNumber.toInt();
2075 data->markerFileName = full;
2076 data->bpFileName = full;
2077 //qDebug() << "FOUND BREAKPOINT\n" << output
2078 // << re.cap(1) << "\n" << re.cap(2) << "\n"
2079 // << re.cap(3) << "\n" << re.cap(4) << "\n";
2081 qDebug() << "COULD NOT MATCH " << re.pattern() << " AND " << output;
2082 data->bpNumber = "<unavailable>";
2086 void GdbEngine::handleBreakInfo(const GdbResultRecord &record, int bpNumber)
2088 BreakHandler *handler = qq->breakHandler();
2089 if (record.resultClass == GdbResultDone) {
2090 // Old-style output for multiple breakpoints, presumably in a
2092 int found = handler->findBreakpoint(bpNumber);
2094 QString str = record.data.findChild("consolestreamoutput").data();
2095 extractDataFromInfoBreak(str, handler->at(found));
2096 handler->updateMarkers();
2097 attemptBreakpointSynchronization(); // trigger "ready"
2102 void GdbEngine::handleBreakInsert1(const GdbResultRecord &record, int index)
2104 BreakHandler *handler = qq->breakHandler();
2105 if (record.resultClass == GdbResultDone) {
2106 // Pending breakpoints in dylibs on Mac only?
2107 BreakpointData *data = handler->at(index);
2108 GdbMi bkpt = record.data.findChild("bkpt");
2109 breakpointDataFromOutput(data, bkpt);
2110 attemptBreakpointSynchronization(); // trigger "ready"
2111 handler->updateMarkers();
2112 } else if (record.resultClass == GdbResultError) {
2113 qDebug() << "INSERTING BREAKPOINT WITH BASE NAME FAILED. GIVING UP";
2114 BreakpointData *data = handler->at(index);
2115 data->bpNumber = "<unavailable>";
2116 attemptBreakpointSynchronization(); // trigger "ready"
2117 handler->updateMarkers();
2121 void GdbEngine::attemptBreakpointSynchronization()
2123 // Non-lethal check for nested calls
2124 static bool inBreakpointSychronization = false;
2125 QTC_ASSERT(!inBreakpointSychronization, /**/);
2126 inBreakpointSychronization = true;
2128 BreakHandler *handler = qq->breakHandler();
2130 foreach (BreakpointData *data, handler->takeRemovedBreakpoints()) {
2131 QString bpNumber = data->bpNumber;
2132 debugMessage(QString("DELETING BP %1 IN %2").arg(bpNumber)
2133 .arg(data->markerFileName));
2134 if (!bpNumber.trimmed().isEmpty())
2135 sendCommand("-break-delete " + bpNumber, BreakDelete, QVariant(),
2140 bool updateNeeded = false;
2142 for (int index = 0; index != handler->size(); ++index) {
2143 BreakpointData *data = handler->at(index);
2144 // multiple breakpoints?
2145 if (data->bpMultiple && data->bpFileName.isEmpty()) {
2146 sendCommand(QString("info break %1").arg(data->bpNumber),
2147 BreakInfo, data->bpNumber.toInt());
2148 updateNeeded = true;
2153 for (int index = 0; index != handler->size(); ++index) {
2154 BreakpointData *data = handler->at(index);
2155 // unset breakpoints?
2156 if (data->bpNumber.isEmpty()) {
2157 data->bpNumber = " ";
2158 sendInsertBreakpoint(index);
2159 //qDebug() << "UPDATE NEEDED BECAUSE OF UNKNOWN BREAKPOINT";
2160 updateNeeded = true;
2165 if (!updateNeeded) {
2166 for (int index = 0; index != handler->size(); ++index) {
2167 BreakpointData *data = handler->at(index);
2168 // update conditions if needed
2169 if (data->bpNumber.toInt() && data->condition != data->bpCondition
2170 && !data->conditionsMatch()) {
2171 sendCommand(QString("condition %1 %2").arg(data->bpNumber)
2172 .arg(data->condition), BreakCondition, index);
2173 //qDebug() << "UPDATE NEEDED BECAUSE OF CONDITION"
2174 // << data->condition << data->bpCondition;
2175 updateNeeded = true;
2178 // update ignorecount if needed
2179 if (data->bpNumber.toInt() && data->ignoreCount != data->bpIgnoreCount) {
2180 sendCommand(QString("ignore %1 %2").arg(data->bpNumber)
2181 .arg(data->ignoreCount), BreakIgnore, index);
2182 updateNeeded = true;
2188 for (int index = 0; index != handler->size(); ++index) {
2189 // happens sometimes on Mac. Brush over symptoms
2190 BreakpointData *data = handler->at(index);
2191 if (data->markerFileName.startsWith("../")) {
2192 data->markerFileName = fullName(data->markerFileName);
2193 handler->updateMarkers();
2197 if (!updateNeeded && m_waitingForBreakpointSynchronizationToContinue) {
2198 m_waitingForBreakpointSynchronizationToContinue = false;
2199 // we continue the execution
2203 inBreakpointSychronization = false;
2207 //////////////////////////////////////////////////////////////////////
2209 // Disassembler specific stuff
2211 //////////////////////////////////////////////////////////////////////
2213 void GdbEngine::reloadDisassembler()
2215 emit sendCommand("disassemble", DisassemblerList, m_address);
2218 void GdbEngine::handleDisassemblerList(const GdbResultRecord &record,
2219 const QString &cookie)
2221 QList<DisassemblerLine> lines;
2222 static const QString pad = QLatin1String(" ");
2223 int currentLine = -1;
2224 if (record.resultClass == GdbResultDone) {
2225 QString res = record.data.findChild("consolestreamoutput").data();
2226 QTextStream ts(&res, QIODevice::ReadOnly);
2227 while (!ts.atEnd()) {
2228 //0x0000000000405fd8 <_ZN11QTextStreamD1Ev@plt+0>:
2229 // jmpq *2151890(%rip) # 0x6135b0 <_GLOBAL_OFFSET_TABLE_+640>
2230 //0x0000000000405fde <_ZN11QTextStreamD1Ev@plt+6>:
2232 //0x0000000000405fe3 <_ZN11QTextStreamD1Ev@plt+11>:
2233 // jmpq 0x405af8 <_init+24>
2234 //0x0000000000405fe8 <_ZN9QHashData6rehashEi@plt+0>:
2235 // jmpq *2151882(%rip) # 0x6135b8 <_GLOBAL_OFFSET_TABLE_+648>
2236 QString str = ts.readLine();
2237 if (!str.startsWith(QLatin1String("0x"))) {
2238 //qDebug() << "IGNORING DISASSEMBLER" << str;
2241 DisassemblerLine line;
2242 QTextStream ts(&str, QIODevice::ReadOnly);
2243 ts >> line.address >> line.symbol;
2244 line.mnemonic = ts.readLine().trimmed();
2245 if (line.symbol.endsWith(QLatin1Char(':')))
2246 line.symbol.chop(1);
2247 line.addressDisplay = line.address + pad;
2248 if (line.addressDisplay.startsWith(QLatin1String("0x00000000")))
2249 line.addressDisplay.replace(2, 8, QString());
2250 line.symbolDisplay = line.symbol + pad;
2252 if (line.address == cookie)
2253 currentLine = lines.size();
2258 DisassemblerLine line;
2259 line.addressDisplay = tr("<could not retreive module information>");
2263 qq->disassemblerHandler()->setLines(lines);
2264 if (currentLine != -1)
2265 qq->disassemblerHandler()->setCurrentLine(currentLine);
2269 //////////////////////////////////////////////////////////////////////
2271 // Modules specific stuff
2273 //////////////////////////////////////////////////////////////////////
2275 void GdbEngine::loadSymbols(const QString &moduleName)
2277 // FIXME: gdb does not understand quoted names here (tested with 6.8)
2278 sendCommand("sharedlibrary " + dotEscape(moduleName));
2282 void GdbEngine::loadAllSymbols()
2284 sendCommand("sharedlibrary .*");
2288 void GdbEngine::reloadModules()
2290 sendCommand("info shared", ModulesList, QVariant());
2293 void GdbEngine::handleModulesList(const GdbResultRecord &record)
2295 QList<Module> modules;
2296 if (record.resultClass == GdbResultDone) {
2297 QString data = record.data.findChild("consolestreamoutput").data();
2298 QTextStream ts(&data, QIODevice::ReadOnly);
2299 while (!ts.atEnd()) {
2300 QString line = ts.readLine();
2301 if (!line.startsWith("0x"))
2304 QString symbolsRead;
2305 QTextStream ts(&line, QIODevice::ReadOnly);
2306 ts >> module.startAddress >> module.endAddress >> symbolsRead;
2307 module.moduleName = ts.readLine().trimmed();
2308 module.symbolsRead = (symbolsRead == "Yes");
2309 modules.append(module);
2312 qq->modulesHandler()->setModules(modules);
2316 //////////////////////////////////////////////////////////////////////
2318 // Source files specific stuff
2320 //////////////////////////////////////////////////////////////////////
2322 void GdbEngine::reloadSourceFiles()
2324 sendCommand("-file-list-exec-source-files", GdbQuerySources);
2328 //////////////////////////////////////////////////////////////////////
2330 // Stack specific stuff
2332 //////////////////////////////////////////////////////////////////////
2334 void GdbEngine::handleStackSelectThread(const GdbResultRecord &record, int)
2337 //qDebug("FIXME: StackHandler::handleOutput: SelectThread");
2338 q->showStatusMessage(tr("Retrieving data for stack view..."), 3000);
2339 sendCommand("-stack-list-frames", StackListFrames);
2343 void GdbEngine::handleStackListFrames(const GdbResultRecord &record)
2345 QList<StackFrame> stackFrames;
2347 const GdbMi stack = record.data.findChild("stack");
2348 QString dummy = stack.toString();
2349 if (!stack.isValid()) {
2350 qDebug() << "FIXME: stack: " << stack.toString();
2356 for (int i = 0; i != stack.childCount(); ++i) {
2357 //qDebug() << "HANDLING FRAME: " << stack.childAt(i).toString();
2358 const GdbMi frameMi = stack.childAt(i);
2362 files.append(frameMi.findChild("fullname").data());
2363 files.append(frameMi.findChild("file").data());
2364 frame.file = fullName(files);
2365 frame.function = frameMi.findChild("func").data();
2366 frame.from = frameMi.findChild("from").data();
2367 frame.line = frameMi.findChild("line").data().toInt();
2368 frame.address = frameMi.findChild("addr").data();
2370 stackFrames.append(frame);
2373 const bool isBogus =
2374 // Assume this is wrong and points to some strange stl_algobase
2375 // implementation. Happens on Karsten's XP system with Gdb 5.50
2376 (frame.file.endsWith("/bits/stl_algobase.h") && frame.line == 150)
2377 // Also wrong. Happens on Vista with Gdb 5.50
2378 || (frame.function == "operator new" && frame.line == 151);
2380 // immediately leave bogus frames
2381 if (topFrame == -1 && isBogus) {
2382 sendCommand("-exec-finish");
2388 // Initialize top frame to the first valid frame
2389 const bool isValid = !frame.file.isEmpty() && !frame.function.isEmpty();
2390 if (isValid && topFrame == -1)
2394 qq->stackHandler()->setFrames(stackFrames);
2397 if (0 && topFrame != -1) {
2398 // updates of locals already triggered early
2399 const StackFrame &frame = qq->stackHandler()->currentFrame();
2400 bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable();
2402 q->gotoLocation(frame.file, frame.line, true);
2404 qDebug() << "FULL NAME NOT USABLE 0: " << frame.file;
2406 activateFrame(topFrame);
2409 if (topFrame != -1) {
2410 // updates of locals already triggered early
2411 const StackFrame &frame = qq->stackHandler()->currentFrame();
2412 bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable();
2414 q->gotoLocation(frame.file, frame.line, true);
2416 qDebug() << "FULL NAME NOT USABLE 0: " << frame.file << topFrame;
2421 void GdbEngine::selectThread(int index)
2423 //reset location arrow
2426 ThreadsHandler *threadsHandler = qq->threadsHandler();
2427 threadsHandler->setCurrentThread(index);
2429 QList<ThreadData> threads = threadsHandler->threads();
2430 QTC_ASSERT(index < threads.size(), return);
2431 int id = threads.at(index).id;
2432 q->showStatusMessage(tr("Retrieving data for stack view..."), 10000);
2433 sendCommand(QLatin1String("-thread-select ") + QString::number(id),
2437 void GdbEngine::activateFrame(int frameIndex)
2439 if (q->status() != DebuggerInferiorStopped)
2442 StackHandler *stackHandler = qq->stackHandler();
2443 int oldIndex = stackHandler->currentIndex();
2444 //qDebug() << "ACTIVATE FRAME: " << frameIndex << oldIndex
2445 // << stackHandler->currentIndex();
2447 QTC_ASSERT(frameIndex < stackHandler->stackSize(), return);
2449 if (oldIndex != frameIndex) {
2452 // Assuming this always succeeds saves a roundtrip.
2453 // Otherwise the lines below would need to get triggered
2454 // after a response to this -stack-select-frame here.
2455 sendCommand("-stack-select-frame " + QString::number(frameIndex));
2457 stackHandler->setCurrentIndex(frameIndex);
2461 const StackFrame &frame = stackHandler->currentFrame();
2463 bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable();
2465 q->gotoLocation(frame.file, frame.line, true);
2467 qDebug() << "FULL NAME NOT USABLE: " << frame.file;
2470 void GdbEngine::handleStackListThreads(const GdbResultRecord &record, int id)
2472 // "72^done,{thread-ids={thread-id="2",thread-id="1"},number-of-threads="2"}
2473 const QList<GdbMi> items = record.data.findChild("thread-ids").children();
2474 QList<ThreadData> threads;
2475 int currentIndex = -1;
2476 for (int index = 0, n = items.size(); index != n; ++index) {
2478 thread.id = items.at(index).data().toInt();
2479 threads.append(thread);
2480 if (thread.id == id) {
2481 //qDebug() << "SETTING INDEX TO: " << index << " ID: "<< id << "RECOD: "<< record.toString();
2482 currentIndex = index;
2485 ThreadsHandler *threadsHandler = qq->threadsHandler();
2486 threadsHandler->setThreads(threads);
2487 threadsHandler->setCurrentThread(currentIndex);
2491 //////////////////////////////////////////////////////////////////////
2493 // Register specific stuff
2495 //////////////////////////////////////////////////////////////////////
2497 void GdbEngine::reloadRegisters()
2499 QString format = qq->registerHandler()->model()->property(PROPERTY_REGISTER_FORMAT).toString();
2500 sendCommand("-data-list-register-values " + format, RegisterListValues);
2503 void GdbEngine::handleRegisterListNames(const GdbResultRecord &record)
2505 if (record.resultClass != GdbResultDone)
2508 QList<Register> registers;
2509 foreach (const GdbMi &item, record.data.findChild("register-names").children())
2510 registers.append(Register(item.data()));
2512 qq->registerHandler()->setRegisters(registers);
2515 void GdbEngine::handleRegisterListValues(const GdbResultRecord &record)
2517 if (record.resultClass != GdbResultDone)
2520 QList<Register> registers = qq->registerHandler()->registers();
2522 // 24^done,register-values=[{number="0",value="0xf423f"},...]
2523 foreach (const GdbMi &item, record.data.findChild("register-values").children()) {
2524 int index = item.findChild("number").data().toInt();
2525 if (index < registers.size()) {
2526 Register ® = registers[index];
2527 QString value = item.findChild("value").data();
2528 reg.changed = (value != reg.value);
2533 qq->registerHandler()->setRegisters(registers);
2537 //////////////////////////////////////////////////////////////////////
2539 // Thread specific stuff
2541 //////////////////////////////////////////////////////////////////////
2543 bool GdbEngine::supportsThreads() const
2545 // 6.3 crashes happily on -thread-list-ids. So don't use it.
2546 // The test below is a semi-random pick, 6.8 works fine
2547 return m_gdbVersion > 60500;
2550 //////////////////////////////////////////////////////////////////////
2552 // Tooltip specific stuff
2554 //////////////////////////////////////////////////////////////////////
2556 static WatchData m_toolTip;
2557 static QString m_toolTipExpression;
2558 static QPoint m_toolTipPos;
2559 static QMap<QString, WatchData> m_toolTipCache;
2561 static bool hasLetterOrNumber(const QString &exp)
2563 for (int i = exp.size(); --i >= 0; )
2564 if (exp[i].isLetterOrNumber() || exp[i] == '_')
2569 static bool hasSideEffects(const QString &exp)
2572 return exp.contains("-=")
2573 || exp.contains("+=")
2574 || exp.contains("/=")
2575 || exp.contains("*=")
2576 || exp.contains("&=")
2577 || exp.contains("|=")
2578 || exp.contains("^=")
2579 || exp.contains("--")
2580 || exp.contains("++");
2583 static bool isKeyWord(const QString &exp)
2585 // FIXME: incomplete
2586 return exp == QLatin1String("class")
2587 || exp == QLatin1String("const")
2588 || exp == QLatin1String("do")
2589 || exp == QLatin1String("if")
2590 || exp == QLatin1String("return")
2591 || exp == QLatin1String("struct")
2592 || exp == QLatin1String("template")
2593 || exp == QLatin1String("void")
2594 || exp == QLatin1String("volatile")
2595 || exp == QLatin1String("while");
2598 void GdbEngine::setToolTipExpression(const QPoint &pos, const QString &exp0)
2600 //qDebug() << "SET TOOLTIP EXP" << pos << exp0;
2601 if (q->status() != DebuggerInferiorStopped) {
2602 //qDebug() << "SUPPRESSING DEBUGGER TOOLTIP, INFERIOR NOT STOPPED";
2606 if (q->settings()->m_debugDumpers) {
2607 // minimize interference
2612 m_toolTipExpression = exp0;
2615 if (m_toolTip.isTypePending()) {
2616 qDebug() << "suppressing duplicated tooltip creation";
2620 if (m_toolTipCache.contains(exp)) {
2621 const WatchData & data = m_toolTipCache[exp];
2622 // FIXME: qq->watchHandler()->collapseChildren(data.iname);
2627 QToolTip::hideText();
2628 if (exp.isEmpty() || exp.startsWith("#")) {
2629 QToolTip::hideText();
2633 if (!hasLetterOrNumber(exp)) {
2634 QToolTip::showText(m_toolTipPos,
2635 "'" + exp + "' contains no identifier");
2642 if (exp.startsWith('"') && exp.endsWith('"')) {
2643 QToolTip::showText(m_toolTipPos, "String literal " + exp);
2647 if (exp.startsWith("++") || exp.startsWith("--"))
2650 if (exp.endsWith("++") || exp.endsWith("--"))
2653 if (exp.startsWith("<") || exp.startsWith("["))
2656 if (hasSideEffects(exp)) {
2657 QToolTip::showText(m_toolTipPos,
2658 "Cowardly refusing to evaluate expression '" + exp
2659 + "' with potential side effects");
2663 // Gdb crashes when creating a variable object with the name
2664 // of the type of 'this'
2666 for (int i = 0; i != m_currentLocals.childCount(); ++i) {
2667 if (m_currentLocals.childAt(i).exp == "this") {
2668 qDebug() << "THIS IN ROW " << i;
2669 if (m_currentLocals.childAt(i).type.startsWith(exp)) {
2670 QToolTip::showText(m_toolTipPos,
2671 exp + ": type of current 'this'");
2672 qDebug() << " TOOLTIP CRASH SUPPRESSED";
2680 //if (m_manager->status() != DebuggerInferiorStopped)
2683 // FIXME: 'exp' can contain illegal characters
2684 m_toolTip = WatchData();
2685 //m_toolTip.level = 0;
2686 // m_toolTip.row = 0;
2687 // m_toolTip.parentIndex = 2;
2688 m_toolTip.exp = exp;
2689 m_toolTip.name = exp;
2690 m_toolTip.iname = tooltipIName;
2691 insertData(m_toolTip);
2692 updateWatchModel2();
2696 //////////////////////////////////////////////////////////////////////
2698 // Watch specific stuff
2700 //////////////////////////////////////////////////////////////////////
2702 static const QString strNotInScope = QLatin1String("<not in scope>");
2704 static bool isPointerType(const QString &type)
2706 return type.endsWith("*") || type.endsWith("* const");
2709 static bool isAccessSpecifier(const QString &str)
2711 static const QStringList items =
2712 QStringList() << "private" << "protected" << "public";
2713 return items.contains(str);
2716 static bool startsWithDigit(const QString &str)
2718 return !str.isEmpty() && str[0] >= '0' && str[0] <= '9';
2721 QString stripPointerType(QString type)
2723 if (type.endsWith("*"))
2725 if (type.endsWith("* const"))
2727 if (type.endsWith(' '))
2732 static QString gdbQuoteTypes(const QString &type)
2734 // gdb does not understand sizeof(Core::IFile*).
2735 // "sizeof('Core::IFile*')" is also not acceptable,
2736 // it needs to be "sizeof('Core::IFile'*)"
2738 // We never will have a perfect solution here (even if we had a full blown
2739 // C++ parser as we do not have information on what is a type and what is
2740 // a variable name. So "a<b>::c" could either be two comparisons of values
2741 // 'a', 'b' and '::c', or a nested type 'c' in a template 'a<b>'. We
2742 // assume here it is the latter.
2745 // (*('myns::QPointer<myns::QObject>*'*)0x684060)" is not acceptable
2746 // (*('myns::QPointer<myns::QObject>'**)0x684060)" is acceptable
2747 if (isPointerType(type))
2748 return gdbQuoteTypes(stripPointerType(type)) + "*";
2752 int templateLevel = 0;
2753 for (int i = 0; i != type.size(); ++i) {
2754 QChar c = type.at(i);
2755 if (c.isLetterOrNumber() || c == '_' || c == ':' || c == ' ') {
2757 } else if (c == '<') {
2760 } else if (c == '<') {
2763 } else if (templateLevel > 0) {
2766 if (accu.contains(':') || accu.contains('<'))
2767 result += '\'' + accu + '\'';
2774 if (accu.contains(':') || accu.contains('<'))
2775 result += '\'' + accu + '\'';
2778 //qDebug() << "GDB_QUOTING" << type << " TO " << result;
2783 static void setWatchDataValue(WatchData &data, const GdbMi &mi,
2789 case 0: // unencoded 8 bit data
2792 case 1: // base64 encoded 8 bit data
2793 ba = QByteArray::fromBase64(mi.data());
2794 ba = '"' + ba + '"';
2796 case 2: // base64 encoded 16 bit data
2797 ba = QByteArray::fromBase64(mi.data());
2798 ba = QString::fromUtf16((ushort *)ba.data(), ba.size() / 2).toUtf8();
2799 ba = '"' + ba + '"';
2801 case 3: // base64 encoded 32 bit data
2802 ba = QByteArray::fromBase64(mi.data());
2803 ba = QString::fromUcs4((uint *)ba.data(), ba.size() / 4).toUtf8();
2804 ba = '"' + ba + '"';
2809 data.setValueNeeded();
2813 static void setWatchDataEditValue(WatchData &data, const GdbMi &mi)
2816 data.editvalue = mi.data();
2819 static void setWatchDataValueToolTip(WatchData &data, const GdbMi &mi)
2822 data.setValueToolTip(mi.data());
2825 static void setWatchDataChildCount(WatchData &data, const GdbMi &mi)
2828 data.childCount = mi.data().toInt();
2829 data.setChildCountUnneeded();
2830 if (data.childCount == 0)
2831 data.setChildrenUnneeded();
2833 data.childCount = -1;
2837 static void setWatchDataValueDisabled(WatchData &data, const GdbMi &mi)
2839 if (mi.data() == "true")
2840 data.valuedisabled = true;
2841 else if (mi.data() == "false")
2842 data.valuedisabled = false;
2845 static void setWatchDataExpression(WatchData &data, const GdbMi &mi)
2848 data.exp = "(" + mi.data() + ")";
2851 static void setWatchDataAddress(WatchData &data, const GdbMi &mi)
2854 data.addr = mi.data();
2855 if (data.exp.isEmpty())
2856 data.exp = "(*(" + gdbQuoteTypes(data.type) + "*)" + data.addr + ")";
2860 static bool extractTemplate(const QString &type, QString *tmplate, QString *inner)
2862 // Input "Template<Inner1,Inner2,...>::Foo" will return "Template::Foo" in
2863 // 'tmplate' and "Inner1@Inner2@..." etc in 'inner'. Result indicates
2864 // whether parsing was successful
2866 bool skipSpace = false;
2867 for (int i = 0; i != type.size(); ++i) {
2869 if (c == ' ' && skipSpace) {
2871 } else if (c == '<') {
2872 *(level == 0 ? tmplate : inner) += c;
2874 } else if (c == '>') {
2876 *(level == 0 ? tmplate : inner) += c;
2877 } else if (c == ',') {
2878 *inner += (level == 1) ? '@' : ',';
2881 *(level == 0 ? tmplate : inner) += c;
2884 *tmplate = tmplate->trimmed();
2885 *tmplate = tmplate->remove("<>");
2886 *inner = inner->trimmed();
2887 //qDebug() << "EXTRACT TEMPLATE: " << *tmplate << *inner << " FROM " << type;
2888 return !inner->isEmpty();
2891 static QString extractTypeFromPTypeOutput(const QString &str)
2893 int pos0 = str.indexOf('=');
2894 int pos1 = str.indexOf('{');
2895 int pos2 = str.lastIndexOf('}');
2897 if (pos0 != -1 && pos1 != -1 && pos2 != -1)
2898 res = str.mid(pos0 + 2, pos1 - 1 - pos0)
2899 + " ... " + str.right(str.size() - pos2);
2900 return res.simplified();
2903 static bool isIntOrFloatType(const QString &type)
2905 static const QStringList types = QStringList()
2906 << "char" << "int" << "short" << "float" << "double" << "long"
2907 << "bool" << "signed char" << "unsigned" << "unsigned char"
2908 << "unsigned int" << "unsigned long" << "long long"
2909 << "unsigned long long";
2910 return types.contains(type);
2913 static QString sizeofTypeExpression(const QString &type)
2915 if (type.endsWith('*'))
2916 return "sizeof(void*)";
2917 if (type.endsWith('>'))
2918 return "sizeof(" + type + ")";
2919 return "sizeof(" + gdbQuoteTypes(type) + ")";
2922 void GdbEngine::setUseCustomDumpers(bool on)
2924 //qDebug() << "SWITCHING ON/OFF DUMPER DEBUGGING:" << on;
2926 // FIXME: a bit too harsh, but otherwise the treeview sometimes look funny
2927 //m_expandedINames.clear();
2932 bool GdbEngine::isCustomValueDumperAvailable(const QString &type) const
2934 DebuggerSettings *s = q->settings();
2935 if (!s->m_useCustomDumpers)
2937 if (s->m_debugDumpers && qq->stackHandler()->isDebuggingDumpers())
2939 if (m_dataDumperState != DataDumperAvailable)
2943 if (m_availableSimpleDumpers.contains(type))
2949 if (!extractTemplate(type, &tmplate, &inner))
2951 return m_availableSimpleDumpers.contains(tmplate);
2954 void GdbEngine::runCustomDumper(const WatchData & data0, bool dumpChildren)
2956 WatchData data = data0;
2957 QTC_ASSERT(!data.exp.isEmpty(), return);
2960 bool isTemplate = extractTemplate(data.type, &tmplate, &inner);
2961 QStringList inners = inner.split('@');
2962 if (inners.at(0).isEmpty())
2964 for (int i = 0; i != inners.size(); ++i)
2965 inners[i] = inners[i].simplified();
2967 QString outertype = isTemplate ? tmplate : data.type;
2968 // adjust the data extract
2969 if (outertype == m_namespace + "QWidget")
2970 outertype = m_namespace + "QObject";
2972 QString extraArgs[4];
2977 int extraArgCount = 0;
2979 // "generic" template dumpers: passing sizeof(argument)
2980 // gives already most information the dumpers need
2981 foreach (const QString &arg, inners)
2982 extraArgs[extraArgCount++] = sizeofTypeExpression(arg);
2984 // in rare cases we need more or less:
2985 if (outertype == m_namespace + "QObject") {
2986 extraArgs[0] = "(char*)&((('"
2987 + m_namespace + "QObjectPrivate'*)&"
2988 + data.exp + ")->children)-(char*)&" + data.exp;
2989 } else if (outertype == m_namespace + "QVector") {
2990 extraArgs[1] = "(char*)&(("
2991 + data.exp + ").d->array)-(char*)" + data.exp + ".d";
2992 } else if (outertype == m_namespace + "QObjectSlot"
2993 || outertype == m_namespace + "QObjectSignal") {
2994 // we need the number out of something like
2995 // iname="local.ob.slots.[2]deleteLater()"
2996 int lastOpened = data.iname.lastIndexOf('[');
2997 int lastClosed = data.iname.lastIndexOf(']');
2998 QString slotNumber = "-1";
2999 if (lastOpened != -1 && lastClosed != -1)
3000 slotNumber = data.iname.mid(lastOpened + 1, lastClosed - lastOpened - 1);
3001 extraArgs[0] = slotNumber;
3002 } else if (outertype == m_namespace + "QMap" || outertype == m_namespace + "QMultiMap") {
3004 if (m_qtVersion >= (4 << 16) + (5 << 8) + 0) {
3005 nodetype = m_namespace + "QMapNode";
3006 nodetype += data.type.mid(outertype.size());
3008 // FIXME: doesn't work for QMultiMap
3009 nodetype = data.type + "::Node";
3011 //qDebug() << "OUTERTYPE: " << outertype << " NODETYPE: " << nodetype
3012 // << "QT VERSION" << m_qtVersion << ((4 << 16) + (5 << 8) + 0);
3013 extraArgs[2] = sizeofTypeExpression(nodetype);
3014 extraArgs[3] = "(size_t)&(('" + nodetype + "'*)0)->value";
3015 } else if (outertype == m_namespace + "QMapNode") {
3016 extraArgs[2] = sizeofTypeExpression(data.type);
3017 extraArgs[3] = "(size_t)&(('" + data.type + "'*)0)->value";
3018 } else if (outertype == "std::vector") {
3019 //qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
3020 if (inners.at(0) == "bool") {
3021 outertype = "std::vector::bool";
3023 //extraArgs[extraArgCount++] = sizeofTypeExpression(data.type);
3024 //extraArgs[extraArgCount++] = "(size_t)&(('" + data.type + "'*)0)->value";
3026 } else if (outertype == "std::deque") {
3027 // remove 'std::allocator<...>':
3029 } else if (outertype == "std::stack") {
3030 // remove 'std::allocator<...>':
3032 } else if (outertype == "std::map") {
3033 // We don't want the comparator and the allocator confuse gdb.
3034 // But we need the offset of the second item in the value pair.
3035 // We read the type of the pair from the allocator argument because
3036 // that gets the constness "right" (in the sense that gdb can
3038 QString pairType = inners.at(3);
3039 // remove 'std::allocator<...>':
3040 pairType = pairType.mid(15, pairType.size() - 15 - 2);
3041 extraArgs[2] = "(size_t)&(('" + pairType + "'*)0)->second";
3043 } else if (outertype == "std::basic_string") {
3044 //qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
3045 if (inners.at(0) == "char") {
3046 outertype = "std::string";
3047 } else if (inners.at(0) == "wchar_t") {
3048 outertype = "std::wstring";
3056 //int protocol = (data.iname.startsWith("watch") && data.type == "QImage") ? 3 : 2;
3057 //int protocol = data.iname.startsWith("watch") ? 3 : 2;
3059 //int protocol = isDisplayedIName(data.iname) ? 3 : 2;
3062 if (data.addr.startsWith("0x"))
3063 addr = "(void*)" + data.addr;
3065 addr = "&(" + data.exp + ")";
3068 params.append(outertype.toUtf8());
3069 params.append('\0');
3070 params.append(data.iname.toUtf8());
3071 params.append('\0');
3072 params.append(data.exp.toUtf8());
3073 params.append('\0');
3074 params.append(inner.toUtf8());
3075 params.append('\0');
3076 params.append(data.iname.toUtf8());
3077 params.append('\0');
3079 sendWatchParameters(params);
3081 QString cmd ="call "
3082 + QString("qDumpObjectData440(")
3083 + QString::number(protocol)
3084 + ',' + "%1+1" // placeholder for token
3086 + ',' + (dumpChildren ? "1" : "0")
3087 + ',' + extraArgs[0]
3088 + ',' + extraArgs[1]
3089 + ',' + extraArgs[2]
3090 + ',' + extraArgs[3] + ')';
3092 //qDebug() << "CMD: " << cmd;
3096 sendSynchronizedCommand(cmd, WatchDumpCustomValue1, var);
3098 q->showStatusMessage(
3099 tr("Retrieving data for watch view (%1 requests pending)...")
3100 .arg(m_pendingRequests + 1), 10000);
3102 // retrieve response
3103 sendSynchronizedCommand("p (char*)qDumpOutBuffer", WatchDumpCustomValue2, var);
3106 void GdbEngine::createGdbVariable(const WatchData &data)
3108 sendSynchronizedCommand("-var-delete \"" + data.iname + '"');
3109 QString exp = data.exp;
3110 if (exp.isEmpty() && data.addr.startsWith("0x"))
3111 exp = "*(" + gdbQuoteTypes(data.type) + "*)" + data.addr;
3112 QVariant val = QVariant::fromValue<WatchData>(data);
3113 sendSynchronizedCommand("-var-create \"" + data.iname + '"' + " * "
3114 + '"' + exp + '"', WatchVarCreate, val);
3117 void GdbEngine::updateSubItem(const WatchData &data0)
3119 WatchData data = data0;
3121 qDebug() << "UPDATE SUBITEM: " << data.toString();
3123 QTC_ASSERT(data.isValid(), return);
3125 // in any case we need the type first
3126 if (data.isTypeNeeded()) {
3127 // This should only happen if we don't have a variable yet.
3128 // Let's play safe, though.
3129 if (!data.variable.isEmpty()) {
3130 // Update: It does so for out-of-scope watchers.
3132 qDebug() << "FIXME: GdbEngine::updateSubItem: "
3133 << data.toString() << "should not happen";
3135 data.setType("<out of scope>");
3136 data.setValue("<out of scope>");
3137 data.setChildCount(0);
3142 // The WatchVarCreate handler will receive type information
3143 // and re-insert a WatchData item with correct type, so
3144 // we will not re-enter this bit.
3145 // FIXME: Concurrency issues?
3146 createGdbVariable(data);
3150 // we should have a type now. this is relied upon further below
3151 QTC_ASSERT(!data.type.isEmpty(), return);
3153 // a common case that can be easily solved
3154 if (data.isChildrenNeeded() && isPointerType(data.type)
3155 && !isCustomValueDumperAvailable(data.type)) {
3156 // We sometimes know what kind of children pointers have
3158 qDebug() << "IT'S A POINTER";
3162 data1.iname = data.iname + ".*";
3163 data1.name = "*" + data.name;
3164 data1.exp = "(*(" + data.exp + "))";
3165 data1.type = stripPointerType(data.type);
3166 data1.setValueNeeded();
3168 data.setChildrenUnneeded();
3171 // Try automatic dereferentiation
3172 data.exp = "*(" + data.exp + ")";
3173 data.type = data.type + "."; // FIXME: fragile HACK to avoid recursion
3179 if (data.isValueNeeded() && isCustomValueDumperAvailable(data.type)) {
3181 qDebug() << "UPDATE SUBITEM: CUSTOMVALUE";
3183 runCustomDumper(data, qq->watchHandler()->isExpandedIName(data.iname));
3188 if (data.isValueNeeded() && data.exp.isEmpty()) {
3190 qDebug() << "UPDATE SUBITEM: NO EXPRESSION?";
3192 data.setError("<no expression given>");
3198 if (data.isValueNeeded() && data.variable.isEmpty()) {
3200 qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR VALUE";
3202 createGdbVariable(data);
3203 // the WatchVarCreate handler will re-insert a WatchData
3204 // item, with valueNeeded() set.
3208 if (data.isValueNeeded()) {
3209 QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
3211 qDebug() << "UPDATE SUBITEM: VALUE";
3213 QString cmd = "-var-evaluate-expression \"" + data.iname + "\"";
3214 sendSynchronizedCommand(cmd, WatchEvaluateExpression,
3215 QVariant::fromValue(data));
3219 if (data.isChildrenNeeded() && isCustomValueDumperAvailable(data.type)) {
3221 qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN";
3223 runCustomDumper(data, true);
3227 if (data.isChildrenNeeded() && data.variable.isEmpty()) {
3229 qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDREN";
3231 createGdbVariable(data);
3232 // the WatchVarCreate handler will re-insert a WatchData
3233 // item, with childrenNeeded() set.
3237 if (data.isChildrenNeeded()) {
3238 QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
3239 QString cmd = "-var-list-children --all-values \"" + data.variable + "\"";
3240 sendSynchronizedCommand(cmd, WatchVarListChildren, QVariant::fromValue(data));
3244 if (data.isChildCountNeeded() && isCustomValueDumperAvailable(data.type)) {
3246 qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN";
3248 runCustomDumper(data, qq->watchHandler()->isExpandedIName(data.iname));
3252 if (data.isChildCountNeeded() && data.variable.isEmpty()) {
3254 qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDCOUNT";
3256 createGdbVariable(data);
3257 // the WatchVarCreate handler will re-insert a WatchData
3258 // item, with childrenNeeded() set.
3262 if (data.isChildCountNeeded()) {
3263 QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
3264 QString cmd = "-var-list-children --all-values \"" + data.variable + "\"";
3265 sendCommand(cmd, WatchVarListChildren, QVariant::fromValue(data));
3269 qDebug() << "FIXME: UPDATE SUBITEM: " << data.toString();
3270 QTC_ASSERT(false, return);
3273 void GdbEngine::updateWatchModel()
3275 m_pendingRequests = 0;
3276 PENDING_DEBUG("EXTERNAL TRIGGERING UPDATE WATCH MODEL");
3277 updateWatchModel2();
3280 void GdbEngine::updateWatchModel2()
3282 PENDING_DEBUG("UPDATE WATCH MODEL");
3283 QList<WatchData> incomplete = qq->watchHandler()->takeCurrentIncompletes();
3284 //QTC_ASSERT(incomplete.isEmpty(), /**/);
3285 if (!incomplete.isEmpty()) {
3287 qDebug() << "##############################################";
3288 qDebug() << "UPDATE MODEL, FOUND INCOMPLETES:";
3289 foreach (const WatchData &data, incomplete)
3290 qDebug() << data.toString();
3293 // Bump requests to avoid model rebuilding during the nested
3294 // updateWatchModel runs.
3295 ++m_pendingRequests;
3296 foreach (const WatchData &data, incomplete)
3297 updateSubItem(data);
3298 PENDING_DEBUG("INTERNAL TRIGGERING UPDATE WATCH MODEL");
3299 updateWatchModel2();
3300 --m_pendingRequests;
3305 if (m_pendingRequests > 0) {
3306 PENDING_DEBUG("UPDATE MODEL, PENDING: " << m_pendingRequests);
3310 PENDING_DEBUG("REBUILDING MODEL");
3311 emit gdbInputAvailable(QString(),
3312 "[" + currentTime() + "] <Rebuild Watchmodel>");
3313 q->showStatusMessage(tr("Finished retrieving data."), 400);
3314 qq->watchHandler()->rebuildModel();
3316 if (!m_toolTipExpression.isEmpty()) {
3317 WatchData *data = qq->watchHandler()->findData(tooltipIName);
3319 //m_toolTipCache[data->exp] = *data;
3320 QToolTip::showText(m_toolTipPos,
3321 "(" + data->type + ") " + data->exp + " = " + data->value);
3323 QToolTip::showText(m_toolTipPos,
3324 "Cannot evaluate expression: " + m_toolTipExpression);
3329 void GdbEngine::handleQueryDataDumper1(const GdbResultRecord &record)
3334 void GdbEngine::handleQueryDataDumper2(const GdbResultRecord &record)
3336 //qDebug() << "DATA DUMPER TRIAL:" << record.toString();
3337 GdbMi output = record.data.findChild("consolestreamoutput");
3338 QByteArray out = output.data();
3339 out = out.mid(out.indexOf('"') + 2); // + 1 is success marker
3340 out = out.left(out.lastIndexOf('"'));
3341 //out.replace('\'', '"');
3342 out.replace("\\", "");
3343 out = "dummy={" + out + "}";
3344 //qDebug() << "OUTPUT: " << out;
3347 contents.fromString(out);
3348 GdbMi simple = contents.findChild("dumpers");
3349 m_namespace = contents.findChild("namespace").data();
3350 GdbMi qtversion = contents.findChild("qtversion");
3351 if (qtversion.children().size() == 3) {
3352 m_qtVersion = (qtversion.childAt(0).data().toInt() << 16)
3353 + (qtversion.childAt(1).data().toInt() << 8)
3354 + qtversion.childAt(2).data().toInt();
3355 //qDebug() << "FOUND QT VERSION: " << qtversion.toString() << m_qtVersion;
3360 //qDebug() << "CONTENTS: " << contents.toString();
3361 //qDebug() << "SIMPLE DUMPERS: " << simple.toString();
3362 m_availableSimpleDumpers.clear();
3363 foreach (const GdbMi &item, simple.children())
3364 m_availableSimpleDumpers.append(item.data());
3365 if (m_availableSimpleDumpers.isEmpty()) {
3366 m_dataDumperState = DataDumperUnavailable;
3367 QMessageBox::warning(q->mainWindow(),
3368 tr("Cannot find special data dumpers"),
3369 tr("The debugged binary does not contain information needed for "
3370 "nice display of Qt data types.\n\n"
3371 "You might want to try including the file\n\n"
3372 ".../share/qtcreator/gdbmacros/gdbmacros.cpp\n\n"
3373 "into your project directly.")
3376 m_dataDumperState = DataDumperAvailable;
3378 //qDebug() << "DATA DUMPERS AVAILABLE" << m_availableSimpleDumpers;
3381 void GdbEngine::sendWatchParameters(const QByteArray ¶ms0)
3383 QByteArray params = params0;
3384 params.append('\0');
3386 sprintf(buf, "set {char[%d]} qDumpInBuffer = {", params.size());
3388 encoded.append(buf);
3389 for (int i = 0; i != params.size(); ++i) {
3390 sprintf(buf, "%d,", int(params[i]));
3391 encoded.append(buf);
3393 encoded[encoded.size() - 1] = '}';
3395 sendCommand(encoded);
3398 void GdbEngine::handleVarAssign()
3400 // everything might have changed, force re-evaluation
3401 // FIXME: Speed this up by re-using variables and only
3402 // marking values as 'unknown'
3407 void GdbEngine::setWatchDataType(WatchData &data, const GdbMi &mi)
3410 if (!data.framekey.isEmpty())
3411 m_varToType[data.framekey] = mi.data();
3412 data.setType(mi.data());
3413 } else if (data.type.isEmpty()) {
3414 data.setTypeNeeded();
3418 void GdbEngine::handleVarCreate(const GdbResultRecord &record,
3419 const WatchData &data0)
3421 WatchData data = data0;
3422 // happens e.g. when we already issued a var-evaluate command
3423 if (!data.isValid())
3425 //qDebug() << "HANDLE VARIABLE CREATION: " << data.toString();
3426 if (record.resultClass == GdbResultDone) {
3427 data.variable = data.iname;
3428 setWatchDataType(data, record.data.findChild("type"));
3429 if (isCustomValueDumperAvailable(data.type)) {
3430 // we do not trust gdb if we have a custom dumper
3431 if (record.data.findChild("children").isValid())
3432 data.setChildrenUnneeded();
3433 else if (qq->watchHandler()->isExpandedIName(data.iname))
3434 data.setChildrenNeeded();
3437 if (record.data.findChild("children").isValid())
3438 data.setChildrenUnneeded();
3439 else if (qq->watchHandler()->isExpandedIName(data.iname))
3440 data.setChildrenNeeded();
3441 setWatchDataChildCount(data, record.data.findChild("numchild"));
3442 //if (data.isValueNeeded() && data.childCount > 0)
3443 // data.setValue(QByteArray());
3446 } else if (record.resultClass == GdbResultError) {
3447 data.setError(record.data.findChild("msg").data());
3448 if (data.isWatcher()) {
3449 data.value = strNotInScope;
3451 data.setAllUnneeded();
3452 data.setChildCount(0);
3453 data.valuedisabled = true;
3459 void GdbEngine::handleEvaluateExpression(const GdbResultRecord &record,
3460 const WatchData &data0)
3462 WatchData data = data0;
3463 QTC_ASSERT(data.isValid(), qDebug() << "HUH?");
3464 if (record.resultClass == GdbResultDone) {
3466 // data.name = record.data.findChild("value").data();
3468 setWatchDataValue(data, record.data.findChild("value"));
3469 } else if (record.resultClass == GdbResultError) {
3470 data.setError(record.data.findChild("msg").data());
3472 //qDebug() << "HANDLE EVALUATE EXPRESSION: " << data.toString();
3474 //updateWatchModel2();
3477 void GdbEngine::handleDumpCustomSetup(const GdbResultRecord &record)
3479 //qDebug() << "CUSTOM SETUP RESULT: " << record.toString();
3480 if (record.resultClass == GdbResultDone) {
3481 } else if (record.resultClass == GdbResultError) {
3482 QString msg = record.data.findChild("msg").data();
3483 //qDebug() << "CUSTOM DUMPER SETUP ERROR MESSAGE: " << msg;
3484 q->showStatusMessage(tr("Custom dumper setup: %1").arg(msg), 10000);
3488 void GdbEngine::handleDumpCustomValue1(const GdbResultRecord &record,
3489 const WatchData &data0)
3491 WatchData data = data0;
3492 QTC_ASSERT(data.isValid(), return);
3493 if (record.resultClass == GdbResultDone) {
3494 // ignore this case, data will follow
3495 } else if (record.resultClass == GdbResultError) {
3496 // Record an extra result, as the socket result will be lost
3498 //--m_pendingRequests;
3499 QString msg = record.data.findChild("msg").data();
3500 //qDebug() << "CUSTOM DUMPER ERROR MESSAGE: " << msg;
3502 // Make debugging of dumpers easier
3503 if (q->settings()->m_debugDumpers
3504 && msg.startsWith("The program being debugged stopped while")
3505 && msg.contains("qDumpObjectData440")) {
3507 sendCommand("p 0", GdbAsyncOutput2); // dummy
3511 //if (msg.startsWith("The program being debugged was sig"))
3512 // msg = strNotInScope;
3513 //if (msg.startsWith("The program being debugged stopped while"))
3514 // msg = strNotInScope;
3515 //data.setError(msg);
3520 void GdbEngine::handleDumpCustomValue2(const GdbResultRecord &record,
3521 const WatchData &data0)
3523 WatchData data = data0;
3524 QTC_ASSERT(data.isValid(), return);
3525 //qDebug() << "CUSTOM VALUE RESULT: " << record.toString();
3526 //qDebug() << "FOR DATA: " << data.toString() << record.resultClass;
3527 if (record.resultClass != GdbResultDone) {
3528 qDebug() << "STRANGE CUSTOM DUMPER RESULT DATA: " << data.toString();
3532 GdbMi output = record.data.findChild("consolestreamoutput");
3533 QByteArray out = output.data();
3535 int markerPos = out.indexOf('"') + 1; // position of 'success marker'
3536 if (markerPos == -1 || out.at(markerPos) == 'f') { // 't' or 'f'
3537 // custom dumper produced no output
3538 data.setError(strNotInScope);
3543 out = out.mid(markerPos + 1);
3544 out = out.left(out.lastIndexOf('"'));
3545 out.replace("\\", "");
3546 out = "dummy={" + out + "}";
3549 contents.fromString(out);
3550 //qDebug() << "CONTENTS" << contents.toString(true);
3551 if (!contents.isValid()) {
3552 data.setError(strNotInScope);
3557 setWatchDataType(data, contents.findChild("type"));
3558 setWatchDataValue(data, contents.findChild("value"),
3559 contents.findChild("valueencoded").data().toInt());
3560 setWatchDataAddress(data, contents.findChild("addr"));
3561 setWatchDataChildCount(data, contents.findChild("numchild"));
3562 setWatchDataValueToolTip(data, contents.findChild("valuetooltip"));
3563 setWatchDataValueDisabled(data, contents.findChild("valuedisabled"));
3564 setWatchDataEditValue(data, contents.findChild("editvalue"));
3565 if (qq->watchHandler()->isDisplayedIName(data.iname)) {
3566 GdbMi editvalue = contents.findChild("editvalue");
3567 if (editvalue.isValid()) {
3568 setWatchDataEditValue(data, editvalue);
3569 qq->watchHandler()->showEditValue(data);
3572 if (!qq->watchHandler()->isExpandedIName(data.iname))
3573 data.setChildrenUnneeded();
3574 GdbMi children = contents.findChild("children");
3575 if (children.isValid() || !qq->watchHandler()->isExpandedIName(data.iname))
3576 data.setChildrenUnneeded();
3577 data.setValueUnneeded();
3579 // try not to repeat data too often
3580 WatchData childtemplate;
3581 setWatchDataType(childtemplate, contents.findChild("childtype"));
3582 setWatchDataChildCount(childtemplate, contents.findChild("childnumchild"));
3583 //qDebug() << "DATA: " << data.toString();
3585 foreach (GdbMi item, children.children()) {
3586 WatchData data1 = childtemplate;
3587 data1.name = item.findChild("name").data();
3588 data1.iname = data.iname + "." + data1.name;
3589 if (!data1.name.isEmpty() && data1.name.at(0).isDigit())
3590 data1.name = '[' + data1.name + ']';
3591 QString key = item.findChild("key").data();
3592 if (!key.isEmpty()) {
3593 if (item.findChild("keyencoded").data()[0] == '1') {
3594 key = '"' + QByteArray::fromBase64(key.toUtf8()) + '"';
3595 if (key.size() > 13) {
3600 //data1.name += " (" + key + ")";
3603 setWatchDataType(data1, item.findChild("type"));
3604 setWatchDataExpression(data1, item.findChild("exp"));
3605 setWatchDataChildCount(data1, item.findChild("numchild"));
3606 setWatchDataValue(data1, item.findChild("value"),
3607 item.findChild("valueencoded").data().toInt());
3608 setWatchDataAddress(data1, item.findChild("addr"));
3609 setWatchDataValueToolTip(data1, item.findChild("valuetooltip"));
3610 setWatchDataValueDisabled(data1, item.findChild("valuedisabled"));
3611 if (!qq->watchHandler()->isExpandedIName(data1.iname))
3612 data1.setChildrenUnneeded();
3613 //qDebug() << "HANDLE CUSTOM SUBCONTENTS:" << data1.toString();
3618 void GdbEngine::updateLocals()
3620 m_pendingRequests = 0;
3622 PENDING_DEBUG("\nRESET PENDING");
3623 m_toolTipCache.clear();
3624 m_toolTipExpression.clear();
3625 qq->watchHandler()->reinitializeWatchers();
3627 int level = currentFrame();
3628 // '2' is 'list with type and value'
3629 QString cmd = QString("-stack-list-arguments 2 %1 %2").arg(level).arg(level);
3630 sendSynchronizedCommand(cmd, StackListArguments); // stage 1/2
3631 // '2' is 'list with type and value'
3632 sendSynchronizedCommand("-stack-list-locals 2", StackListLocals); // stage 2/2
3634 //tryLoadCustomDumpers();
3637 void GdbEngine::handleStackListArguments(const GdbResultRecord &record)
3642 // 12^done,stack-args=
3643 // [frame={level="0",args=[
3644 // {name="argc",type="int",value="1"},
3645 // {name="argv",type="char **",value="(char **) 0x7..."}]}]
3647 // 78^done,stack-args=
3648 // {frame={level="0",args={
3650 // {exp="this",value="0x38a2fab0",name="var21",numchild="3",
3651 // type="CurrentDocumentFind * const",typecode="PTR",
3652 // dynamic_type="",in_scope="true",block_start_addr="0x3938e946",
3653 // block_end_addr="0x3938eb2d"},
3655 // {exp="before",value="@0xbfffb9f8: {d = 0x3a7f2a70}",
3656 // name="var22",numchild="1",type="const QString ...} }}}
3658 // In both cases, iterating over the children of stack-args/frame/args
3660 m_currentFunctionArgs.clear();
3661 if (record.resultClass == GdbResultDone) {
3662 const GdbMi list = record.data.findChild("stack-args");
3663 const GdbMi frame = list.findChild("frame");
3664 const GdbMi args = frame.findChild("args");
3665 m_currentFunctionArgs = args.children();
3666 } else if (record.resultClass == GdbResultError) {
3667 qDebug() << "FIXME: GdbEngine::handleStackListArguments: should not happen";
3671 void GdbEngine::handleStackListLocals(const GdbResultRecord &record)
3675 // There could be shadowed variables
3676 QList<GdbMi> locals = record.data.findChild("locals").children();
3677 locals += m_currentFunctionArgs;
3682 void GdbEngine::setLocals(const QList<GdbMi> &locals)
3684 //qDebug() << m_varToType;
3685 QMap<QString, int> seen;
3687 foreach (const GdbMi &item, locals) {
3688 // Local variables of inlined code are reported as
3689 // 26^done,locals={varobj={exp="this",value="",name="var4",exp="this",
3690 // numchild="1",type="const QtSharedPointer::Basic<CPlusPlus::..."
3691 // We do not want these at all. Current hypotheses is that those
3692 // "spurious" locals have _two_ "exp" field. Try to filter them:
3695 foreach (const GdbMi &child, item.children())
3696 numExps += int(child.name() == "exp");
3699 QString name = item.findChild("exp").data();
3701 QString name = item.findChild("name").data();
3703 int n = seen.value(name);
3707 data.iname = "local." + name + QString::number(n + 1);
3708 data.name = name + QString(" <shadowed %1>").arg(n);
3709 //data.setValue("<shadowed>");
3710 setWatchDataValue(data, item.findChild("value"));
3711 data.setType("<shadowed>");
3712 data.setChildCount(0);
3717 data.iname = "local." + name;
3720 data.framekey = m_currentFrame + data.name;
3721 setWatchDataType(data, item.findChild("type"));
3722 // set value only directly if it is simple enough, otherwise
3723 // pass through the insertData() machinery
3724 if (isIntOrFloatType(data.type) || isPointerType(data.type))
3725 setWatchDataValue(data, item.findChild("value"));
3726 if (!qq->watchHandler()->isExpandedIName(data.iname))
3727 data.setChildrenUnneeded();
3728 if (isPointerType(data.type) || data.name == "this")
3729 data.setChildCount(1);
3730 if (0 && m_varToType.contains(data.framekey)) {
3731 qDebug() << "RE-USING " << m_varToType.value(data.framekey);
3732 data.setType(m_varToType.value(data.framekey));
3739 void GdbEngine::insertData(const WatchData &data0)
3741 //qDebug() << "INSERT DATA" << data0.toString();
3742 WatchData data = data0;
3743 if (data.value.startsWith("mi_cmd_var_create:")) {
3744 qDebug() << "BOGUS VALUE: " << data.toString();
3747 qq->watchHandler()->insertData(data);
3750 void GdbEngine::handleTypeContents(const QString &output)
3752 // output.startsWith("type = ") == true
3754 // "type = class QString {"
3755 // "type = class QStringList : public QList<QString> {"
3758 if (output.startsWith("type = class")) {
3759 int posBrace = output.indexOf('{');
3760 QString head = output.mid(13, posBrace - 13 - 1);
3761 int posColon = head.indexOf(": public");
3763 posColon = head.indexOf(": protected");
3765 posColon = head.indexOf(": private");
3766 if (posColon == -1) {
3768 tip = "class " + className + " { ... }";
3770 className = head.left(posColon - 1);
3771 tip = "class " + head + " { ... }";
3773 //qDebug() << "posColon: " << posColon;
3774 //qDebug() << "posBrace: " << posBrace;
3775 //qDebug() << "head: " << head;
3777 className = output.mid(7);
3780 //qDebug() << "output: " << output.left(100) + "...";
3781 //qDebug() << "className: " << className;
3782 //qDebug() << "tip: " << tip;
3783 //m_toolTip.type = className;
3784 m_toolTip.type.clear();
3785 m_toolTip.value = tip;
3788 void GdbEngine::handleVarListChildrenHelper(const GdbMi &item,
3789 const WatchData &parent)
3791 //qDebug() << "VAR_LIST_CHILDREN: PARENT 2" << parent.toString();
3792 //qDebug() << "VAR_LIST_CHILDREN: APPENDEE " << data.toString();
3793 QByteArray exp = item.findChild("exp").data();
3794 QByteArray name = item.findChild("name").data();
3795 if (isAccessSpecifier(exp)) {
3796 // suppress 'private'/'protected'/'public' level
3798 data.variable = name;
3799 data.iname = parent.iname;
3800 //data.iname = data.variable;
3801 data.exp = parent.exp;
3802 data.setTypeUnneeded();
3803 data.setValueUnneeded();
3804 data.setChildCountUnneeded();
3805 data.setChildrenUnneeded();
3806 //qDebug() << "DATA" << data.toString();
3807 QString cmd = "-var-list-children --all-values \"" + data.variable + "\"";
3808 //iname += '.' + exp;
3809 sendSynchronizedCommand(cmd, WatchVarListChildren, QVariant::fromValue(data));
3810 } else if (item.findChild("numchild").data() == "0") {
3811 // happens for structs without data, e.g. interfaces.
3813 data.iname = parent.iname + '.' + exp;
3815 data.variable = name;
3816 setWatchDataType(data, item.findChild("type"));
3817 setWatchDataValue(data, item.findChild("value"));
3818 setWatchDataAddress(data, item.findChild("addr"));
3819 data.setChildCount(0);
3821 } else if (parent.iname.endsWith('.')) {
3822 // Happens with anonymous unions
3825 QString cmd = "-var-list-children --all-values \"" + data.variable + "\"";
3826 sendSynchronizedCommand(cmd, WatchVarListChildren, QVariant::fromValue(data));
3827 } else if (exp == "staticMetaObject") {
3828 // && item.findChild("type").data() == "const QMetaObject")
3829 // FIXME: Namespaces?
3830 // { do nothing } FIXME: make coinfigurable?
3831 // special "clever" hack to avoid clutter in the GUI.
3832 // I am not sure this is a good idea...
3835 data.iname = parent.iname + '.' + exp;
3836 data.variable = name;
3837 setWatchDataType(data, item.findChild("type"));
3838 setWatchDataValue(data, item.findChild("value"));
3839 setWatchDataAddress(data, item.findChild("addr"));
3840 setWatchDataChildCount(data, item.findChild("numchild"));
3841 if (!qq->watchHandler()->isExpandedIName(data.iname))
3842 data.setChildrenUnneeded();
3845 if (isPointerType(parent.type) && data.type == exp) {
3846 data.exp = "*(" + parent.exp + ")";
3847 data.name = "*" + parent.name;
3848 } else if (data.type == exp) {
3849 // A type we derive from? gdb crashes when creating variables here
3850 data.exp = parent.exp;
3851 } else if (exp.startsWith("*")) {
3853 data.exp = "*(" + parent.exp + ")";
3854 } else if (startsWithDigit(exp)) {
3855 // An array. No variables needed?
3856 data.name = "[" + data.name + "]";
3857 data.exp = parent.exp + "[" + exp + "]";
3858 } else if (0 && parent.name.endsWith('.')) {
3859 // Happens with anonymous unions
3860 data.exp = parent.exp + exp;
3861 //data.name = "<anonymous union>";
3862 } else if (exp.isEmpty()) {
3863 // Happens with anonymous unions
3864 data.exp = parent.exp;
3865 data.name = "<n/a>";
3866 data.iname = parent.iname + ".@";
3867 data.type = "<anonymous union>";
3869 // A structure. Hope there's nothing else...
3870 data.exp = parent.exp + '.' + exp;
3873 if (isCustomValueDumperAvailable(data.type)) {
3874 // we do not trust gdb if we have a custom dumper
3875 data.setValueNeeded();
3876 data.setChildCountNeeded();
3879 //qDebug() << "VAR_LIST_CHILDREN: PARENT 3" << parent.toString();
3880 //qDebug() << "VAR_LIST_CHILDREN: APPENDEE " << data.toString();
3885 void GdbEngine::handleVarListChildren(const GdbResultRecord &record,
3886 const WatchData &data0)
3888 //WatchResultCounter dummy(this, WatchVarListChildren);
3889 WatchData data = data0;
3890 if (!data.isValid())
3892 if (record.resultClass == GdbResultDone) {
3893 //qDebug() << "VAR_LIST_CHILDREN: PARENT " << data.toString();
3894 GdbMi children = record.data.findChild("children");
3896 foreach (const GdbMi &child, children.children())
3897 handleVarListChildrenHelper(child, data);
3899 if (!isAccessSpecifier(data.variable.split('.').takeLast())) {
3900 data.setChildrenUnneeded();
3903 } else if (record.resultClass == GdbResultError) {
3904 data.setError(record.data.findChild("msg").data());
3906 data.setError("Unknown error: " + record.toString());
3910 void GdbEngine::handleToolTip(const GdbResultRecord &record,
3911 const QString &what)
3913 //qDebug() << "HANDLE TOOLTIP: " << what << m_toolTip.toString();
3914 // << "record: " << record.toString();
3915 if (record.resultClass == GdbResultError) {
3916 QString msg = record.data.findChild("msg").data();
3917 if (what == "create") {
3918 sendCommand("ptype " + m_toolTip.exp, WatchToolTip, "ptype");
3921 if (what == "evaluate") {
3922 if (msg.startsWith("Cannot look up value of a typedef")) {
3923 m_toolTip.value = m_toolTip.exp + " is a typedef.";
3927 } else if (record.resultClass == GdbResultDone) {
3928 if (what == "create") {
3929 setWatchDataType(m_toolTip, record.data.findChild("type"));
3930 setWatchDataChildCount(m_toolTip, record.data.findChild("numchild"));
3931 if (isCustomValueDumperAvailable(m_toolTip.type))
3932 runCustomDumper(m_toolTip, false);
3934 q->showStatusMessage(tr("Retrieving data for tooltip..."), 10000);
3935 sendCommand("-data-evaluate-expression " + m_toolTip.exp,
3936 WatchToolTip, "evaluate");
3937 //sendToolTipCommand("-var-evaluate-expression tooltip")
3940 if (what == "evaluate") {
3941 m_toolTip.value = m_toolTip.type + ' ' + m_toolTip.exp
3942 + " = " + record.data.findChild("value").data();
3945 if (what == "ptype") {
3946 GdbMi mi = record.data.findChild("consolestreamoutput");
3947 m_toolTip.value = extractTypeFromPTypeOutput(mi.data());
3952 m_toolTip.iname = tooltipIName;
3953 m_toolTip.setChildrenUnneeded();
3954 m_toolTip.setChildCountUnneeded();
3955 insertData(m_toolTip);
3956 qDebug() << "DATA INSERTED";
3957 QTimer::singleShot(0, this, SLOT(updateWatchModel2()));
3958 qDebug() << "HANDLE TOOLTIP END";
3962 void GdbEngine::handleChangedItem(QStandardItem *item)
3964 // HACK: Just store the item for the slot
3965 // handleChangedItem(QWidget *widget) below.
3966 QModelIndex index = item->index().sibling(item->index().row(), 0);
3967 //WatchData data = m_currentSet.takeData(iname);
3968 //m_editedData = inameFromItem(m_model.itemFromIndex(index)).exp;
3969 //qDebug() << "HANDLE CHANGED EXPRESSION: " << m_editedData;
3973 void GdbEngine::assignValueInDebugger(const QString &expression, const QString &value)
3975 sendCommand("-var-delete assign");
3976 sendCommand("-var-create assign * " + expression);
3977 sendCommand("-var-assign assign " + value, WatchVarAssign);
3980 void GdbEngine::tryLoadCustomDumpers()
3982 if (m_dataDumperState != DataDumperUninitialized)
3985 PENDING_DEBUG("TRY LOAD CUSTOM DUMPERS");
3986 m_dataDumperState = DataDumperUnavailable;
3988 #if defined(Q_OS_LINUX)
3989 QString lib = q->m_buildDir + "/qtc-gdbmacros/libgdbmacros.so";
3990 if (QFileInfo(lib).exists()) {
3991 m_dataDumperState = DataDumperLoadTried;
3992 //sendCommand("p dlopen");
3993 QString flag = QString::number(RTLD_NOW);
3994 sendCommand("sharedlibrary libc"); // for malloc
3995 sendCommand("sharedlibrary libdl"); // for dlopen
3996 sendCommand("call (void)dlopen(\"" + lib + "\", " + flag + ")",
3997 WatchDumpCustomSetup);
3998 // some older systems like CentOS 4.6 prefer this:
3999 sendCommand("call (void)__dlopen(\"" + lib + "\", " + flag + ")",
4000 WatchDumpCustomSetup);
4001 sendCommand("sharedlibrary " + dotEscape(lib));
4004 #if defined(Q_OS_MAC)
4005 QString lib = q->m_buildDir + "/qtc-gdbmacros/libgdbmacros.dylib";
4006 if (QFileInfo(lib).exists()) {
4007 m_dataDumperState = DataDumperLoadTried;
4008 //sendCommand("sharedlibrary libc"); // for malloc
4009 //sendCommand("sharedlibrary libdl"); // for dlopen
4010 QString flag = QString::number(RTLD_NOW);
4011 sendCommand("call (void)dlopen(\"" + lib + "\", " + flag + ")",
4012 WatchDumpCustomSetup);
4013 //sendCommand("sharedlibrary " + dotEscape(lib));
4016 #if defined(Q_OS_WIN)
4017 QString lib = q->m_buildDir + "/qtc-gdbmacros/debug/gdbmacros.dll";
4018 if (QFileInfo(lib).exists()) {
4019 m_dataDumperState = DataDumperLoadTried;
4020 sendCommand("sharedlibrary .*"); // for LoadLibraryA
4021 //sendCommand("handle SIGSEGV pass stop print");
4022 //sendCommand("set unwindonsignal off");
4023 sendCommand("call LoadLibraryA(\"" + lib + "\")",
4024 WatchDumpCustomSetup);
4025 sendCommand("sharedlibrary " + dotEscape(lib));
4029 if (m_dataDumperState == DataDumperLoadTried) {
4030 // retreive list of dumpable classes
4031 sendCommand("call qDumpObjectData440(1,%1+1,0,0,0,0,0,0)",
4032 GdbQueryDataDumper1);
4033 sendCommand("p (char*)qDumpOutBuffer", GdbQueryDataDumper2);
4035 gdbOutputAvailable("", QString("DEBUG HELPER LIBRARY IS NOT USABLE: "
4036 " %1 EXISTS: %2, EXECUTABLE: %3").arg(lib)
4037 .arg(QFileInfo(lib).exists())
4038 .arg(QFileInfo(lib).isExecutable()));
4043 IDebuggerEngine *createGdbEngine(DebuggerManager *parent)
4045 return new GdbEngine(parent);