OSDN Git Service

66d4ce19f424b042cd6fba32c1d913a14fca9d83
[qt-creator-jp/qt-creator-jp.git] / src / plugins / debugger / gdbengine.cpp
1 /***************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact:  Qt Software Information (qt-info@nokia.com)
8 **
9 **
10 ** Non-Open Source Usage
11 **
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.
16 **
17 ** GNU General Public License Usage
18 **
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:
24 **
25 ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
26 ** http://www.gnu.org/copyleft/gpl.html.
27 **
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.
31 **
32 ***************************************************************************/
33
34 #include "gdbengine.h"
35
36 #include "debuggerconstants.h"
37 #include "debuggermanager.h"
38 #include "gdbmi.h"
39 #include "procinterrupt.h"
40
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"
48
49 #include "startexternaldialog.h"
50 #include "attachexternaldialog.h"
51
52 #include <utils/qtcassert.h>
53
54 #include <QtCore/QDebug>
55 #include <QtCore/QDir>
56 #include <QtCore/QFileInfo>
57 #include <QtCore/QTime>
58 #include <QtCore/QTimer>
59
60 #include <QtGui/QAction>
61 #include <QtGui/QLabel>
62 #include <QtGui/QMainWindow>
63 #include <QtGui/QMessageBox>
64 #include <QtGui/QToolTip>
65
66 #if defined(Q_OS_LINUX) || defined(Q_OS_MAC)
67 #include <unistd.h>
68 #include <dlfcn.h>
69 #endif
70
71 using namespace Debugger;
72 using namespace Debugger::Internal;
73 using namespace Debugger::Constants;
74
75 Q_DECLARE_METATYPE(Debugger::Internal::GdbMi);
76
77 //#define DEBUG_PENDING  1
78 //#define DEBUG_SUBITEM  1
79
80 #if DEBUG_PENDING
81 #   define PENDING_DEBUG(s) qDebug() << s
82 #else
83 #   define PENDING_DEBUG(s) 
84 #endif
85
86 static const QString tooltipIName = "tooltip";
87
88 ///////////////////////////////////////////////////////////////////////
89 //
90 // GdbCommandType
91 //
92 ///////////////////////////////////////////////////////////////////////
93
94 enum GdbCommandType
95 {
96     GdbInvalidCommand = 0,
97
98     GdbShowVersion = 100,
99     GdbFileExecAndSymbols,
100     GdbQueryPwd,
101     GdbQuerySources,
102     GdbAsyncOutput2,
103     GdbStart,
104     GdbAttached,
105     GdbExecRun,
106     GdbExecRunToFunction,
107     GdbExecStep,
108     GdbExecNext,
109     GdbExecStepI,
110     GdbExecNextI,
111     GdbExecContinue,
112     GdbExecFinish,
113     GdbExecJumpToLine,
114     GdbExecInterrupt,
115     GdbInfoShared,
116     GdbInfoProc,
117     GdbInfoThreads,
118     GdbQueryDataDumper1,
119     GdbQueryDataDumper2,
120     GdbTemporaryContinue,
121
122     BreakCondition = 200,
123     BreakEnablePending,
124     BreakSetAnnotate,
125     BreakDelete,
126     BreakList,
127     BreakIgnore,
128     BreakInfo,
129     BreakInsert,
130     BreakInsert1,
131
132     DisassemblerList = 300,
133
134     ModulesList = 400,
135
136     RegisterListNames = 500,
137     RegisterListValues,
138
139     StackSelectThread = 600,
140     StackListThreads,
141     StackListFrames,
142     StackListLocals,
143     StackListArguments,
144
145     WatchVarAssign = 700,             // data changed by user
146     WatchVarListChildren,
147     WatchVarCreate,
148     WatchEvaluateExpression,
149     WatchToolTip,
150     WatchDumpCustomSetup,
151     WatchDumpCustomValue1,           // waiting for gdb ack
152     WatchDumpCustomValue2,           // waiting for actual data
153     WatchDumpCustomEditValue,
154 };
155
156 QString dotEscape(QString str)
157 {
158     str.replace(' ', '.');
159     str.replace('\\', '.');
160     str.replace('/', '.');
161     return str;
162 }
163
164 QString currentTime()
165 {
166     return QTime::currentTime().toString("hh:mm:ss.zzz");
167 }
168
169 static int &currentToken()
170 {
171     static int token = 0;
172     return token;
173 }
174
175 static bool isSkippableFunction(const QString &funcName, const QString &fileName)
176 {
177     if (fileName.endsWith("kernel/qobject.cpp"))
178         return true;
179     if (fileName.endsWith("kernel/moc_qobject.cpp"))
180         return true;
181     if (fileName.endsWith("kernel/qmetaobject.cpp"))
182         return true;
183     if (fileName.endsWith(".moc"))
184         return true;
185
186     if (funcName.endsWith("::qt_metacall"))
187         return true;
188
189     return false;
190 }
191
192 static bool isLeavableFunction(const QString &funcName, const QString &fileName)
193 {
194     if (funcName.endsWith("QObjectPrivate::setCurrentSender"))
195         return true;
196     if (fileName.endsWith("kernel/qmetaobject.cpp")
197             && funcName.endsWith("QMetaObject::methodOffset"))
198         return true;
199     if (fileName.endsWith("kernel/qobject.h"))
200         return true;
201     if (fileName.endsWith("kernel/qobject.cpp")
202             && funcName.endsWith("QObjectConnectionListVector::at"))
203         return true;
204     if (fileName.endsWith("kernel/qobject.cpp")
205             && funcName.endsWith("~QObject"))
206         return true;
207     if (fileName.endsWith("thread/qmutex.cpp"))
208         return true;
209     if (fileName.endsWith("thread/qthread.cpp"))
210         return true;
211     if (fileName.endsWith("thread/qthread_unix.cpp"))
212         return true;
213     if (fileName.endsWith("thread/qmutex.h"))
214         return true;
215     if (fileName.contains("thread/qbasicatomic"))
216         return true;
217     if (fileName.contains("thread/qorderedmutexlocker_p"))
218         return true;
219     if (fileName.contains("arch/qatomic"))
220         return true;
221     if (fileName.endsWith("tools/qvector.h"))
222         return true;
223     if (fileName.endsWith("tools/qlist.h"))
224         return true;
225     if (fileName.endsWith("tools/qhash.h"))
226         return true;
227     if (fileName.endsWith("tools/qmap.h"))
228         return true;
229     if (fileName.endsWith("tools/qstring.h"))
230         return true;
231     if (fileName.endsWith("global/qglobal.h"))
232         return true;
233
234     return false;
235 }
236
237
238 ///////////////////////////////////////////////////////////////////////
239 //
240 // GdbEngine
241 //
242 ///////////////////////////////////////////////////////////////////////
243
244 GdbEngine::GdbEngine(DebuggerManager *parent)
245 {
246     q = parent;
247     qq = parent->engineInterface();
248     initializeVariables();
249     initializeConnections();
250 }
251
252 GdbEngine::~GdbEngine()
253 {
254     // prevent sending error messages afterwards
255     m_gdbProc.disconnect(this);
256 }
257
258 void GdbEngine::initializeConnections()
259 {
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()));
269
270     // Output
271     connect(&m_outputCollector, SIGNAL(byteDelivery(QByteArray)),
272             SLOT(readDebugeeOutput(QByteArray)));
273
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);
283 }
284
285 void GdbEngine::initializeVariables()
286 {
287     m_dataDumperState = DataDumperUninitialized;
288     m_gdbVersion = 100;
289     m_gdbBuildVersion = -1;
290
291     m_fullToShortName.clear();
292     m_shortToFullName.clear();
293     m_varToType.clear();
294
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();
302 }
303
304 void GdbEngine::gdbProcError(QProcess::ProcessError error)
305 {
306     QString msg;
307     switch (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);
312             break;
313         case QProcess::Crashed:
314             msg = tr("The Gdb process crashed some time after starting "
315                 "successfully.");
316             break;
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.");
321             break;
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.");
326             break;
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.");
330             break;
331         default:
332             msg = tr("An unknown error in the Gdb process occurred. "
333                 "This is the default return value of error().");
334     }
335
336     q->showStatusMessage(msg);
337     QMessageBox::critical(q->mainWindow(), tr("Error"), msg);
338     // act as if it was closed by the core
339     q->exitDebugger();
340 }
341
342 static inline bool isNameChar(char c)
343 {
344     // could be 'stopped' or 'shlibs-added'
345     return (c >= 'a' && c <= 'z') || c == '-';
346 }
347
348 #if 0
349 static void dump(const char *first, const char *middle, const QString & to)
350 {
351     QByteArray ba(first, middle - first);
352     Q_UNUSED(to);
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()));
358     //qDebug() << "";
359     //qDebug() << qPrintable(currentTime())
360     //    << " Reading response:  " << QString(ba).trimmed() << "\n";
361 }
362 #endif
363
364 void GdbEngine::readDebugeeOutput(const QByteArray &data)
365 {
366     emit applicationOutputAvailable(m_outputCodec->toUnicode(
367             data.constData(), data.length(), &m_outputCodecState));
368 }
369
370 void GdbEngine::debugMessage(const QString &msg)
371 {
372     emit gdbOutputAvailable("debug:", msg);
373 }
374
375 void GdbEngine::handleResponse(const QByteArray &buff)
376 {
377     static QTime lastTime;
378
379     emit gdbOutputAvailable("            ", currentTime());
380     emit gdbOutputAvailable("stdout:", buff);
381
382 #if 0
383     qDebug() // << "#### start response handling #### "
384         << currentTime()
385         << lastTime.msecsTo(QTime::currentTime()) << "ms,"
386         << "buf: " << buff.left(1500) << "..."
387         //<< "buf: " << buff
388         << "size:" << buff.size();
389 #else
390     //qDebug() << "buf: " << buff;
391 #endif
392
393     lastTime = QTime::currentTime();
394
395     if (buff.isEmpty() || buff == "(gdb) ")
396         return;
397
398     const char *from = buff.constData();
399     const char *to = from + buff.size();
400     const char *inner;
401
402     int token = -1;
403     // token is a sequence of numbers
404     for (inner = from; inner != to; ++inner)
405         if (*inner < '0' || *inner > '9')
406             break;
407     if (from != inner) {
408         token = QByteArray(from, inner - from).toInt();
409         from = inner;
410         //qDebug() << "found token " << token;
411     }
412
413     // next char decides kind of record
414     const char c = *from++;
415     //qDebug() << "CODE:" << c;
416     switch (c) {
417         case '*':
418         case '+':
419         case '=': {
420             QByteArray asyncClass;
421             for (; from != to; ++from) {
422                 const char c = *from;
423                 if (!isNameChar(c))
424                     break;
425                 asyncClass += *from;
426             }
427             //qDebug() << "ASYNCCLASS" << asyncClass;
428
429             GdbMi record;
430             while (from != to) {
431                 if (*from != ',') {
432                     qDebug() << "MALFORMED ASYNC OUTPUT" << from;
433                     return;
434                 }
435                 ++from; // skip ','
436                 GdbMi data;
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;
442                 }
443             }
444             if (asyncClass == "stopped") {
445                 handleAsyncOutput(record);
446             } else if (asyncClass == "running") {
447                 // Archer has 'thread-id="all"' here
448             #ifdef Q_OS_MAC
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=""}}
458             #endif
459             } else {
460                 qDebug() << "IGNORED ASYNC OUTPUT "
461                     << asyncClass << record.toString();
462             }
463             break;
464         }
465
466         case '~': {
467             m_pendingConsoleStreamOutput += GdbMi::parseCString(from, to);
468             break;
469         }
470
471         case '@': {
472             m_pendingTargetStreamOutput += GdbMi::parseCString(from, to);
473             break;
474         }
475
476         case '&': {
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);
483             break;
484         }
485
486         case '^': {
487             GdbResultRecord record;
488
489             record.token = token;
490
491             for (inner = from; inner != to; ++inner)
492                 if (*inner < 'a' || *inner > 'z')
493                     break;
494
495             QByteArray resultClass(from, inner - from);
496
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;
507             else
508                 record.resultClass = GdbResultUnknown;
509
510             from = inner;
511             if (from != to) {
512                 if (*from != ',') {
513                     qDebug() << "MALFORMED RESULT OUTPUT" << from;
514                     return;
515                 }
516                 ++from;
517                 record.data.parseTuple_helper(from, to);
518                 record.data.m_type = GdbMi::Tuple;
519                 record.data.m_name = "data";
520             }
521
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",
534                     '{' + custom + '}');
535             //m_customOutputForToken.remove(token);
536             m_pendingLogStreamOutput.clear();
537             m_pendingTargetStreamOutput.clear();
538             m_pendingConsoleStreamOutput.clear();
539
540             handleResultRecord(record);
541             break;
542         }
543         default: {
544             qDebug() << "UNKNOWN RESPONSE TYPE" << c;
545             break;
546         }
547     }
548 }
549
550 void GdbEngine::readGdbStandardError()
551 {
552     qWarning() << "Unexpected gdb stderr:" << m_gdbProc.readAllStandardError();
553 }
554
555 void GdbEngine::readGdbStandardOutput()
556 {
557     m_inbuffer.append(m_gdbProc.readAllStandardOutput());
558
559     int newstart = 0;
560     while (newstart < m_inbuffer.size()) {
561         int start = newstart;
562         int end = m_inbuffer.indexOf('\n', start);
563         if (end < 0) {
564             m_inbuffer.remove(0, start);
565             return;
566         }
567         newstart = end + 1;
568         if (end == start)
569             continue;
570         if (m_inbuffer.at(end - 1) == '\r') {
571             --end;
572             if (end == start)
573                 continue;
574         }
575         handleResponse(QByteArray::fromRawData(m_inbuffer.constData() + start, end - start));
576     }
577     m_inbuffer.clear();
578 }
579
580 void GdbEngine::interruptInferior()
581 {
582     qq->notifyInferiorStopRequested();
583     if (m_gdbProc.state() == QProcess::NotRunning) {
584         debugMessage("TRYING TO INTERRUPT INFERIOR WITHOUT RUNNING GDB");
585         qq->notifyInferiorExited();
586         return;
587     }
588
589     if (q->m_attachedPID > 0) {
590         if (!interruptProcess(q->m_attachedPID))
591         //    qq->notifyInferiorStopped();
592         //else
593             debugMessage(QString("CANNOT INTERRUPT %1").arg(q->m_attachedPID));
594         return;
595     }
596
597 #ifdef Q_OS_MAC
598     sendCommand("-exec-interrupt", GdbExecInterrupt);
599     //qq->notifyInferiorStopped();
600 #else
601     if (!interruptChildProcess(m_gdbProc.pid()))
602     //    qq->notifyInferiorStopped();
603     //else
604         debugMessage(QString("CANNOT STOP INFERIOR"));
605 #endif
606 }
607
608 void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
609 {
610     int pid = pid0.toInt();
611     if (pid == 0) {
612         debugMessage(QString("Cannot parse PID from %1").arg(pid0));
613         return;
614     }
615     if (pid == q->m_attachedPID)
616         return;
617     debugMessage(QString("FOUND PID %1").arg(pid));
618     q->m_attachedPID = pid;
619     qq->notifyInferiorPidChanged(pid); 
620 }
621
622 void GdbEngine::sendSynchronizedCommand(const QString & command,
623     int type, const QVariant &cookie, StopNeeded needStop)
624 {
625     sendCommand(command, type, cookie, needStop, Synchronized);
626 }
627
628 void GdbEngine::sendCommand(const QString &command, int type,
629     const QVariant &cookie, StopNeeded needStop, Synchronization synchronized)
630 {
631     if (m_gdbProc.state() == QProcess::NotRunning) {
632         debugMessage("NO GDB PROCESS RUNNING, CMD IGNORED: " + command);
633         return;
634     }
635
636     if (synchronized) {
637         ++m_pendingRequests;
638         PENDING_DEBUG("   TYPE " << type << " INCREMENTS PENDING TO: "
639             << m_pendingRequests << command);
640     } else {
641         PENDING_DEBUG("   UNKNOWN TYPE " << type << " LEAVES PENDING AT: "
642             << m_pendingRequests << command);
643     }
644
645     GdbCookie cmd;
646     cmd.synchronized = synchronized;
647     cmd.command = command;
648     cmd.type = type;
649     cmd.cookie = cookie;
650
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);
659         interruptInferior();
660     } else if (!command.isEmpty()) {
661         ++currentToken();
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());
666
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);
671     }
672 }
673
674 void GdbEngine::handleResultRecord(const GdbResultRecord &record)
675 {
676     //qDebug() << "TOKEN: " << record.token
677     //    << " ACCEPTABLE: " << m_oldestAcceptableToken;
678     //qDebug() << "";
679     //qDebug() << "\nRESULT" << record.token << record.toString();
680
681     int token = record.token;
682     if (token == -1)
683         return;
684
685     GdbCookie cmd = m_cookieForToken.take(token);
686
687     if (record.token < m_oldestAcceptableToken) {
688         //qDebug() << "### SKIPPING OLD RESULT " << record.toString();
689         //QMessageBox::information(m_mainWindow, tr("Skipped"), "xxx");
690         return;
691     }
692
693 #if 0
694     qDebug() << "# handleOutput, "
695         << "cmd type: " << cmd.type
696         << "cmd synchronized: " << cmd.synchronized
697         << "\n record: " << record.toString();
698 #endif
699
700     // << "\n data: " << record.data.toString(true);
701
702     if (cmd.type != GdbInvalidCommand)
703         handleResult(record, cmd.type, cmd.cookie);
704
705     if (cmd.synchronized) {
706         --m_pendingRequests;
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");
711             updateWatchModel2();
712         }
713     } else {
714         PENDING_DEBUG("   UNKNOWN TYPE " << cmd.type << " LEAVES PENDING AT: "
715             << m_pendingRequests << cmd.command);
716     }
717 }
718
719 void GdbEngine::handleResult(const GdbResultRecord & record, int type,
720     const QVariant & cookie)
721 {
722     switch (type) {
723         case GdbExecNext:
724         case GdbExecStep:
725         case GdbExecNextI:
726         case GdbExecStepI:
727         case GdbExecContinue:
728         case GdbExecFinish:
729             // evil code sharing
730             handleExecRun(record);
731             break;
732
733         case GdbStart:
734             handleStart(record);
735             break;
736         case GdbAttached:
737             handleAttach();
738             break;
739         case GdbInfoProc:
740             handleInfoProc(record);
741             break;
742         case GdbInfoThreads:
743             handleInfoThreads(record);
744             break;
745
746         case GdbShowVersion:
747             handleShowVersion(record);
748             break;
749         case GdbFileExecAndSymbols:
750             handleFileExecAndSymbols(record);
751             break;
752         case GdbExecRunToFunction:
753             // that should be "^running". We need to handle the resulting
754             // "Stopped"
755             //handleExecRunToFunction(record);
756             break;
757         case GdbExecInterrupt:
758             qq->notifyInferiorStopped();
759             break;
760         case GdbExecJumpToLine:
761             handleExecJumpToLine(record);
762             break;
763         case GdbQueryPwd:
764             handleQueryPwd(record);
765             break;
766         case GdbQuerySources:
767             handleQuerySources(record);
768             break;
769         case GdbAsyncOutput2:
770             handleAsyncOutput2(cookie.value<GdbMi>());
771             break;
772         case GdbInfoShared:
773             handleInfoShared(record);
774             break;
775         case GdbQueryDataDumper1:
776             handleQueryDataDumper1(record);
777             break;
778         case GdbQueryDataDumper2:
779             handleQueryDataDumper2(record);
780             break;
781         case GdbTemporaryContinue:
782             continueInferior();
783             q->showStatusMessage(tr("Continuing after temporary stop."));
784             break;
785
786         case BreakList:
787             handleBreakList(record);
788             break;
789         case BreakInsert:
790             handleBreakInsert(record, cookie.toInt());
791             break;
792         case BreakInsert1:
793             handleBreakInsert1(record, cookie.toInt());
794             break;
795         case BreakInfo:
796             handleBreakInfo(record, cookie.toInt());
797             break;
798         case BreakEnablePending:
799         case BreakDelete:
800             // nothing
801             break;
802         case BreakIgnore:
803             handleBreakIgnore(record, cookie.toInt());
804             break;
805         case BreakCondition:
806             handleBreakCondition(record, cookie.toInt());
807             break;
808
809         case DisassemblerList:
810             handleDisassemblerList(record, cookie.toString());
811             break;
812
813         case ModulesList:
814             handleModulesList(record);
815             break;
816
817         case RegisterListNames:
818             handleRegisterListNames(record);
819             break;
820         case RegisterListValues:
821             handleRegisterListValues(record);
822             break;
823
824         case StackListFrames:
825             handleStackListFrames(record);
826             break;
827         case StackListThreads:
828             handleStackListThreads(record, cookie.toInt());
829             break;
830         case StackSelectThread:
831             handleStackSelectThread(record, cookie.toInt());
832             break;
833         case StackListLocals:
834             handleStackListLocals(record);
835             break;
836         case StackListArguments:
837             handleStackListArguments(record);
838             break;
839
840         case WatchVarListChildren:
841             handleVarListChildren(record, cookie.value<WatchData>());
842             break;
843         case WatchVarCreate:
844             handleVarCreate(record, cookie.value<WatchData>());
845             break;
846         case WatchVarAssign:
847             handleVarAssign();
848             break;
849         case WatchEvaluateExpression:
850             handleEvaluateExpression(record, cookie.value<WatchData>());
851             break;
852         case WatchToolTip:
853             handleToolTip(record, cookie.toString());
854             break;
855         case WatchDumpCustomValue1:
856             handleDumpCustomValue1(record, cookie.value<WatchData>());
857             break;
858         case WatchDumpCustomValue2:
859             handleDumpCustomValue2(record, cookie.value<WatchData>());
860             break;
861         case WatchDumpCustomSetup:
862             handleDumpCustomSetup(record);
863             break;
864
865         default:
866             debugMessage(QString("FIXME: GdbEngine::handleResult: "
867                 "should not happen %1").arg(type));
868             break;
869     }
870 }
871
872 void GdbEngine::executeDebuggerCommand(const QString &command)
873 {
874     //createGdbProcessIfNeeded();
875     if (m_gdbProc.state() == QProcess::NotRunning) {
876         debugMessage("NO GDB PROCESS RUNNING, PLAIN CMD IGNORED: " + command);
877         return;
878     }
879
880     GdbCookie cmd;
881     cmd.command = command;
882     cmd.type = -1;
883
884     emit gdbInputAvailable(QString(), cmd.command);
885     m_gdbProc.write(cmd.command.toLatin1() + "\r\n");
886 }
887
888 void GdbEngine::handleQueryPwd(const GdbResultRecord &record)
889 {
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) {
895 #ifdef Q_OS_LINUX
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('.'))
903             m_pwd.chop(1);
904 #endif
905 #ifdef Q_OS_WIN
906         // ~"Working directory C:\\Users\\Thomas\\Documents\\WBTest3\\debug.\n"
907         m_pwd = record.data.findChild("consolestreamoutput").data();
908         m_pwd = m_pwd.trimmed();
909 #endif
910         debugMessage("PWD RESULT: " + m_pwd);
911     }
912 }
913
914 void GdbEngine::handleQuerySources(const GdbResultRecord &record)
915 {
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();
927             #ifdef Q_OS_WIN
928             full = QDir::cleanPath(full);
929             #endif
930             if (fullName.isValid() && QFileInfo(full).isReadable()) {
931                 //qDebug() << "STORING 2: " << fileName << full;
932                 m_shortToFullName[fileName] = full;
933                 m_fullToShortName[full] = fileName;
934             }
935         }
936         if (m_shortToFullName != oldShortToFull)
937             qq->sourceFileWindow()->setSourceFiles(m_shortToFullName);
938     }
939 }
940
941 void GdbEngine::handleInfoThreads(const GdbResultRecord &record)
942 {
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));
951     }
952 }
953
954 void GdbEngine::handleInfoProc(const GdbResultRecord &record)
955 {
956     if (record.resultClass == GdbResultDone) {
957         #if defined(Q_OS_MAC)
958         //^done,process-id="85075"
959         maybeHandleInferiorPidChanged(record.data.findChild("process-id").data());
960         #endif
961
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));
968         #endif
969     }
970 }
971
972 void GdbEngine::handleInfoShared(const GdbResultRecord &record)
973 {
974     if (record.resultClass == GdbResultDone) {
975         // let the modules handler do the parsing
976         handleModulesList(record);
977     }
978 }
979
980 void GdbEngine::handleExecJumpToLine(const GdbResultRecord &record)
981 {
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"
987     // ~"242\t x *= 2;"
988     //109^done"
989     qq->notifyInferiorStopped();
990     q->showStatusMessage(tr("Jumped. Stopped."));
991     QString output = record.data.findChild("logstreamoutput").data();
992     if (!output.isEmpty())
993         return;
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);
998 }
999
1000 void GdbEngine::handleExecRunToFunction(const GdbResultRecord &record)
1001 {
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);
1016 }
1017
1018 static bool isExitedReason(const QString &reason)
1019 {
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
1024 }
1025
1026 static bool isStoppedReason(const QString &reason)
1027 {
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")
1035 #ifdef Q_OS_MAC
1036         || reason.isEmpty()
1037 #endif
1038     ;
1039 }
1040
1041 void GdbEngine::handleAqcuiredInferior()
1042 {
1043     #if defined(Q_OS_WIN)
1044     sendCommand("info thread", GdbInfoThreads);
1045     #endif
1046     #if defined(Q_OS_LINUX)
1047     sendCommand("info proc", GdbInfoProc);
1048     #endif
1049     #if defined(Q_OS_MAC)
1050     sendCommand("info pid", GdbInfoProc, QVariant(), NeedsStop);
1051     #endif
1052     reloadSourceFiles();
1053     tryLoadCustomDumpers();
1054
1055 #ifndef Q_OS_MAC
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");
1070     }
1071 #endif
1072     // nicer to see a bit of the world we live in
1073     reloadModules();
1074     attemptBreakpointSynchronization();
1075 }
1076
1077 void GdbEngine::handleAsyncOutput(const GdbMi &data)
1078 {
1079     const QString reason = data.findChild("reason").data();
1080
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();
1093         }
1094         q->showStatusMessage(msg);
1095         // FIXME: shouldn't this use a statis change?
1096         debugMessage("CALLING PARENT EXITDEBUGGER");
1097         q->exitDebugger();
1098         return;
1099     }
1100
1101
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.
1108
1109         qq->notifyInferiorStopped();
1110         m_waitingForFirstBreakpointToBeHit = false;
1111         //
1112         // this will "continue" if done
1113         m_waitingForBreakpointSynchronizationToContinue = true;
1114         //
1115         // that's the "early stop"
1116         handleAqcuiredInferior();
1117         return;
1118     }
1119
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."));
1125         // FIXME: racy
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);
1130         }
1131         sendCommand("p temporaryStop", GdbTemporaryContinue);
1132         m_commandsToRunOnTemporaryBreak.clear();
1133         q->showStatusMessage(tr("Handling queued commands."));
1134         return;
1135     }
1136
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());
1143             continueInferior();
1144             q->showStatusMessage(tr("Loading %1...").arg(QString(data.toString())));
1145             return;
1146         }
1147         m_modulesListOutdated = true;
1148         // fall through
1149     }
1150
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") {
1158         continueInferior();
1159         return;
1160     }
1161
1162     //tryLoadCustomDumpers();
1163
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() + '%';
1172
1173             QString funcName = frame.findChild("func").data();
1174             QString fileName = frame.findChild("file").data();
1175             if (isLeavableFunction(funcName, fileName)) {
1176                 //debugMessage("LEAVING" + funcName);
1177                 ++stepCounter;
1178                 q->stepOutExec();
1179                 //stepExec();
1180                 return;
1181             }
1182             if (isSkippableFunction(funcName, fileName)) {
1183                 //debugMessage("SKIPPING" + funcName);
1184                 ++stepCounter;
1185                 q->stepExec();
1186                 return;
1187             }
1188             //if (stepCounter)
1189             //    qDebug() << "STEPCOUNTER:" << stepCounter;
1190             stepCounter = 0;
1191         }
1192     }
1193
1194     if (isStoppedReason(reason) || reason.isEmpty()) {
1195         if (m_modulesListOutdated) {
1196             reloadModules();
1197             m_modulesListOutdated = false;
1198         }
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() + '%';
1206
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
1212         } else {
1213             q->showStatusMessage(tr("Stopped: \"%1\"").arg(reason));
1214             handleAsyncOutput2(data);
1215         }
1216         return;
1217     }
1218
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
1222 #if 1
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"}
1229     //
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"}
1233     q->resetLocation();
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);
1242 #endif
1243 }
1244
1245
1246 void GdbEngine::handleAsyncOutput2(const GdbMi &data)
1247 {
1248     qq->notifyInferiorStopped();
1249
1250     //
1251     // Stack
1252     //
1253     qq->stackHandler()->setCurrentIndex(0);
1254     updateLocals(); // Quick shot
1255
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);
1260
1261     //
1262     // Disassembler
1263     //
1264     // Linux:
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();
1272
1273     //
1274     // Registers
1275     //
1276     qq->reloadRegisters();
1277 }
1278
1279 void GdbEngine::handleShowVersion(const GdbResultRecord &response)
1280 {
1281     //qDebug () << "VERSION 2:" << response.data.findChild("consolestreamoutput").data();
1282     //qDebug () << "VERSION:" << response.toString();
1283     debugMessage("VERSION:" + response.toString());
1284     if (response.resultClass == GdbResultDone) {
1285         m_gdbVersion = 100;
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)
1293                 list.removeLast();
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.");
1299 #if 0
1300             // ugly, but 'Show again' check box...
1301             static QErrorMessage *err = new QErrorMessage(m_mainWindow);
1302             err->setMinimumSize(400, 300);
1303             err->showMessage(msg);
1304 #else
1305             //QMessageBox::information(m_mainWindow, tr("Warning"), msg);
1306 #endif
1307         } else {
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));
1313         }
1314         //qDebug () << "VERSION 3:" << m_gdbVersion << m_gdbBuildVersion;
1315     }
1316 }
1317
1318 void GdbEngine::handleFileExecAndSymbols
1319     (const GdbResultRecord &response)
1320 {
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();
1329     }
1330 }
1331
1332 void GdbEngine::handleExecRun(const GdbResultRecord &response)
1333 {
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..."));
1343             //stepOutExec();
1344         } else {
1345             QMessageBox::critical(q->mainWindow(), tr("Error"),
1346                 tr("Starting executable failed:\n") + msg);
1347             QTC_ASSERT(q->status() == DebuggerInferiorRunning, /**/);
1348             interruptInferior();
1349         }
1350     }
1351 }
1352
1353 void GdbEngine::queryFullName(const QString &fileName, QString *full)
1354 {
1355     *full = fullName(fileName);
1356 }
1357
1358 QString GdbEngine::shortName(const QString &fullName)
1359 {
1360     return m_fullToShortName.value(fullName, QString());
1361 }
1362
1363 QString GdbEngine::fullName(const QString &fileName)
1364 {
1365     //QString absName = m_manager->currentWorkingDirectory() + "/" + file; ??
1366     if (fileName.isEmpty())
1367         return QString();
1368     QString full = m_shortToFullName.value(fileName, QString());
1369     //debugMessage("RESOLVING: " + fileName + " " +  full);
1370     if (!full.isEmpty())
1371         return full;
1372     QFileInfo fi(fileName);
1373     if (!fi.isReadable())
1374         return QString();
1375     full = fi.absoluteFilePath();
1376     #ifdef Q_OS_WIN
1377     full = QDir::cleanPath(full);
1378     #endif
1379     //debugMessage("STORING: " + fileName + " " + full);
1380     m_shortToFullName[fileName] = full;
1381     m_fullToShortName[full] = fileName;
1382     return full;
1383 }
1384
1385 QString GdbEngine::fullName(const QStringList &candidates)
1386 {
1387     QString full;
1388     foreach (const QString &fileName, candidates) {
1389         full = fullName(fileName);
1390         if (!full.isEmpty())
1391             return full;
1392     }
1393     foreach (const QString &fileName, candidates) {
1394         if (!fileName.isEmpty())
1395             return fileName;
1396     }
1397     return full;
1398 }
1399
1400 void GdbEngine::shutdown()
1401 {
1402     exitDebugger();
1403 }
1404
1405 void GdbEngine::exitDebugger()
1406 {
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();
1412     }
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();
1421         }
1422         if (q->startMode() == DebuggerManager::AttachExternal)
1423             sendCommand("detach");
1424         else
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);
1434         }
1435     }
1436     if (m_gdbProc.state() != QProcess::NotRunning)
1437         debugMessage("PROBLEM STOPPING DEBUGGER");
1438
1439     m_outputCollector.shutdown();
1440     initializeVariables();
1441     //q->settings()->m_debugDumpers = false;
1442 }
1443
1444
1445 int GdbEngine::currentFrame() const
1446 {
1447     return qq->stackHandler()->currentIndex();
1448 }
1449
1450
1451 bool GdbEngine::startDebugger()
1452 {
1453     QStringList gdbArgs;
1454
1455     QFileInfo fi(q->m_executable);
1456     QString fileName = '"' + fi.absoluteFilePath() + '"';
1457
1458     if (m_gdbProc.state() != QProcess::NotRunning) {
1459         debugMessage("GDB IS ALREADY RUNNING!");
1460         return false;
1461     }
1462
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()));
1467         return false;
1468     }
1469
1470     gdbArgs.prepend(QLatin1String("--tty=") + m_outputCollector.serverName());
1471
1472     //gdbArgs.prepend(QLatin1String("--quiet"));
1473     gdbArgs.prepend(QLatin1String("mi"));
1474     gdbArgs.prepend(QLatin1String("-i"));
1475
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);
1480
1481     #if 0
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;
1489     #endif
1490
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();
1497         return false;
1498     }
1499
1500     q->showStatusMessage(tr("Gdb Running"));
1501
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);
1519
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");
1527
1528     // From the docs:
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");
1538
1539     // This is useful to kill the inferior whenever gdb dies.
1540     //sendCommand("handle SIGTERM pass nostop print");
1541
1542     sendCommand("set unwindonsignal on");
1543     sendCommand("pwd", GdbQueryPwd);
1544     sendCommand("set width 0");
1545     sendCommand("set height 0");
1546
1547     #ifdef Q_OS_MAC
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");
1558     #endif
1559
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);
1565         } else {
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));
1572         }
1573     }
1574
1575     if (q->startMode() == DebuggerManager::AttachExternal) {
1576         sendCommand("attach " + QString::number(q->m_attachedPID), GdbAttached);
1577     } else {
1578         // StartInternal or StartExternal
1579         sendCommand("-file-exec-and-symbols " + fileName, GdbFileExecAndSymbols);
1580         //sendCommand("file " + fileName, GdbFileExecAndSymbols);
1581         #ifdef Q_OS_MAC
1582         sendCommand("sharedlibrary apply-load-rules all");
1583         #endif
1584         if (!q->m_processArgs.isEmpty())
1585             sendCommand("-exec-arguments " + q->m_processArgs.join(" "));
1586         #ifndef Q_OS_MAC
1587         sendCommand("set auto-solib-add off");
1588         sendCommand("info target", GdbStart);
1589         #else
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");
1595         #endif
1596     }
1597
1598     // set all to "pending"
1599     if (q->startMode() == DebuggerManager::AttachExternal)
1600         qq->breakHandler()->removeAllBreakpoints();
1601     else
1602         qq->breakHandler()->setAllPending();
1603
1604     return true;
1605 }
1606
1607 void GdbEngine::continueInferior()
1608 {
1609     q->resetLocation();
1610     setTokenBarrier();
1611     qq->notifyInferiorRunningRequested();
1612     sendCommand("-exec-continue", GdbExecContinue);
1613 }
1614
1615 void GdbEngine::handleStart(const GdbResultRecord &response)
1616 {
1617 #ifdef Q_OS_MAC
1618     Q_UNUSED(response);
1619 #else
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");
1632         } else {
1633             debugMessage("PARSING START ADDRESS FAILED: " + msg);
1634         }
1635     } else if (response.resultClass == GdbResultError) {
1636         debugMessage("FETCHING START ADDRESS FAILED: " + response.toString());
1637     }
1638 #endif
1639 }
1640
1641 void GdbEngine::handleAttach()
1642 {
1643     qq->notifyInferiorStopped();
1644     q->showStatusMessage(tr("Attached to running process. Stopped."));
1645     handleAqcuiredInferior();
1646
1647     q->resetLocation();
1648
1649     //
1650     // Stack
1651     //
1652     qq->stackHandler()->setCurrentIndex(0);
1653     updateLocals(); // Quick shot
1654
1655     sendSynchronizedCommand("-stack-list-frames", StackListFrames);
1656     if (supportsThreads())
1657         sendSynchronizedCommand("-thread-list-ids", StackListThreads, 0);
1658
1659     //
1660     // Disassembler
1661     //
1662     // XXX we have no data here ...
1663     //m_address = data.findChild("frame").findChild("addr").data();
1664     //qq->reloadDisassembler();
1665
1666     //
1667     // Registers
1668     //
1669     qq->reloadRegisters();
1670 }
1671
1672 void GdbEngine::stepExec()
1673 {
1674     setTokenBarrier();
1675     qq->notifyInferiorRunningRequested();
1676     sendCommand("-exec-step", GdbExecStep);
1677 }
1678
1679 void GdbEngine::stepIExec()
1680 {
1681     setTokenBarrier();
1682     qq->notifyInferiorRunningRequested();
1683     sendCommand("-exec-step-instruction", GdbExecStepI);
1684 }
1685
1686 void GdbEngine::stepOutExec()
1687 {
1688     setTokenBarrier();
1689     qq->notifyInferiorRunningRequested();
1690     sendCommand("-exec-finish", GdbExecFinish);
1691 }
1692
1693 void GdbEngine::nextExec()
1694 {
1695     setTokenBarrier();
1696     qq->notifyInferiorRunningRequested();
1697     sendCommand("-exec-next", GdbExecNext);
1698 }
1699
1700 void GdbEngine::nextIExec()
1701 {
1702     setTokenBarrier();
1703     qq->notifyInferiorRunningRequested();
1704     sendCommand("-exec-next-instruction", GdbExecNextI);
1705 }
1706
1707 void GdbEngine::runToLineExec(const QString &fileName, int lineNumber)
1708 {
1709     setTokenBarrier();
1710     qq->notifyInferiorRunningRequested();
1711     sendCommand("-exec-until " + fileName + ":" + QString::number(lineNumber));
1712 }
1713
1714 void GdbEngine::runToFunctionExec(const QString &functionName)
1715 {
1716     setTokenBarrier();
1717     sendCommand("-break-insert -t " + functionName);
1718     qq->notifyInferiorRunningRequested();
1719     sendCommand("-exec-continue", GdbExecRunToFunction);
1720 }
1721
1722 void GdbEngine::jumpToLineExec(const QString &fileName, int lineNumber)
1723 {
1724 #if 1
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"
1733     //  ~"242\t x *= 2;"
1734     //  23^done"
1735     q->gotoLocation(fileName, lineNumber, true);
1736     //setBreakpoint();
1737     //sendCommand("jump " + fileName + ":" + QString::number(lineNumber));
1738 #else
1739     q->gotoLocation(fileName, lineNumber, true);
1740     setBreakpoint(fileName, lineNumber);
1741     sendCommand("jump " + fileName + ":" + QString::number(lineNumber));
1742 #endif
1743 }
1744
1745 /*!
1746     \fn void GdbEngine::setTokenBarrier()
1747     \brief Discard the results of all pending watch-updating commands.
1748
1749     This method is called at the beginning of all step/next/finish etc.
1750     debugger functions.
1751     If non-watch-updating commands with call-backs are still in the pipe,
1752     it will complain.
1753 */
1754
1755 void GdbEngine::setTokenBarrier()
1756 {
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();
1761 }
1762
1763 void GdbEngine::setDebugDumpers(bool on)
1764 {
1765     if (on) {
1766         debugMessage("SWITCHING ON DUMPER DEBUGGING");
1767         sendCommand("set unwindonsignal off");
1768         q->breakByFunction("qDumpObjectData440");
1769         //updateLocals();
1770     } else {
1771         debugMessage("SWITCHING OFF DUMPER DEBUGGING");
1772         sendCommand("set unwindonsignal on");
1773     }
1774 }
1775
1776
1777 //////////////////////////////////////////////////////////////////////
1778 //
1779 // Breakpoint specific stuff
1780 //
1781 //////////////////////////////////////////////////////////////////////
1782
1783 void GdbEngine::breakpointDataFromOutput(BreakpointData *data, const GdbMi &bkpt)
1784 {
1785     if (!bkpt.isValid())
1786         return;
1787     if (!data)
1788         return;
1789     data->pending = false;
1790     data->bpMultiple = false;
1791     data->bpCondition.clear();
1792     QStringList files;
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;
1803             else
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();
1809             #ifdef Q_OS_WIN
1810             fullName = QDir::cleanPath(fullName);
1811             #endif
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;
1822         }
1823         else if (child.hasName("pending")) {
1824             data->pending = true;
1825             int pos = child.data().lastIndexOf(':');
1826             if (pos > 0) {
1827                 data->bpLineNumber = child.data().mid(pos + 1);
1828                 data->markerLineNumber = child.data().mid(pos + 1).toInt();
1829                 files.prepend(child.data().left(pos));
1830             } else {
1831                 files.prepend(child.data());
1832             }
1833         }
1834     }
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();
1839
1840     QString name = fullName(files);
1841     if (data->bpFileName.isEmpty())
1842         data->bpFileName = name;
1843     if (data->markerFileName.isEmpty())
1844         data->markerFileName = name;
1845 }
1846
1847 void GdbEngine::sendInsertBreakpoint(int index)
1848 {
1849     const BreakpointData *data = qq->breakHandler()->at(index);
1850     QString where;
1851     if (data->funcName.isEmpty()) {
1852         where = data->fileName;
1853 #ifdef Q_OS_MAC
1854         // full names do not work on Mac/MI
1855         QFileInfo fi(data->fileName);
1856         where = fi.fileName();
1857         //where = fi.absoluteFilePath();
1858 #endif
1859 #ifdef Q_OS_WIN
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;
1866 #endif
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 + "\"";
1870     } else {
1871         where = data->funcName;
1872     }
1873
1874     // set up fallback in case of pending breakpoints which aren't handled
1875     // by the MI interface
1876 #ifdef Q_OS_LINUX
1877     QString cmd = "-break-insert ";
1878     //if (!data->condition.isEmpty())
1879     //    cmd += "-c " + data->condition + " ";
1880     cmd += where;
1881 #endif
1882 #ifdef Q_OS_MAC
1883     QString cmd = "-break-insert -l -1 ";
1884     //if (!data->condition.isEmpty())
1885     //    cmd += "-c " + data->condition + " ";
1886     cmd += where;
1887 #endif
1888 #ifdef Q_OS_WIN
1889     QString cmd = "-break-insert ";
1890     //if (!data->condition.isEmpty())
1891     //    cmd += "-c " + data->condition + " ";
1892     cmd += where;
1893 #endif
1894     debugMessage(QString("Current state: %1").arg(q->status()));
1895     sendCommand(cmd, BreakInsert, index, NeedsStop);
1896 }
1897
1898 void GdbEngine::handleBreakList(const GdbResultRecord &record)
1899 {
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"}] ... }
1908
1909     if (record.resultClass == GdbResultDone) {
1910         GdbMi table = record.data.findChild("BreakpointTable");
1911         if (table.isValid())
1912             handleBreakList(table);
1913     }
1914 }
1915
1916 void GdbEngine::handleBreakList(const GdbMi &table)
1917 {
1918     //qDebug() << "GdbEngine::handleOutput: table: "
1919     //  << table.toString();
1920     GdbMi body = table.findChild("body");
1921     //qDebug() << "GdbEngine::handleOutput: body: "
1922     //  << body.toString();
1923     QList<GdbMi> bkpts;
1924     if (body.isValid()) {
1925         // Non-Mac
1926         bkpts = body.children();
1927     } else {
1928         // Mac
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();
1934             if (num <= 0) {
1935                 //qDebug() << "REMOVING " << i << bkpts.at(i).toString();
1936                 bkpts.removeAt(i);
1937             }
1938         }
1939         //qDebug() << "LEFT " << bkpts.size() << " BREAKPOINTS";
1940     }
1941
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);
1947         if (found != -1)
1948             breakpointDataFromOutput(handler->at(found), bkpts.at(index));
1949         //else
1950             //qDebug() << "CANNOT HANDLE RESPONSE " << bkpts.at(index).toString();
1951     }
1952
1953     attemptBreakpointSynchronization();
1954     handler->updateMarkers();
1955 }
1956
1957
1958 void GdbEngine::handleBreakIgnore(const GdbResultRecord &record, int index)
1959 {
1960     // gdb 6.8:
1961     // ignore 2 0:
1962     // ~"Will stop next time breakpoint 2 is reached.\n"
1963     // 28^done
1964     // ignore 2 12:
1965     // &"ignore 2 12\n"
1966     // ~"Will ignore next 12 crossings of breakpoint 2.\n"
1967     // 29^done
1968     //
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;
1978         //}
1979         // FIXME: this assumes it is doing the right thing...
1980         data->bpIgnoreCount = data->ignoreCount;
1981         attemptBreakpointSynchronization();
1982         handler->updateMarkers();
1983     }
1984 }
1985
1986 void GdbEngine::handleBreakCondition(const GdbResultRecord &record, int index)
1987 {
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();
1999         // happens on Mac
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();
2007         }
2008     }
2009 }
2010
2011 void GdbEngine::handleBreakInsert(const GdbResultRecord &record, int index)
2012 {
2013     BreakHandler *handler = qq->breakHandler();
2014     if (record.resultClass == GdbResultDone) {
2015         //qDebug() << "HANDLE BREAK INSERT " << index;
2016 //#ifdef Q_OS_MAC
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);
2022 //#endif
2023         attemptBreakpointSynchronization();
2024         handler->updateMarkers();
2025     } else if (record.resultClass == GdbResultError) {
2026         const BreakpointData *data = handler->at(index);
2027 #ifdef Q_OS_LINUX
2028         //QString where = "\"\\\"" + data->fileName + "\\\":"
2029         //    + data->lineNumber + "\"";
2030         QString where = "\"" + data->fileName + "\":"
2031             + data->lineNumber;
2032         sendCommand("break " + where, BreakInsert1, index);
2033 #endif
2034 #ifdef Q_OS_MAC
2035         QFileInfo fi(data->fileName);
2036         QString where = "\"" + fi.fileName() + "\":"
2037             + data->lineNumber;
2038         sendCommand("break " + where, BreakInsert1, index);
2039 #endif
2040 #ifdef Q_OS_WIN
2041         QFileInfo fi(data->fileName);
2042         QString where = "\"" + fi.fileName() + "\":"
2043             + data->lineNumber;
2044         //QString where = m_data->fileName + QLatin1Char(':') + data->lineNumber;
2045         sendCommand("break " + where, BreakInsert1, index);
2046 #endif
2047     }
2048 }
2049
2050 void GdbEngine::extractDataFromInfoBreak(const QString &output, BreakpointData *data)
2051 {
2052     data->bpFileName = "<MULTIPLE>";
2053
2054     //qDebug() << output;
2055     if (output.isEmpty())
2056         return;
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
2061     // - or - 
2062     // everything on a single line on Windows for constructors of classes
2063     // within namespaces.
2064     // Sometimes the path is relative too.
2065   
2066     QRegExp re("MULTIPLE.*(0x[0-9a-f]+) in (.*)\\s+at (.*):([\\d]+)([^\\d]|$)");
2067     re.setMinimal(true);
2068
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";
2080     } else {
2081         qDebug() << "COULD NOT MATCH " << re.pattern() << " AND " << output;
2082         data->bpNumber = "<unavailable>";
2083     }
2084 }
2085
2086 void GdbEngine::handleBreakInfo(const GdbResultRecord &record, int bpNumber)
2087 {
2088     BreakHandler *handler = qq->breakHandler();
2089     if (record.resultClass == GdbResultDone) {
2090         // Old-style output for multiple breakpoints, presumably in a
2091         // constructor
2092         int found = handler->findBreakpoint(bpNumber);
2093         if (found != -1) {
2094             QString str = record.data.findChild("consolestreamoutput").data();
2095             extractDataFromInfoBreak(str, handler->at(found));
2096             handler->updateMarkers();
2097             attemptBreakpointSynchronization(); // trigger "ready"
2098         }
2099     }
2100 }
2101
2102 void GdbEngine::handleBreakInsert1(const GdbResultRecord &record, int index)
2103 {
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();
2118     }
2119 }
2120
2121 void GdbEngine::attemptBreakpointSynchronization()
2122 {
2123     // Non-lethal check for nested calls
2124     static bool inBreakpointSychronization = false;
2125     QTC_ASSERT(!inBreakpointSychronization, /**/);
2126     inBreakpointSychronization = true;
2127
2128     BreakHandler *handler = qq->breakHandler();
2129
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(),
2136                 NeedsStop);
2137         delete data;
2138     }
2139
2140     bool updateNeeded = false;
2141
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;
2149             break;
2150         }
2151     }
2152
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;
2161             break;
2162         }
2163     }
2164
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;
2176                 break;
2177             }
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;
2183                 break;
2184             }
2185         }
2186     }
2187
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();
2194         }
2195     }
2196
2197     if (!updateNeeded && m_waitingForBreakpointSynchronizationToContinue) {
2198         m_waitingForBreakpointSynchronizationToContinue = false;
2199         // we continue the execution
2200         continueInferior();
2201     }
2202
2203     inBreakpointSychronization = false;
2204 }
2205
2206
2207 //////////////////////////////////////////////////////////////////////
2208 //
2209 // Disassembler specific stuff
2210 //
2211 //////////////////////////////////////////////////////////////////////
2212
2213 void GdbEngine::reloadDisassembler()
2214 {
2215     emit sendCommand("disassemble", DisassemblerList, m_address);
2216 }
2217
2218 void GdbEngine::handleDisassemblerList(const GdbResultRecord &record,
2219     const QString &cookie)
2220 {
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>:
2231             //    pushq  $0x4d
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;
2239                 continue;
2240             }
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;
2251
2252             if (line.address == cookie)
2253                 currentLine = lines.size();
2254
2255             lines.append(line);
2256         }
2257     } else {
2258         DisassemblerLine line;
2259         line.addressDisplay = tr("<could not retreive module information>");
2260         lines.append(line);
2261     }
2262
2263     qq->disassemblerHandler()->setLines(lines);
2264     if (currentLine != -1)
2265         qq->disassemblerHandler()->setCurrentLine(currentLine);
2266 }
2267
2268
2269 //////////////////////////////////////////////////////////////////////
2270 //
2271 // Modules specific stuff
2272 //
2273 //////////////////////////////////////////////////////////////////////
2274
2275 void GdbEngine::loadSymbols(const QString &moduleName)
2276 {
2277     // FIXME: gdb does not understand quoted names here (tested with 6.8)
2278     sendCommand("sharedlibrary " + dotEscape(moduleName));
2279     reloadModules();
2280 }
2281
2282 void GdbEngine::loadAllSymbols()
2283 {
2284     sendCommand("sharedlibrary .*");
2285     reloadModules();
2286 }
2287
2288 void GdbEngine::reloadModules()
2289 {
2290     sendCommand("info shared", ModulesList, QVariant());
2291 }
2292
2293 void GdbEngine::handleModulesList(const GdbResultRecord &record)
2294 {
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"))
2302                 continue;
2303             Module module;
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);
2310         }
2311     }
2312     qq->modulesHandler()->setModules(modules);
2313 }
2314
2315
2316 //////////////////////////////////////////////////////////////////////
2317 //
2318 // Source files specific stuff
2319 //
2320 //////////////////////////////////////////////////////////////////////
2321
2322 void GdbEngine::reloadSourceFiles()
2323 {
2324     sendCommand("-file-list-exec-source-files", GdbQuerySources);
2325 }
2326
2327
2328 //////////////////////////////////////////////////////////////////////
2329 //
2330 // Stack specific stuff
2331 //
2332 //////////////////////////////////////////////////////////////////////
2333
2334 void GdbEngine::handleStackSelectThread(const GdbResultRecord &record, int)
2335 {
2336     Q_UNUSED(record);
2337     //qDebug("FIXME: StackHandler::handleOutput: SelectThread");
2338     q->showStatusMessage(tr("Retrieving data for stack view..."), 3000);
2339     sendCommand("-stack-list-frames", StackListFrames);
2340 }
2341
2342
2343 void GdbEngine::handleStackListFrames(const GdbResultRecord &record)
2344 {
2345     QList<StackFrame> stackFrames;
2346
2347     const GdbMi stack = record.data.findChild("stack");
2348     QString dummy = stack.toString();
2349     if (!stack.isValid()) {
2350         qDebug() << "FIXME: stack: " << stack.toString();
2351         return;
2352     }
2353
2354     int topFrame = -1;
2355
2356     for (int i = 0; i != stack.childCount(); ++i) {
2357         //qDebug() << "HANDLING FRAME: " << stack.childAt(i).toString();
2358         const GdbMi frameMi = stack.childAt(i);
2359         StackFrame frame;
2360         frame.level = i;
2361         QStringList files;
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();
2369
2370         stackFrames.append(frame);
2371
2372 #ifdef Q_OS_WIN
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);
2379
2380         // immediately leave bogus frames
2381         if (topFrame == -1 && isBogus) {
2382             sendCommand("-exec-finish");
2383             return;
2384         }
2385
2386 #endif
2387
2388         // Initialize top frame to the first valid frame
2389         const bool isValid = !frame.file.isEmpty() && !frame.function.isEmpty();
2390         if (isValid && topFrame == -1)
2391             topFrame = i;
2392     }
2393
2394     qq->stackHandler()->setFrames(stackFrames);
2395
2396 #if 0
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();
2401         if (usable)
2402             q->gotoLocation(frame.file, frame.line, true);
2403         else
2404             qDebug() << "FULL NAME NOT USABLE 0: " << frame.file;
2405     } else {
2406         activateFrame(topFrame);
2407     }
2408 #else
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();
2413         if (usable)
2414             q->gotoLocation(frame.file, frame.line, true);
2415         else
2416             qDebug() << "FULL NAME NOT USABLE 0: " << frame.file << topFrame;
2417     }
2418 #endif
2419 }
2420
2421 void GdbEngine::selectThread(int index)
2422 {
2423     //reset location arrow
2424     q->resetLocation();
2425
2426     ThreadsHandler *threadsHandler = qq->threadsHandler();
2427     threadsHandler->setCurrentThread(index);
2428
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),
2434         StackSelectThread);
2435 }
2436
2437 void GdbEngine::activateFrame(int frameIndex)
2438 {
2439     if (q->status() != DebuggerInferiorStopped)
2440         return;
2441
2442     StackHandler *stackHandler = qq->stackHandler();
2443     int oldIndex = stackHandler->currentIndex();
2444     //qDebug() << "ACTIVATE FRAME: " << frameIndex << oldIndex
2445     //    << stackHandler->currentIndex();
2446
2447     QTC_ASSERT(frameIndex < stackHandler->stackSize(), return);
2448
2449     if (oldIndex != frameIndex) {
2450         setTokenBarrier();
2451
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));
2456
2457         stackHandler->setCurrentIndex(frameIndex);
2458         updateLocals();
2459     }
2460
2461     const StackFrame &frame = stackHandler->currentFrame();
2462
2463     bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable();
2464     if (usable)
2465         q->gotoLocation(frame.file, frame.line, true);
2466     else
2467         qDebug() << "FULL NAME NOT USABLE: " << frame.file;
2468 }
2469
2470 void GdbEngine::handleStackListThreads(const GdbResultRecord &record, int id)
2471 {
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) {
2477         ThreadData thread;
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;
2483         }
2484     }
2485     ThreadsHandler *threadsHandler = qq->threadsHandler();
2486     threadsHandler->setThreads(threads);
2487     threadsHandler->setCurrentThread(currentIndex);
2488 }
2489
2490
2491 //////////////////////////////////////////////////////////////////////
2492 //
2493 // Register specific stuff
2494 //
2495 //////////////////////////////////////////////////////////////////////
2496
2497 void GdbEngine::reloadRegisters()
2498 {
2499     QString format = qq->registerHandler()->model()->property(PROPERTY_REGISTER_FORMAT).toString();
2500     sendCommand("-data-list-register-values " + format, RegisterListValues);
2501 }
2502
2503 void GdbEngine::handleRegisterListNames(const GdbResultRecord &record)
2504 {
2505     if (record.resultClass != GdbResultDone)
2506         return;
2507
2508     QList<Register> registers;
2509     foreach (const GdbMi &item, record.data.findChild("register-names").children())
2510         registers.append(Register(item.data()));
2511
2512     qq->registerHandler()->setRegisters(registers);
2513 }
2514
2515 void GdbEngine::handleRegisterListValues(const GdbResultRecord &record)
2516 {
2517     if (record.resultClass != GdbResultDone)
2518         return;
2519
2520     QList<Register> registers = qq->registerHandler()->registers();
2521
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 &reg = registers[index];
2527             QString value = item.findChild("value").data();
2528             reg.changed = (value != reg.value);
2529             if (reg.changed)
2530                 reg.value = value;
2531         }
2532     }
2533     qq->registerHandler()->setRegisters(registers);
2534 }
2535
2536
2537 //////////////////////////////////////////////////////////////////////
2538 //
2539 // Thread specific stuff
2540 //
2541 //////////////////////////////////////////////////////////////////////
2542
2543 bool GdbEngine::supportsThreads() const
2544 {
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;
2548 }
2549
2550 //////////////////////////////////////////////////////////////////////
2551 //
2552 // Tooltip specific stuff
2553 //
2554 //////////////////////////////////////////////////////////////////////
2555
2556 static WatchData m_toolTip;
2557 static QString m_toolTipExpression;
2558 static QPoint m_toolTipPos;
2559 static QMap<QString, WatchData> m_toolTipCache;
2560
2561 static bool hasLetterOrNumber(const QString &exp)
2562 {
2563     for (int i = exp.size(); --i >= 0; )
2564         if (exp[i].isLetterOrNumber() || exp[i] == '_')
2565             return true;
2566     return false;
2567 }
2568
2569 static bool hasSideEffects(const QString &exp)
2570 {
2571     // FIXME: complete?
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("++");
2581 }
2582
2583 static bool isKeyWord(const QString &exp)
2584 {
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");
2596 }
2597
2598 void GdbEngine::setToolTipExpression(const QPoint &pos, const QString &exp0)
2599 {
2600     //qDebug() << "SET TOOLTIP EXP" << pos << exp0;
2601     if (q->status() != DebuggerInferiorStopped) {
2602         //qDebug() << "SUPPRESSING DEBUGGER TOOLTIP, INFERIOR NOT STOPPED";
2603         return;
2604     }
2605     
2606     if (q->settings()->m_debugDumpers) {
2607         // minimize interference
2608         return;
2609     }
2610
2611     m_toolTipPos = pos;
2612     m_toolTipExpression = exp0;
2613     QString exp = exp0;
2614 /*
2615     if (m_toolTip.isTypePending()) {
2616         qDebug() << "suppressing duplicated tooltip creation";
2617         return;
2618     }
2619 */
2620     if (m_toolTipCache.contains(exp)) {
2621         const WatchData & data = m_toolTipCache[exp];
2622         // FIXME: qq->watchHandler()->collapseChildren(data.iname);
2623         insertData(data);
2624         return;
2625     }
2626
2627     QToolTip::hideText();
2628     if (exp.isEmpty() || exp.startsWith("#"))  {
2629         QToolTip::hideText();
2630         return;
2631     }
2632
2633     if (!hasLetterOrNumber(exp)) {
2634         QToolTip::showText(m_toolTipPos,
2635             "'" + exp + "' contains no identifier");
2636         return;
2637     }
2638
2639     if (isKeyWord(exp))
2640         return;
2641
2642     if (exp.startsWith('"') && exp.endsWith('"'))  {
2643         QToolTip::showText(m_toolTipPos, "String literal " + exp);
2644         return;
2645     }
2646
2647     if (exp.startsWith("++") || exp.startsWith("--"))
2648         exp = exp.mid(2);
2649
2650     if (exp.endsWith("++") || exp.endsWith("--"))
2651         exp = exp.mid(2);
2652
2653     if (exp.startsWith("<") || exp.startsWith("["))
2654         return;
2655
2656     if (hasSideEffects(exp)) {
2657         QToolTip::showText(m_toolTipPos,
2658             "Cowardly refusing to evaluate expression '" + exp
2659                 + "' with potential side effects");
2660         return;
2661     }
2662
2663     // Gdb crashes when creating a variable object with the name
2664     // of the type of 'this'
2665 /*
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";
2673                 return;
2674             }
2675             break;
2676         }
2677     }
2678 */
2679
2680     //if (m_manager->status() != DebuggerInferiorStopped)
2681     //    return;
2682
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();
2693 }
2694
2695
2696 //////////////////////////////////////////////////////////////////////
2697 //
2698 // Watch specific stuff
2699 //
2700 //////////////////////////////////////////////////////////////////////
2701
2702 static const QString strNotInScope = QLatin1String("<not in scope>");
2703
2704 static bool isPointerType(const QString &type)
2705 {
2706     return type.endsWith("*") || type.endsWith("* const");
2707 }
2708
2709 static bool isAccessSpecifier(const QString &str)
2710 {
2711     static const QStringList items =
2712         QStringList() << "private" << "protected" << "public";
2713     return items.contains(str);
2714 }
2715
2716 static bool startsWithDigit(const QString &str)
2717 {
2718     return !str.isEmpty() && str[0] >= '0' && str[0] <= '9';
2719 }
2720
2721 QString stripPointerType(QString type)
2722 {
2723     if (type.endsWith("*"))
2724         type.chop(1);
2725     if (type.endsWith("* const"))
2726         type.chop(7);
2727     if (type.endsWith(' '))
2728         type.chop(1);
2729     return type;
2730 }
2731
2732 static QString gdbQuoteTypes(const QString &type)
2733 {
2734     // gdb does not understand sizeof(Core::IFile*).
2735     // "sizeof('Core::IFile*')" is also not acceptable,
2736     // it needs to be "sizeof('Core::IFile'*)"
2737     //
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.
2743     //return type;
2744
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)) + "*";
2749
2750     QString accu;
2751     QString result;
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 == ' ') {
2756             accu += c;
2757         } else if (c == '<') {
2758             ++templateLevel;
2759             accu += c;
2760         } else if (c == '<') {
2761             --templateLevel;
2762             accu += c;
2763         } else if (templateLevel > 0) {
2764             accu += c;
2765         } else {
2766             if (accu.contains(':') || accu.contains('<'))
2767                 result += '\'' + accu + '\'';
2768             else
2769                 result += accu;
2770             accu.clear();
2771             result += c;
2772         }
2773     }
2774     if (accu.contains(':') || accu.contains('<'))
2775         result += '\'' + accu + '\'';
2776     else
2777         result += accu;
2778     //qDebug() << "GDB_QUOTING" << type << " TO " << result;
2779
2780     return result;
2781 }
2782
2783 static void setWatchDataValue(WatchData &data, const GdbMi &mi,
2784     int encoding = 0)
2785 {
2786     if (mi.isValid()) {
2787         QByteArray ba;
2788         switch (encoding) {
2789             case 0: // unencoded 8 bit data
2790                 ba = mi.data();
2791                 break;
2792             case 1: //  base64 encoded 8 bit data
2793                 ba = QByteArray::fromBase64(mi.data());
2794                 ba = '"' + ba + '"';
2795                 break;
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 + '"';
2800                 break;
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 + '"';
2805                 break;
2806         }
2807        data.setValue(ba);
2808     } else {
2809        data.setValueNeeded();
2810     }
2811 }
2812
2813 static void setWatchDataEditValue(WatchData &data, const GdbMi &mi)
2814 {
2815     if (mi.isValid())
2816         data.editvalue = mi.data();
2817 }
2818
2819 static void setWatchDataValueToolTip(WatchData &data, const GdbMi &mi)
2820 {
2821     if (mi.isValid())
2822         data.setValueToolTip(mi.data());
2823 }
2824
2825 static void setWatchDataChildCount(WatchData &data, const GdbMi &mi)
2826 {
2827     if (mi.isValid()) {
2828         data.childCount = mi.data().toInt();
2829         data.setChildCountUnneeded();
2830         if (data.childCount == 0)
2831             data.setChildrenUnneeded();
2832     } else {
2833         data.childCount = -1;
2834     }
2835 }
2836
2837 static void setWatchDataValueDisabled(WatchData &data, const GdbMi &mi)
2838 {
2839     if (mi.data() == "true")
2840         data.valuedisabled = true;
2841     else if (mi.data() == "false")
2842         data.valuedisabled = false;
2843 }
2844
2845 static void setWatchDataExpression(WatchData &data, const GdbMi &mi)
2846 {
2847     if (mi.isValid())
2848         data.exp = "(" + mi.data() + ")";
2849 }
2850
2851 static void setWatchDataAddress(WatchData &data, const GdbMi &mi)
2852 {
2853     if (mi.isValid()) {
2854         data.addr = mi.data();
2855         if (data.exp.isEmpty())
2856             data.exp = "(*(" + gdbQuoteTypes(data.type) + "*)" + data.addr + ")";
2857     }
2858 }
2859
2860 static bool extractTemplate(const QString &type, QString *tmplate, QString *inner)
2861 {
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
2865     int level = 0;
2866     bool skipSpace = false;
2867     for (int i = 0; i != type.size(); ++i) {
2868         QChar c = type[i];
2869         if (c == ' ' && skipSpace) {
2870             skipSpace = false;
2871         } else if (c == '<') {
2872             *(level == 0 ? tmplate : inner) += c;
2873             ++level;
2874         } else if (c == '>') {
2875             --level;
2876             *(level == 0 ? tmplate : inner) += c;
2877         } else if (c == ',') {
2878             *inner += (level == 1) ? '@' : ',';
2879             skipSpace = true;
2880         } else {
2881             *(level == 0 ? tmplate : inner) += c;
2882         }
2883     }
2884     *tmplate = tmplate->trimmed();
2885     *tmplate = tmplate->remove("<>");
2886     *inner = inner->trimmed();
2887     //qDebug() << "EXTRACT TEMPLATE: " << *tmplate << *inner << " FROM " << type;
2888     return !inner->isEmpty();
2889 }
2890
2891 static QString extractTypeFromPTypeOutput(const QString &str)
2892 {
2893     int pos0 = str.indexOf('=');
2894     int pos1 = str.indexOf('{');
2895     int pos2 = str.lastIndexOf('}');
2896     QString res = str;
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();
2901 }
2902
2903 static bool isIntOrFloatType(const QString &type)
2904 {
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);
2911 }
2912
2913 static QString sizeofTypeExpression(const QString &type)
2914 {
2915     if (type.endsWith('*'))
2916         return "sizeof(void*)";
2917     if (type.endsWith('>'))
2918         return "sizeof(" + type + ")";
2919     return "sizeof(" + gdbQuoteTypes(type) + ")";
2920 }
2921
2922 void GdbEngine::setUseCustomDumpers(bool on)
2923 {
2924     //qDebug() << "SWITCHING ON/OFF DUMPER DEBUGGING:" << on;
2925     Q_UNUSED(on);
2926     // FIXME: a bit too harsh, but otherwise the treeview sometimes look funny
2927     //m_expandedINames.clear();
2928     setTokenBarrier();
2929     updateLocals();
2930 }
2931
2932 bool GdbEngine::isCustomValueDumperAvailable(const QString &type) const
2933 {
2934     DebuggerSettings *s = q->settings();
2935     if (!s->m_useCustomDumpers)
2936         return false;
2937     if (s->m_debugDumpers && qq->stackHandler()->isDebuggingDumpers())
2938         return false;
2939     if (m_dataDumperState != DataDumperAvailable)
2940         return false;
2941
2942     // simple types
2943     if (m_availableSimpleDumpers.contains(type))
2944         return true;
2945
2946     // templates
2947     QString tmplate;
2948     QString inner;
2949     if (!extractTemplate(type, &tmplate, &inner))
2950         return false;
2951     return m_availableSimpleDumpers.contains(tmplate);
2952 }
2953
2954 void GdbEngine::runCustomDumper(const WatchData & data0, bool dumpChildren)
2955 {
2956     WatchData data = data0;
2957     QTC_ASSERT(!data.exp.isEmpty(), return);
2958     QString tmplate;
2959     QString inner;
2960     bool isTemplate = extractTemplate(data.type, &tmplate, &inner);
2961     QStringList inners = inner.split('@');
2962     if (inners.at(0).isEmpty())
2963         inners.clear();
2964     for (int i = 0; i != inners.size(); ++i)
2965         inners[i] = inners[i].simplified();
2966
2967     QString outertype = isTemplate ? tmplate : data.type;
2968     // adjust the data extract
2969     if (outertype == m_namespace + "QWidget")
2970         outertype = m_namespace + "QObject";
2971
2972     QString extraArgs[4];
2973     extraArgs[0] = "0";
2974     extraArgs[1] = "0";
2975     extraArgs[2] = "0";
2976     extraArgs[3] = "0";
2977     int extraArgCount = 0;
2978
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);
2983
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") {
3003         QString nodetype;
3004         if (m_qtVersion >= (4 << 16) + (5 << 8) + 0) {
3005             nodetype  = m_namespace + "QMapNode";
3006             nodetype += data.type.mid(outertype.size());
3007         } else {
3008             // FIXME: doesn't work for QMultiMap
3009             nodetype  = data.type + "::Node"; 
3010         }
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";
3022         } else {
3023             //extraArgs[extraArgCount++] = sizeofTypeExpression(data.type);
3024             //extraArgs[extraArgCount++] = "(size_t)&(('" + data.type + "'*)0)->value";
3025         }
3026     } else if (outertype == "std::deque") {
3027         // remove 'std::allocator<...>':
3028         extraArgs[1] = "0";
3029     } else if (outertype == "std::stack") {
3030         // remove 'std::allocator<...>':
3031         extraArgs[1] = "0";
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
3037         // read it back;
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";
3042         extraArgs[3] = "0";
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";
3049         }
3050         extraArgs[0] = "0";
3051         extraArgs[1] = "0";
3052         extraArgs[2] = "0";
3053         extraArgs[3] = "0";
3054     }
3055
3056     //int protocol = (data.iname.startsWith("watch") && data.type == "QImage") ? 3 : 2;
3057     //int protocol = data.iname.startsWith("watch") ? 3 : 2;
3058     int protocol = 2;
3059     //int protocol = isDisplayedIName(data.iname) ? 3 : 2;
3060
3061     QString addr;
3062     if (data.addr.startsWith("0x"))
3063         addr = "(void*)" + data.addr;
3064     else
3065         addr = "&(" + data.exp + ")";
3066
3067     QByteArray params;
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');
3078
3079     sendWatchParameters(params);
3080
3081     QString cmd ="call "
3082             + QString("qDumpObjectData440(")
3083             + QString::number(protocol)
3084             + ',' + "%1+1"                // placeholder for token
3085             + ',' + addr
3086             + ',' + (dumpChildren ? "1" : "0")
3087             + ',' + extraArgs[0]
3088             + ',' + extraArgs[1]
3089             + ',' + extraArgs[2]
3090             + ',' + extraArgs[3] + ')';
3091
3092     //qDebug() << "CMD: " << cmd;
3093
3094     QVariant var;
3095     var.setValue(data);
3096     sendSynchronizedCommand(cmd, WatchDumpCustomValue1, var);
3097
3098     q->showStatusMessage(
3099         tr("Retrieving data for watch view (%1 requests pending)...")
3100             .arg(m_pendingRequests + 1), 10000);
3101
3102     // retrieve response
3103     sendSynchronizedCommand("p (char*)qDumpOutBuffer", WatchDumpCustomValue2, var);
3104 }
3105
3106 void GdbEngine::createGdbVariable(const WatchData &data)
3107 {
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);
3115 }
3116
3117 void GdbEngine::updateSubItem(const WatchData &data0)
3118 {
3119     WatchData data = data0;
3120     #if DEBUG_SUBITEM
3121     qDebug() << "UPDATE SUBITEM: " << data.toString();
3122     #endif
3123     QTC_ASSERT(data.isValid(), return);
3124
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.
3131             #if 1
3132             qDebug() << "FIXME: GdbEngine::updateSubItem: "
3133                  << data.toString() << "should not happen";
3134             #else
3135             data.setType("<out of scope>");
3136             data.setValue("<out of scope>");
3137             data.setChildCount(0);
3138             insertData(data);
3139             return;
3140             #endif
3141         }
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);
3147         return;
3148     }
3149
3150     // we should have a type now. this is relied upon further below
3151     QTC_ASSERT(!data.type.isEmpty(), return);
3152
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
3157         #if DEBUG_SUBITEM
3158         qDebug() << "IT'S A POINTER";
3159         #endif
3160 #if 1
3161         WatchData data1;
3162         data1.iname = data.iname + ".*";
3163         data1.name = "*" + data.name;
3164         data1.exp = "(*(" + data.exp + "))";
3165         data1.type = stripPointerType(data.type);
3166         data1.setValueNeeded();
3167         insertData(data1);
3168         data.setChildrenUnneeded();
3169         insertData(data);
3170 #else
3171         // Try automatic dereferentiation
3172         data.exp = "*(" + data.exp + ")";
3173         data.type = data.type + "."; // FIXME: fragile HACK to avoid recursion
3174         insertData(data);
3175 #endif
3176         return;
3177     }
3178
3179     if (data.isValueNeeded() && isCustomValueDumperAvailable(data.type)) {
3180         #if DEBUG_SUBITEM
3181         qDebug() << "UPDATE SUBITEM: CUSTOMVALUE";
3182         #endif
3183         runCustomDumper(data, qq->watchHandler()->isExpandedIName(data.iname));
3184         return;
3185     }
3186
3187 /*
3188     if (data.isValueNeeded() && data.exp.isEmpty()) {
3189         #if DEBUG_SUBITEM
3190         qDebug() << "UPDATE SUBITEM: NO EXPRESSION?";
3191         #endif
3192         data.setError("<no expression given>");
3193         insertData(data);
3194         return;
3195     }
3196 */
3197
3198     if (data.isValueNeeded() && data.variable.isEmpty()) {
3199         #if DEBUG_SUBITEM
3200         qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR VALUE";
3201         #endif
3202         createGdbVariable(data);
3203         // the WatchVarCreate handler will re-insert a WatchData
3204         // item, with valueNeeded() set.
3205         return;
3206     }
3207
3208     if (data.isValueNeeded()) {
3209         QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
3210         #if DEBUG_SUBITEM
3211         qDebug() << "UPDATE SUBITEM: VALUE";
3212         #endif
3213         QString cmd = "-var-evaluate-expression \"" + data.iname + "\"";
3214         sendSynchronizedCommand(cmd, WatchEvaluateExpression,
3215             QVariant::fromValue(data));
3216         return;
3217     }
3218
3219     if (data.isChildrenNeeded() && isCustomValueDumperAvailable(data.type)) {
3220         #if DEBUG_SUBITEM
3221         qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN";
3222         #endif
3223         runCustomDumper(data, true);
3224         return;
3225     }
3226
3227     if (data.isChildrenNeeded() && data.variable.isEmpty()) {
3228         #if DEBUG_SUBITEM
3229         qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDREN";
3230         #endif
3231         createGdbVariable(data);
3232         // the WatchVarCreate handler will re-insert a WatchData
3233         // item, with childrenNeeded() set.
3234         return;
3235     }
3236
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));
3241         return;
3242     }
3243
3244     if (data.isChildCountNeeded() && isCustomValueDumperAvailable(data.type)) {
3245         #if DEBUG_SUBITEM
3246         qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN";
3247         #endif
3248         runCustomDumper(data, qq->watchHandler()->isExpandedIName(data.iname));
3249         return;
3250     }
3251
3252     if (data.isChildCountNeeded() && data.variable.isEmpty()) {
3253         #if DEBUG_SUBITEM
3254         qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDCOUNT";
3255         #endif
3256         createGdbVariable(data);
3257         // the WatchVarCreate handler will re-insert a WatchData
3258         // item, with childrenNeeded() set.
3259         return;
3260     }
3261
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));
3266         return;
3267     }
3268
3269     qDebug() << "FIXME: UPDATE SUBITEM: " << data.toString();
3270     QTC_ASSERT(false, return);
3271 }
3272
3273 void GdbEngine::updateWatchModel()
3274 {
3275     m_pendingRequests = 0;
3276     PENDING_DEBUG("EXTERNAL TRIGGERING UPDATE WATCH MODEL");
3277     updateWatchModel2();
3278 }
3279
3280 void GdbEngine::updateWatchModel2()
3281 {
3282     PENDING_DEBUG("UPDATE WATCH MODEL");
3283     QList<WatchData> incomplete = qq->watchHandler()->takeCurrentIncompletes();
3284     //QTC_ASSERT(incomplete.isEmpty(), /**/);
3285     if (!incomplete.isEmpty()) {
3286         #if DEBUG_PENDING
3287         qDebug() << "##############################################";
3288         qDebug() << "UPDATE MODEL, FOUND INCOMPLETES:";
3289         foreach (const WatchData &data, incomplete)
3290             qDebug() << data.toString();
3291         #endif
3292
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;
3301
3302         return;
3303     }
3304
3305     if (m_pendingRequests > 0) {
3306         PENDING_DEBUG("UPDATE MODEL, PENDING: " << m_pendingRequests);
3307         return;
3308     }
3309
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();
3315
3316     if (!m_toolTipExpression.isEmpty()) {
3317         WatchData *data = qq->watchHandler()->findData(tooltipIName);
3318         if (data) {
3319             //m_toolTipCache[data->exp] = *data;
3320             QToolTip::showText(m_toolTipPos,
3321                     "(" + data->type + ") " + data->exp + " = " + data->value);
3322         } else {
3323             QToolTip::showText(m_toolTipPos,
3324                 "Cannot evaluate expression: " + m_toolTipExpression);
3325         }
3326     }
3327 }
3328
3329 void GdbEngine::handleQueryDataDumper1(const GdbResultRecord &record)
3330 {
3331     Q_UNUSED(record);
3332 }
3333
3334 void GdbEngine::handleQueryDataDumper2(const GdbResultRecord &record)
3335 {
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;
3345
3346     GdbMi contents;
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;
3356     } else {
3357         m_qtVersion = 0;
3358     }
3359    
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.")
3374                 );
3375     } else {
3376         m_dataDumperState = DataDumperAvailable;
3377     }
3378     //qDebug() << "DATA DUMPERS AVAILABLE" << m_availableSimpleDumpers;
3379 }
3380
3381 void GdbEngine::sendWatchParameters(const QByteArray &params0)
3382 {
3383     QByteArray params = params0;
3384     params.append('\0');
3385     char buf[50];
3386     sprintf(buf, "set {char[%d]} qDumpInBuffer = {", params.size());
3387     QByteArray encoded;
3388     encoded.append(buf);
3389     for (int i = 0; i != params.size(); ++i) {
3390         sprintf(buf, "%d,", int(params[i]));
3391         encoded.append(buf);
3392     }
3393     encoded[encoded.size() - 1] = '}';
3394
3395     sendCommand(encoded);
3396 }
3397
3398 void GdbEngine::handleVarAssign()
3399 {
3400     // everything might have changed, force re-evaluation
3401     // FIXME: Speed this up by re-using variables and only
3402     // marking values as 'unknown'
3403     setTokenBarrier();
3404     updateLocals();
3405 }
3406
3407 void GdbEngine::setWatchDataType(WatchData &data, const GdbMi &mi)
3408 {
3409     if (mi.isValid()) {
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();
3415     }
3416 }
3417
3418 void GdbEngine::handleVarCreate(const GdbResultRecord &record,
3419     const WatchData &data0)
3420 {
3421     WatchData data = data0;
3422     // happens e.g. when we already issued a var-evaluate command
3423     if (!data.isValid())
3424         return;
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();
3435             insertData(data);
3436         } else {
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());
3444             insertData(data);
3445         }
3446     } else if (record.resultClass == GdbResultError) {
3447         data.setError(record.data.findChild("msg").data());
3448         if (data.isWatcher()) {
3449             data.value = strNotInScope;
3450             data.type = " ";
3451             data.setAllUnneeded();
3452             data.setChildCount(0);
3453             data.valuedisabled = true;
3454             insertData(data);
3455         }
3456     }
3457 }
3458
3459 void GdbEngine::handleEvaluateExpression(const GdbResultRecord &record,
3460     const WatchData &data0)
3461 {
3462     WatchData data = data0;
3463     QTC_ASSERT(data.isValid(), qDebug() << "HUH?");
3464     if (record.resultClass == GdbResultDone) {
3465         //if (col == 0)
3466         //    data.name = record.data.findChild("value").data();
3467         //else
3468             setWatchDataValue(data, record.data.findChild("value"));
3469     } else if (record.resultClass == GdbResultError) {
3470         data.setError(record.data.findChild("msg").data());
3471     }
3472     //qDebug() << "HANDLE EVALUATE EXPRESSION: " << data.toString();
3473     insertData(data);
3474     //updateWatchModel2();
3475 }
3476
3477 void GdbEngine::handleDumpCustomSetup(const GdbResultRecord &record)
3478 {
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);
3485     }
3486 }
3487
3488 void GdbEngine::handleDumpCustomValue1(const GdbResultRecord &record,
3489     const WatchData &data0)
3490 {
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
3497         // in transmission
3498         //--m_pendingRequests;
3499         QString msg = record.data.findChild("msg").data();
3500         //qDebug() << "CUSTOM DUMPER ERROR MESSAGE: " << msg;
3501 #ifdef QT_DEBUG
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")) {
3506             // Fake full stop
3507             sendCommand("p 0", GdbAsyncOutput2);  // dummy
3508             return;
3509         }
3510 #endif
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);
3516         //insertData(data);
3517     }
3518 }
3519
3520 void GdbEngine::handleDumpCustomValue2(const GdbResultRecord &record,
3521     const WatchData &data0)
3522 {
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();
3529         return;
3530     }
3531
3532     GdbMi output = record.data.findChild("consolestreamoutput");
3533     QByteArray out = output.data();
3534
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);
3539         insertData(data);
3540         return;
3541     }
3542
3543     out = out.mid(markerPos +  1);
3544     out = out.left(out.lastIndexOf('"'));
3545     out.replace("\\", "");
3546     out = "dummy={" + out + "}";
3547     
3548     GdbMi contents;
3549     contents.fromString(out);
3550     //qDebug() << "CONTENTS" << contents.toString(true);
3551     if (!contents.isValid()) {
3552         data.setError(strNotInScope);
3553         insertData(data);
3554         return;
3555     }
3556
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);
3570         }
3571     }
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();
3578
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();
3584     insertData(data);
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) {
3596                     key = key.left(12);
3597                     key += "...";
3598                 }
3599             }
3600             //data1.name += " (" + key + ")";
3601             data1.name = key;
3602         }
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();
3614         insertData(data1);
3615     }
3616 }
3617
3618 void GdbEngine::updateLocals()
3619 {
3620     m_pendingRequests = 0;
3621
3622     PENDING_DEBUG("\nRESET PENDING");
3623     m_toolTipCache.clear();
3624     m_toolTipExpression.clear();
3625     qq->watchHandler()->reinitializeWatchers();
3626
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
3633
3634     //tryLoadCustomDumpers();
3635 }
3636
3637 void GdbEngine::handleStackListArguments(const GdbResultRecord &record)
3638 {
3639     // stage 1/2
3640
3641     // Linux:
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..."}]}]
3646     // Mac:
3647     // 78^done,stack-args=
3648     //    {frame={level="0",args={
3649     //      varobj=
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"},
3654     //      varobj=
3655     //         {exp="before",value="@0xbfffb9f8: {d = 0x3a7f2a70}",
3656     //              name="var22",numchild="1",type="const QString  ...} }}}
3657     //
3658     // In both cases, iterating over the children of stack-args/frame/args
3659     // is ok.
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";
3668     }
3669 }
3670
3671 void GdbEngine::handleStackListLocals(const GdbResultRecord &record)
3672 {
3673     // stage 2/2
3674
3675     // There could be shadowed variables
3676     QList<GdbMi> locals = record.data.findChild("locals").children();
3677     locals += m_currentFunctionArgs;
3678
3679     setLocals(locals);
3680 }
3681
3682 void GdbEngine::setLocals(const QList<GdbMi> &locals) 
3683
3684     //qDebug() << m_varToType;
3685     QMap<QString, int> seen;
3686
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:
3693         #ifdef Q_OS_MAC
3694         int numExps = 0;
3695         foreach (const GdbMi &child, item.children())
3696             numExps += int(child.name() == "exp");
3697         if (numExps > 1)
3698             continue;
3699         QString name = item.findChild("exp").data();
3700         #else
3701         QString name = item.findChild("name").data();
3702         #endif
3703         int n = seen.value(name);
3704         if (n) {
3705             seen[name] = n + 1;
3706             WatchData data;
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);
3713             insertData(data);
3714         } else {
3715             seen[name] = 1;
3716             WatchData data;
3717             data.iname = "local." + name;
3718             data.name = name;
3719             data.exp = 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));
3733             }
3734             insertData(data);
3735         }
3736     }
3737 }
3738
3739 void GdbEngine::insertData(const WatchData &data0)
3740 {
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();
3745         return;
3746     }
3747     qq->watchHandler()->insertData(data);
3748 }
3749
3750 void GdbEngine::handleTypeContents(const QString &output)
3751 {
3752     // output.startsWith("type = ") == true
3753     // "type = int"
3754     // "type = class QString {"
3755     // "type = class QStringList : public QList<QString> {"
3756     QString tip;
3757     QString className;
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");
3762         if (posColon == -1)
3763             posColon = head.indexOf(": protected");
3764         if (posColon == -1)
3765             posColon = head.indexOf(": private");
3766         if (posColon == -1) {
3767             className = head;
3768             tip = "class " + className + " { ... }";
3769         } else {
3770             className = head.left(posColon - 1);
3771             tip = "class " + head + " { ... }";
3772         }
3773         //qDebug() << "posColon: " << posColon;
3774         //qDebug() << "posBrace: " << posBrace;
3775         //qDebug() << "head: " << head;
3776     } else {
3777         className = output.mid(7);
3778         tip = className;
3779     }
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;
3786 }
3787
3788 void GdbEngine::handleVarListChildrenHelper(const GdbMi &item,
3789     const WatchData &parent)
3790 {
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
3797         WatchData data;
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.
3812         WatchData data;
3813         data.iname = parent.iname + '.' + exp;
3814         data.name = 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);
3820         insertData(data);
3821     } else if (parent.iname.endsWith('.')) {
3822         // Happens with anonymous unions
3823         WatchData data;
3824         data.iname = name;
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...
3833     } else {
3834         WatchData data;
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();
3843
3844         data.name = exp;
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("*")) {
3852             // A pointer
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>";
3868         } else {
3869             // A structure. Hope there's nothing else...
3870             data.exp = parent.exp + '.' + exp;
3871         }
3872
3873         if (isCustomValueDumperAvailable(data.type)) {
3874             // we do not trust gdb if we have a custom dumper
3875             data.setValueNeeded();
3876             data.setChildCountNeeded();
3877         }
3878
3879         //qDebug() <<  "VAR_LIST_CHILDREN: PARENT 3" << parent.toString();
3880         //qDebug() <<  "VAR_LIST_CHILDREN: APPENDEE " << data.toString();
3881         insertData(data);
3882     }
3883 }
3884
3885 void GdbEngine::handleVarListChildren(const GdbResultRecord &record,
3886     const WatchData &data0)
3887 {
3888     //WatchResultCounter dummy(this, WatchVarListChildren);
3889     WatchData data = data0;
3890     if (!data.isValid())
3891         return;
3892     if (record.resultClass == GdbResultDone) {
3893         //qDebug() <<  "VAR_LIST_CHILDREN: PARENT " << data.toString();
3894         GdbMi children = record.data.findChild("children");
3895
3896         foreach (const GdbMi &child, children.children())
3897             handleVarListChildrenHelper(child, data);
3898
3899         if (!isAccessSpecifier(data.variable.split('.').takeLast())) {
3900             data.setChildrenUnneeded();
3901             insertData(data);
3902         }
3903     } else if (record.resultClass == GdbResultError) {
3904         data.setError(record.data.findChild("msg").data());
3905     } else {
3906         data.setError("Unknown error: " + record.toString());
3907     }
3908 }
3909
3910 void GdbEngine::handleToolTip(const GdbResultRecord &record,
3911         const QString &what)
3912 {
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");
3919             return;
3920         }
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.";
3924                 //return;
3925             }
3926         }
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);
3933             else
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")
3938             return;
3939         }
3940         if (what == "evaluate") {
3941             m_toolTip.value = m_toolTip.type + ' ' + m_toolTip.exp
3942                    + " = " + record.data.findChild("value").data();
3943             //return;
3944         }
3945         if (what == "ptype") {
3946             GdbMi mi = record.data.findChild("consolestreamoutput");
3947             m_toolTip.value = extractTypeFromPTypeOutput(mi.data());
3948             //return;
3949         }
3950     }
3951
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";
3959 }
3960
3961 #if 0
3962 void GdbEngine::handleChangedItem(QStandardItem *item)
3963 {
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;
3970 }
3971 #endif
3972
3973 void GdbEngine::assignValueInDebugger(const QString &expression, const QString &value)
3974 {
3975     sendCommand("-var-delete assign");
3976     sendCommand("-var-create assign * " + expression);
3977     sendCommand("-var-assign assign " + value, WatchVarAssign);
3978 }
3979
3980 void GdbEngine::tryLoadCustomDumpers()
3981 {
3982     if (m_dataDumperState != DataDumperUninitialized)
3983         return;
3984
3985     PENDING_DEBUG("TRY LOAD CUSTOM DUMPERS");
3986     m_dataDumperState = DataDumperUnavailable; 
3987
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));
4002     }
4003 #endif
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));
4014     }
4015 #endif
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));
4026     }
4027 #endif
4028
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);
4034     } else {
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()));
4039     }
4040 }
4041
4042
4043 IDebuggerEngine *createGdbEngine(DebuggerManager *parent)
4044 {
4045     return new GdbEngine(parent);
4046 }
4047