OSDN Git Service

Fixes: debugger: fix Mac OS 10.4
[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     GdbExecRun,
105     GdbExecRunToFunction,
106     GdbExecStep,
107     GdbExecNext,
108     GdbExecStepI,
109     GdbExecNextI,
110     GdbExecContinue,
111     GdbExecFinish,
112     GdbExecJumpToLine,
113     GdbExecInterrupt,
114     GdbInfoShared,
115     GdbInfoProc,
116     GdbInfoThreads,
117     GdbQueryDataDumper1,
118     GdbQueryDataDumper2,
119     GdbTemporaryContinue,
120
121     BreakCondition = 200,
122     BreakEnablePending,
123     BreakSetAnnotate,
124     BreakDelete,
125     BreakList,
126     BreakIgnore,
127     BreakInfo,
128     BreakInsert,
129     BreakInsert1,
130
131     DisassemblerList = 300,
132
133     ModulesList = 400,
134
135     RegisterListNames = 500,
136     RegisterListValues,
137
138     StackSelectThread = 600,
139     StackListThreads,
140     StackListFrames,
141     StackListLocals,
142     StackListArguments,
143
144     WatchVarAssign = 700,             // data changed by user
145     WatchVarListChildren,
146     WatchVarCreate,
147     WatchEvaluateExpression,
148     WatchToolTip,
149     WatchDumpCustomSetup,
150     WatchDumpCustomValue1,           // waiting for gdb ack
151     WatchDumpCustomValue2,           // waiting for actual data
152     WatchDumpCustomEditValue,
153 };
154
155 QString dotEscape(QString str)
156 {
157     str.replace(' ', '.');
158     str.replace('\\', '.');
159     str.replace('/', '.');
160     return str;
161 }
162
163 QString currentTime()
164 {
165     return QTime::currentTime().toString("hh:mm:ss.zzz");
166 }
167
168 static int &currentToken()
169 {
170     static int token = 0;
171     return token;
172 }
173
174 static bool isSkippableFunction(const QString &funcName, const QString &fileName)
175 {
176     if (fileName.endsWith("kernel/qobject.cpp"))
177         return true;
178     if (fileName.endsWith("kernel/moc_qobject.cpp"))
179         return true;
180     if (fileName.endsWith("kernel/qmetaobject.cpp"))
181         return true;
182     if (fileName.endsWith(".moc"))
183         return true;
184
185     if (funcName.endsWith("::qt_metacall"))
186         return true;
187
188     return false;
189 }
190
191 static bool isLeavableFunction(const QString &funcName, const QString &fileName)
192 {
193     if (funcName.endsWith("QObjectPrivate::setCurrentSender"))
194         return true;
195     if (fileName.endsWith("kernel/qmetaobject.cpp")
196             && funcName.endsWith("QMetaObject::methodOffset"))
197         return true;
198     if (fileName.endsWith("kernel/qobject.h"))
199         return true;
200     if (fileName.endsWith("kernel/qobject.cpp")
201             && funcName.endsWith("QObjectConnectionListVector::at"))
202         return true;
203     if (fileName.endsWith("kernel/qobject.cpp")
204             && funcName.endsWith("~QObject"))
205         return true;
206     if (fileName.endsWith("thread/qmutex.cpp"))
207         return true;
208     if (fileName.endsWith("thread/qthread.cpp"))
209         return true;
210     if (fileName.endsWith("thread/qthread_unix.cpp"))
211         return true;
212     if (fileName.endsWith("thread/qmutex.h"))
213         return true;
214     if (fileName.contains("thread/qbasicatomic"))
215         return true;
216     if (fileName.contains("thread/qorderedmutexlocker_p"))
217         return true;
218     if (fileName.contains("arch/qatomic"))
219         return true;
220     if (fileName.endsWith("tools/qvector.h"))
221         return true;
222     if (fileName.endsWith("tools/qlist.h"))
223         return true;
224     if (fileName.endsWith("tools/qhash.h"))
225         return true;
226     if (fileName.endsWith("tools/qmap.h"))
227         return true;
228     if (fileName.endsWith("tools/qstring.h"))
229         return true;
230     if (fileName.endsWith("global/qglobal.h"))
231         return true;
232
233     return false;
234 }
235
236 static QString startSymbolName()
237 {
238 #ifdef Q_OS_WIN
239     return "WinMainCRTStartup";
240 #endif
241 #ifdef Q_OS_MAC
242     return "main";
243     return "_start";
244 #endif
245 #ifdef Q_OS_LINUX
246     return "_start";
247 #endif
248 }
249
250
251 ///////////////////////////////////////////////////////////////////////
252 //
253 // GdbEngine
254 //
255 ///////////////////////////////////////////////////////////////////////
256
257 GdbEngine::GdbEngine(DebuggerManager *parent)
258 {
259     q = parent;
260     qq = parent->engineInterface();
261     initializeVariables();
262     initializeConnections();
263 }
264
265 GdbEngine::~GdbEngine()
266 {
267     // prevent sending error messages afterwards
268     m_gdbProc.disconnect(this);
269 }
270
271 void GdbEngine::initializeConnections()
272 {
273     // Gdb Process interaction
274     connect(&m_gdbProc, SIGNAL(error(QProcess::ProcessError)), this,
275         SLOT(gdbProcError(QProcess::ProcessError)));
276     connect(&m_gdbProc, SIGNAL(readyReadStandardOutput()), this,
277         SLOT(readGdbStandardOutput()));
278     connect(&m_gdbProc, SIGNAL(readyReadStandardError()), this,
279         SLOT(readGdbStandardError()));
280     connect(&m_gdbProc, SIGNAL(finished(int, QProcess::ExitStatus)), q,
281         SLOT(exitDebugger()));
282
283     // Output
284     connect(&m_outputCollector, SIGNAL(byteDelivery(QByteArray)),
285             SLOT(readDebugeeOutput(QByteArray)));
286     connect(this, SIGNAL(gdbResponseAvailable()),
287         this, SLOT(handleResponse()), Qt::QueuedConnection);
288
289     connect(this, SIGNAL(gdbOutputAvailable(QString,QString)),
290         q, SLOT(showDebuggerOutput(QString,QString)),
291         Qt::QueuedConnection);
292     connect(this, SIGNAL(gdbInputAvailable(QString,QString)),
293         q, SLOT(showDebuggerInput(QString,QString)),
294         Qt::QueuedConnection);
295     connect(this, SIGNAL(applicationOutputAvailable(QString)),
296         q, SLOT(showApplicationOutput(QString)),
297         Qt::QueuedConnection);
298 }
299
300 void GdbEngine::initializeVariables()
301 {
302     m_dataDumperState = DataDumperUninitialized;
303     m_gdbVersion = 100;
304     m_gdbBuildVersion = -1;
305
306     m_fullToShortName.clear();
307     m_shortToFullName.clear();
308     m_varToType.clear();
309
310     m_modulesListOutdated = true;
311     m_oldestAcceptableToken = -1;
312     m_outputCodec = QTextCodec::codecForLocale();
313     m_pendingRequests = 0;
314     m_waitingForBreakpointSynchronizationToContinue = false;
315     m_waitingForFirstBreakpointToBeHit = false;
316     m_commandsToRunOnTemporaryBreak.clear();
317 }
318
319 void GdbEngine::gdbProcError(QProcess::ProcessError error)
320 {
321     QString msg;
322     switch (error) {
323         case QProcess::FailedToStart:
324             msg = QString(tr("The Gdb process failed to start. Either the "
325                 "invoked program '%1' is missing, or you may have insufficient "
326                 "permissions to invoke the program.")).arg(q->settings()->m_gdbCmd);
327             break;
328         case QProcess::Crashed:
329             msg = tr("The Gdb process crashed some time after starting "
330                 "successfully.");
331             break;
332         case QProcess::Timedout:
333             msg = tr("The last waitFor...() function timed out. "
334                 "The state of QProcess is unchanged, and you can try calling "
335                 "waitFor...() again.");
336             break;
337         case QProcess::WriteError:
338             msg = tr("An error occurred when attempting to write "
339                 "to the Gdb process. For example, the process may not be running, "
340                 "or it may have closed its input channel.");
341             break;
342         case QProcess::ReadError:
343             msg = tr("An error occurred when attempting to read from "
344                 "the Gdb process. For example, the process may not be running.");
345             break;
346         default:
347             msg = tr("An unknown error in the Gdb process occurred. "
348                 "This is the default return value of error().");
349     }
350
351     q->showStatusMessage(msg);
352     QMessageBox::critical(q->mainWindow(), tr("Error"), msg);
353     // act as if it was closed by the core
354     q->exitDebugger();
355 }
356
357 static void skipSpaces(const char *&from, const char *to)
358 {
359     while (from != to && QChar(*from).isSpace())
360         ++from;
361 }
362
363 static inline bool isNameChar(char c)
364 {
365     // could be 'stopped' or 'shlibs-added'
366     return (c >= 'a' && c <= 'z') || c == '-';
367 }
368
369 #if 0
370 static void dump(const char *first, const char *middle, const QString & to)
371 {
372     QByteArray ba(first, middle - first);
373     Q_UNUSED(to);
374     // note that qDebug cuts off output after a certain size... (bug?)
375     qDebug("\n>>>>> %s\n%s\n====\n%s\n<<<<<\n",
376         qPrintable(currentTime()),
377         qPrintable(QString(ba).trimmed()),
378         qPrintable(to.trimmed()));
379     //qDebug() << "";
380     //qDebug() << qPrintable(currentTime())
381     //    << " Reading response:  " << QString(ba).trimmed() << "\n";
382 }
383 #endif
384
385 static void skipTerminator(const char *&from, const char *to)
386 {
387     skipSpaces(from, to);
388     // skip '(gdb)'
389     if (from[0] == '(' && from[1] == 'g' && from[3] == 'b' && from[4] == ')')
390         from += 5;
391     skipSpaces(from, to);
392 }
393
394 void GdbEngine::readDebugeeOutput(const QByteArray &data)
395 {
396     emit applicationOutputAvailable(m_outputCodec->toUnicode(
397             data.constData(), data.length(), &m_outputCodecState));
398 }
399
400 void GdbEngine::debugMessage(const QString &msg)
401 {
402     emit gdbOutputAvailable("debug:", msg);
403 }
404
405 // called asyncronously as response to Gdb stdout output in
406 // gdbResponseAvailable()
407 void GdbEngine::handleResponse()
408 {
409     static QTime lastTime;
410
411     emit gdbOutputAvailable("            ", currentTime());
412     emit gdbOutputAvailable("stdout:", m_inbuffer);
413
414 #if 0
415     qDebug() // << "#### start response handling #### "
416         << currentTime()
417         << lastTime.msecsTo(QTime::currentTime()) << "ms,"
418         << "buf: " << m_inbuffer.left(1500) << "..."
419         //<< "buf: " << m_inbuffer
420         << "size:" << m_inbuffer.size();
421 #else
422     //qDebug() << "buf: " << m_inbuffer;
423 #endif
424
425     lastTime = QTime::currentTime();
426
427     while (1) {
428         if (m_inbuffer.isEmpty())
429             break;
430
431         const char *from = m_inbuffer.constData();
432         // FIXME: check for line ending in '\n(gdb)\n'
433         const char *to = from + m_inbuffer.size();
434         const char *inner;
435
436         //const char *oldfrom = from;
437
438         //skipSpaces(from, to);
439         skipTerminator(from, to);
440         int token = -1;
441
442         // token is a sequence of numbers
443         for (inner = from; inner != to; ++inner)
444             if (*inner < '0' || *inner > '9')
445                 break;
446         if (from != inner) {
447             token = QString(QByteArray(from, inner - from)).toInt();
448             from = inner;
449             //qDebug() << "found token " << token;
450         }
451
452         if (from == to) {
453             //qDebug() << "Returning: " << toString();
454             break;
455         }
456
457         // next char decides kind of record
458         const char c = *from++;
459         //qDebug() << "CODE:" << c;
460
461         switch (c) {
462             case '*':
463             case '+':
464             case '=': {
465                 QByteArray asyncClass;
466                 for (; from != to; ++from) {
467                     const char c = *from;
468                     if (!isNameChar(c))
469                         break;
470                     asyncClass += *from;
471                 }
472                 //qDebug() << "ASYNCCLASS" << asyncClass;
473
474                 GdbMi record;
475                 while (from != to && *from == ',') {
476                     ++from; // skip ','
477                     GdbMi data;
478                     data.parseResultOrValue(from, to);
479                     if (data.isValid()) {
480                         //qDebug() << "parsed response: " << data.toString();
481                         record.m_children += data;
482                         record.m_type = GdbMi::Tuple;
483                     }
484                 }
485                 //dump(oldfrom, from, record.toString());
486                 skipTerminator(from, to);
487                 m_inbuffer = QByteArray(from, to - from);
488                 if (asyncClass == "stopped") {
489                     handleAsyncOutput(record);
490                 } else if (asyncClass == "running") {
491                     // Archer has 'thread-id="all"' here
492                 #ifdef Q_OS_MAC
493                 } else if (asyncClass == "shlibs-updated") {
494                     // MAC announces updated libs
495                 } else if (asyncClass == "shlibs-added") {
496                     // MAC announces added libs
497                     // {shlib-info={num="2", name="libmathCommon.A_debug.dylib",
498                     // kind="-", dyld-addr="0x7f000", reason="dyld", requested-state="Y",
499                     // state="Y", path="/usr/lib/system/libmathCommon.A_debug.dylib", 
500                     // description="/usr/lib/system/libmathCommon.A_debug.dylib",
501                     // loaded_addr="0x7f000", slide="0x7f000", prefix=""}}
502                 #endif
503                 } else {
504                     qDebug() << "IGNORED ASYNC OUTPUT "
505                         << asyncClass << record.toString();
506                 }
507                 break;
508             }
509
510             case '~': {
511                 QString data = GdbMi::parseCString(from, to);
512                 m_pendingConsoleStreamOutput += data;
513                 m_inbuffer = QByteArray(from, to - from);
514                 break;
515             }
516
517             case '@': {
518                 QString data = GdbMi::parseCString(from, to);
519                 m_pendingTargetStreamOutput += data;
520                 m_inbuffer = QByteArray(from, to - from);
521                 break;
522             }
523
524             case '&': {
525                 QString data = GdbMi::parseCString(from, to);
526                 m_pendingLogStreamOutput += data;
527                 m_inbuffer = QByteArray(from, to - from);
528                 // On Windows, the contents seem to depend on the debugger
529                 // version and/or OS version used.
530                 if (data.startsWith("warning:"))
531                     qq->showApplicationOutput(data);
532                 break;
533             }
534
535             case '^': {
536                 GdbResultRecord record;
537
538                 record.token = token;
539
540                 for (inner = from; inner != to; ++inner)
541                     if (*inner < 'a' || *inner > 'z')
542                         break;
543
544                 QByteArray resultClass(from, inner - from);
545
546                 if (resultClass == "done")
547                     record.resultClass = GdbResultDone;
548                 else if (resultClass == "running")
549                     record.resultClass = GdbResultRunning;
550                 else if (resultClass == "connected")
551                     record.resultClass = GdbResultConnected;
552                 else if (resultClass == "error")
553                     record.resultClass = GdbResultError;
554                 else if (resultClass == "exit")
555                     record.resultClass = GdbResultExit;
556                 else
557                     record.resultClass = GdbResultUnknown;
558
559                 from = inner;
560                 skipSpaces(from, to);
561                 if (from != to && *from == ',') {
562                     ++from;
563                     record.data.parseTuple_helper(from, to);
564                     record.data.m_type = GdbMi::Tuple;
565                     record.data.m_name = "data";
566                 }
567                 skipSpaces(from, to);
568                 skipTerminator(from, to);
569
570                 //qDebug() << "\nLOG STREAM:" + m_pendingLogStreamOutput;
571                 //qDebug() << "\nTARGET STREAM:" + m_pendingTargetStreamOutput;
572                 //qDebug() << "\nCONSOLE STREAM:" + m_pendingConsoleStreamOutput;
573                 record.data.setStreamOutput("logstreamoutput",
574                     m_pendingLogStreamOutput);
575                 record.data.setStreamOutput("targetstreamoutput",
576                     m_pendingTargetStreamOutput);
577                 record.data.setStreamOutput("consolestreamoutput",
578                     m_pendingConsoleStreamOutput);
579                 QByteArray custom = m_customOutputForToken[token];
580                 if (!custom.isEmpty())
581                     record.data.setStreamOutput("customvaluecontents",
582                         '{' + custom + '}');
583                 //m_customOutputForToken.remove(token);
584                 m_pendingLogStreamOutput.clear();
585                 m_pendingTargetStreamOutput.clear();
586                 m_pendingConsoleStreamOutput.clear();
587
588                 //dump(oldfrom, from, record.toString());
589                 m_inbuffer = QByteArray(from, to - from);
590                 handleResultRecord(record);
591                 break;
592             }
593             default: {
594                 qDebug() << "FIXME: UNKNOWN CODE: " << c << " IN " << m_inbuffer;
595                 m_inbuffer = QByteArray(from, to - from);
596                 break;
597             }
598         }
599     }
600
601     //qDebug() << "##### end response handling ####\n\n\n"
602     //    << currentTime() << lastTime.msecsTo(QTime::currentTime());
603     lastTime = QTime::currentTime();
604 }
605
606 #ifdef Q_OS_MAC
607 static void fixMac(QByteArray &out)
608 {
609     // HACK: gdb on Mac mixes MI1 and MI2 syntax. Not nice.
610     // it returns:   9^done,locals={{name="a"},{name="w"}}
611     // instead of:   9^done,locals=[{name="a"},{name="w"}]
612     if (!out.contains("locals={{name"))
613         return;
614
615     static const QByteArray termArray("(gdb) ");
616     int pos = out.indexOf(termArray);
617     if (pos == -1)
618         return;
619
620     int pos1 = out.indexOf("={{");
621     if (pos1 == -1)
622         return;
623
624     int pos2 = out.indexOf("]]");
625     if (pos2 == -1)
626         return;
627
628     if (pos1 < pos && pos2 < pos) {
629         out[pos1 + 1] = '[';
630         out[pos2 + 1] = ']';
631     }
632 }
633 #endif
634
635 void GdbEngine::readGdbStandardError()
636 {
637     qWarning() << "Unexpected gdb stderr:" << m_gdbProc.readAllStandardError();
638 }
639
640 void GdbEngine::readGdbStandardOutput()
641 {
642     // This is the function called whenever the Gdb process created
643     // output. As a rule of thumb, stdout contains _real_ Gdb output
644     // as responses to our command
645     // and "spontaneous" events like messages on loaded shared libraries.
646     // OTOH, stderr contains application output produced by qDebug etc.
647     // There is no organized way to pass application stdout output.
648
649     QByteArray out = m_gdbProc.readAllStandardOutput();
650
651     //qDebug() << "\n\n\nPLUGIN OUT: '" <<  out.data() << "'\n\n\n";
652
653     #ifdef Q_OS_MAC
654     fixMac(out);
655     #endif
656
657     m_inbuffer.append(out);
658     //QTC_ASSERT(!m_inbuffer.isEmpty(), return);
659
660     char c = m_inbuffer[m_inbuffer.size() - 1];
661     static const QByteArray termArray("(gdb) ");
662     if (out.indexOf(termArray) == -1 && c != 10 && c != 13) {
663         //qDebug() << "\n\nBuffer not yet filled, waiting for more data to arrive";
664         //qDebug() << m_inbuffer.data() << m_inbuffer.size();
665         //qDebug() << "\n\n";
666         return;
667     }
668
669     emit gdbResponseAvailable();
670 }
671
672 void GdbEngine::interruptInferior()
673 {
674     qq->notifyInferiorStopRequested();
675     if (m_gdbProc.state() == QProcess::NotRunning) {
676         debugMessage("TRYING TO INTERRUPT INFERIOR WITHOUT RUNNING GDB");
677         qq->notifyInferiorExited();
678         return;
679     }
680
681     if (q->m_attachedPID > 0) {
682         if (!interruptProcess(q->m_attachedPID))
683         //    qq->notifyInferiorStopped();
684         //else
685             debugMessage(QString("CANNOT INTERRUPT %1").arg(q->m_attachedPID));
686         return;
687     }
688
689 #ifdef Q_OS_MAC
690     sendCommand("-exec-interrupt", GdbExecInterrupt);
691     //qq->notifyInferiorStopped();
692 #else
693     if (!interruptChildProcess(m_gdbProc.pid()))
694     //    qq->notifyInferiorStopped();
695     //else
696         debugMessage(QString("CANNOT STOP INFERIOR"));
697 #endif
698 }
699
700 void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
701 {
702     int pid = pid0.toInt();
703     if (pid == 0) {
704         debugMessage(QString("Cannot parse PID from %1").arg(pid0));
705         return;
706     }
707     if (pid == q->m_attachedPID)
708         return;
709     debugMessage(QString("FOUND PID %1").arg(pid));
710     q->m_attachedPID = pid;
711     qq->notifyInferiorPidChanged(pid); 
712 }
713
714 void GdbEngine::sendSynchronizedCommand(const QString & command,
715     int type, const QVariant &cookie, StopNeeded needStop)
716 {
717     sendCommand(command, type, cookie, needStop, Synchronized);
718 }
719
720 void GdbEngine::sendCommand(const QString &command, int type,
721     const QVariant &cookie, StopNeeded needStop, Synchronization synchronized)
722 {
723     if (m_gdbProc.state() == QProcess::NotRunning) {
724         debugMessage("NO GDB PROCESS RUNNING, CMD IGNORED: " + command);
725         return;
726     }
727
728     if (synchronized) {
729         ++m_pendingRequests;
730         PENDING_DEBUG("   TYPE " << type << " INCREMENTS PENDING TO: "
731             << m_pendingRequests << command);
732     } else {
733         PENDING_DEBUG("   UNKNOWN TYPE " << type << " LEAVES PENDING AT: "
734             << m_pendingRequests << command);
735     }
736
737     GdbCookie cmd;
738     cmd.synchronized = synchronized;
739     cmd.command = command;
740     cmd.type = type;
741     cmd.cookie = cookie;
742
743     if (needStop && q->status() != DebuggerInferiorStopped
744             && q->status() != DebuggerProcessStartingUp) {
745         // queue the commands that we cannot send at once
746         QTC_ASSERT(q->status() == DebuggerInferiorRunning,
747             qDebug() << "STATUS: " << q->status());
748         q->showStatusMessage(tr("Stopping temporarily."));
749         debugMessage("QUEUING COMMAND " + cmd.command);
750         m_commandsToRunOnTemporaryBreak.append(cmd);
751         interruptInferior();
752     } else if (!command.isEmpty()) {
753         ++currentToken();
754         m_cookieForToken[currentToken()] = cmd;
755         cmd.command = QString::number(currentToken()) + cmd.command;
756         if (cmd.command.contains("%1"))
757             cmd.command = cmd.command.arg(currentToken());
758
759         m_gdbProc.write(cmd.command.toLatin1() + "\r\n");
760         //emit gdbInputAvailable(QString(), "         " +  currentTime());
761         //emit gdbInputAvailable(QString(), "[" + currentTime() + "]    " + cmd.command);
762         emit gdbInputAvailable(QString(), cmd.command);
763     }
764 }
765
766 void GdbEngine::handleResultRecord(const GdbResultRecord &record)
767 {
768     //qDebug() << "TOKEN: " << record.token
769     //    << " ACCEPTABLE: " << m_oldestAcceptableToken;
770     //qDebug() << "";
771     //qDebug() << "\nRESULT" << record.token << record.toString();
772
773     int token = record.token;
774     if (token == -1)
775         return;
776
777     GdbCookie cmd = m_cookieForToken.take(token);
778
779     // FIXME: this falsely rejects results from the custom dumper recognition
780     // procedure, too...
781     if (record.token < m_oldestAcceptableToken && cmd.type >= 300) {
782         //qDebug() << "### SKIPPING OLD RESULT " << record.toString();
783         //QMessageBox::information(m_mainWindow, tr("Skipped"), "xxx");
784         return;
785     }
786
787 #if 0
788     qDebug() << "# handleOutput, "
789         << "cmd type: " << cmd.type
790         << "cmd synchronized: " << cmd.synchronized
791         << "\n record: " << record.toString();
792 #endif
793
794     // << "\n data: " << record.data.toString(true);
795
796     if (cmd.type != GdbInvalidCommand)
797         handleResult(record, cmd.type, cmd.cookie);
798
799     if (cmd.synchronized) {
800         --m_pendingRequests;
801         PENDING_DEBUG("   TYPE " << cmd.type << " DECREMENTS PENDING TO: "
802             << m_pendingRequests << cmd.command);
803         if (m_pendingRequests <= 0) {
804             PENDING_DEBUG(" ....  AND TRIGGERS MODEL UPDATE");
805             updateWatchModel2();
806         }
807     } else {
808         PENDING_DEBUG("   UNKNOWN TYPE " << cmd.type << " LEAVES PENDING AT: "
809             << m_pendingRequests << cmd.command);
810     }
811 }
812
813 void GdbEngine::handleResult(const GdbResultRecord & record, int type,
814     const QVariant & cookie)
815 {
816     switch (type) {
817         case GdbExecNext:
818         case GdbExecStep:
819         case GdbExecNextI:
820         case GdbExecStepI:
821         case GdbExecContinue:
822         case GdbExecFinish:
823             // evil code sharing
824             handleExecRun(record);
825             break;
826
827         case GdbStart:
828             handleStart(record);
829             break;
830         case GdbInfoProc:
831             handleInfoProc(record);
832             break;
833         case GdbInfoThreads:
834             handleInfoThreads(record);
835             break;
836
837         case GdbShowVersion:
838             handleShowVersion(record);
839             break;
840         case GdbFileExecAndSymbols:
841             handleFileExecAndSymbols(record);
842             break;
843         case GdbExecRunToFunction:
844             // that should be "^running". We need to handle the resulting
845             // "Stopped"
846             //handleExecRunToFunction(record);
847             break;
848         case GdbExecInterrupt:
849             qq->notifyInferiorStopped();
850             break;
851         case GdbExecJumpToLine:
852             handleExecJumpToLine(record);
853             break;
854         case GdbQueryPwd:
855             handleQueryPwd(record);
856             break;
857         case GdbQuerySources:
858             handleQuerySources(record);
859             break;
860         case GdbAsyncOutput2:
861             handleAsyncOutput2(cookie.value<GdbMi>());
862             break;
863         case GdbInfoShared:
864             handleInfoShared(record);
865             break;
866         case GdbQueryDataDumper1:
867             handleQueryDataDumper1(record);
868             break;
869         case GdbQueryDataDumper2:
870             handleQueryDataDumper2(record);
871             break;
872         case GdbTemporaryContinue:
873             continueInferior();
874             q->showStatusMessage(tr("Continuing after temporary stop."));
875             break;
876
877         case BreakList:
878             handleBreakList(record);
879             break;
880         case BreakInsert:
881             handleBreakInsert(record, cookie.toInt());
882             break;
883         case BreakInsert1:
884             handleBreakInsert1(record, cookie.toInt());
885             break;
886         case BreakInfo:
887             handleBreakInfo(record, cookie.toInt());
888             break;
889         case BreakEnablePending:
890         case BreakDelete:
891             // nothing
892             break;
893         case BreakIgnore:
894             handleBreakIgnore(record, cookie.toInt());
895             break;
896         case BreakCondition:
897             handleBreakCondition(record, cookie.toInt());
898             break;
899
900         case DisassemblerList:
901             handleDisassemblerList(record, cookie.toString());
902             break;
903
904         case ModulesList:
905             handleModulesList(record);
906             break;
907
908         case RegisterListNames:
909             handleRegisterListNames(record);
910             break;
911         case RegisterListValues:
912             handleRegisterListValues(record);
913             break;
914
915         case StackListFrames:
916             handleStackListFrames(record);
917             break;
918         case StackListThreads:
919             handleStackListThreads(record, cookie.toInt());
920             break;
921         case StackSelectThread:
922             handleStackSelectThread(record, cookie.toInt());
923             break;
924         case StackListLocals:
925             handleStackListLocals(record);
926             break;
927         case StackListArguments:
928             handleStackListArguments(record);
929             break;
930
931         case WatchVarListChildren:
932             handleVarListChildren(record, cookie.value<WatchData>());
933             break;
934         case WatchVarCreate:
935             handleVarCreate(record, cookie.value<WatchData>());
936             break;
937         case WatchVarAssign:
938             handleVarAssign();
939             break;
940         case WatchEvaluateExpression:
941             handleEvaluateExpression(record, cookie.value<WatchData>());
942             break;
943         case WatchToolTip:
944             handleToolTip(record, cookie.toString());
945             break;
946         case WatchDumpCustomValue1:
947             handleDumpCustomValue1(record, cookie.value<WatchData>());
948             break;
949         case WatchDumpCustomValue2:
950             handleDumpCustomValue2(record, cookie.value<WatchData>());
951             break;
952         case WatchDumpCustomSetup:
953             handleDumpCustomSetup(record);
954             break;
955
956         default:
957             debugMessage(QString("FIXME: GdbEngine::handleResult: "
958                 "should not happen %1").arg(type));
959             break;
960     }
961 }
962
963 void GdbEngine::executeDebuggerCommand(const QString &command)
964 {
965     //createGdbProcessIfNeeded();
966     if (m_gdbProc.state() == QProcess::NotRunning) {
967         debugMessage("NO GDB PROCESS RUNNING, PLAIN CMD IGNORED: " + command);
968         return;
969     }
970
971     GdbCookie cmd;
972     cmd.command = command;
973     cmd.type = -1;
974
975     emit gdbInputAvailable(QString(), cmd.command);
976     m_gdbProc.write(cmd.command.toLatin1() + "\r\n");
977 }
978
979 void GdbEngine::handleQueryPwd(const GdbResultRecord &record)
980 {
981     // FIXME: remove this special case as soon as 'pwd'
982     // is supported by MI
983     //qDebug() << "PWD OUTPUT:" <<  record.toString();
984     // Gdb responses _unless_ we get an error first.
985     if (record.resultClass == GdbResultDone) {
986 #ifdef Q_OS_LINUX
987         // "5^done,{logstreamoutput="pwd ",consolestreamoutput
988         // ="Working directory /home/apoenitz/dev/work/test1.  "}
989         m_pwd = record.data.findChild("consolestreamoutput").data();
990         int pos = m_pwd.indexOf("Working directory");
991         m_pwd = m_pwd.mid(pos + 18);
992         m_pwd = m_pwd.trimmed();
993         if (m_pwd.endsWith('.'))
994             m_pwd.chop(1);
995 #endif
996 #ifdef Q_OS_WIN
997         // ~"Working directory C:\\Users\\Thomas\\Documents\\WBTest3\\debug.\n"
998         m_pwd = record.data.findChild("consolestreamoutput").data();
999         m_pwd = m_pwd.trimmed();
1000 #endif
1001         debugMessage("PWD RESULT: " + m_pwd);
1002     }
1003 }
1004
1005 void GdbEngine::handleQuerySources(const GdbResultRecord &record)
1006 {
1007     if (record.resultClass == GdbResultDone) {
1008         QMap<QString, QString> oldShortToFull = m_shortToFullName;
1009         m_shortToFullName.clear();
1010         m_fullToShortName.clear();
1011         // "^done,files=[{file="../../../../bin/gdbmacros/gdbmacros.cpp",
1012         // fullname="/data5/dev/ide/main/bin/gdbmacros/gdbmacros.cpp"},
1013         GdbMi files = record.data.findChild("files");
1014         foreach (const GdbMi &item, files.children()) {
1015             QString fileName = item.findChild("file").data();
1016             GdbMi fullName = item.findChild("fullname");
1017             QString full = fullName.data();
1018             #ifdef Q_OS_WIN
1019             full = QDir::cleanPath(full);
1020             #endif
1021             if (fullName.isValid() && QFileInfo(full).isReadable()) {
1022                 //qDebug() << "STORING 2: " << fileName << full;
1023                 m_shortToFullName[fileName] = full;
1024                 m_fullToShortName[full] = fileName;
1025             }
1026         }
1027         if (m_shortToFullName != oldShortToFull)
1028             qq->sourceFileWindow()->setSourceFiles(m_shortToFullName);
1029     }
1030 }
1031
1032 void GdbEngine::handleInfoThreads(const GdbResultRecord &record)
1033 {
1034     if (record.resultClass == GdbResultDone) {
1035         // FIXME: use something more robust
1036         // WIN:     * 3 Thread 2312.0x4d0  0x7c91120f in ?? () 
1037         // LINUX:   * 1 Thread 0x7f466273c6f0 (LWP 21455)  0x0000000000404542 in ...
1038         QRegExp re(QLatin1String("Thread (\\d+)\\.0x.* in"));
1039         QString data = record.data.findChild("consolestreamoutput").data();
1040         if (re.indexIn(data) != -1)
1041             maybeHandleInferiorPidChanged(re.cap(1));
1042     }
1043 }
1044
1045 void GdbEngine::handleInfoProc(const GdbResultRecord &record)
1046 {
1047     if (record.resultClass == GdbResultDone) {
1048         #if defined(Q_OS_MAC)
1049         //^done,process-id="85075"
1050         maybeHandleInferiorPidChanged(record.data.findChild("process-id").data());
1051         #endif
1052
1053         #if defined(Q_OS_LINUX) || defined(Q_OS_WIN)
1054         // FIXME: use something more robust
1055         QRegExp re(QLatin1String("process (\\d+)"));
1056         QString data = record.data.findChild("consolestreamoutput").data();
1057         if (re.indexIn(data) != -1)
1058             maybeHandleInferiorPidChanged(re.cap(1));
1059         #endif
1060     }
1061 }
1062
1063 void GdbEngine::handleInfoShared(const GdbResultRecord &record)
1064 {
1065     if (record.resultClass == GdbResultDone) {
1066         // let the modules handler do the parsing
1067         handleModulesList(record);
1068     }
1069 }
1070
1071 void GdbEngine::handleExecJumpToLine(const GdbResultRecord &record)
1072 {
1073     // FIXME: remove this special case as soon as 'jump'
1074     // is supported by MI
1075     // "&"jump /home/apoenitz/dev/work/test1/test1.cpp:242"
1076     // ~"Continuing at 0x4058f3."
1077     // ~"run1 (argc=1, argv=0x7fffb213a478) at test1.cpp:242"
1078     // ~"242\t x *= 2;"
1079     //109^done"
1080     qq->notifyInferiorStopped();
1081     q->showStatusMessage(tr("Jumped. Stopped."));
1082     QString output = record.data.findChild("logstreamoutput").data();
1083     if (!output.isEmpty())
1084         return;
1085     QString fileAndLine = output.section(' ', 1, 1);
1086     QString file = fileAndLine.section(':', 0, 0);
1087     int line = fileAndLine.section(':', 1, 1).toInt();
1088     q->gotoLocation(file, line, true);
1089 }
1090
1091 void GdbEngine::handleExecRunToFunction(const GdbResultRecord &record)
1092 {
1093     // FIXME: remove this special case as soon as there's a real
1094     // reason given when the temporary breakpoint is hit.
1095     // reight now we get:
1096     // 14*stopped,thread-id="1",frame={addr="0x0000000000403ce4",
1097     // func="foo",args=[{name="str",value="@0x7fff0f450460"}],
1098     // file="main.cpp",fullname="/tmp/g/main.cpp",line="37"}
1099     qq->notifyInferiorStopped();
1100     q->showStatusMessage(tr("Run to Function finished. Stopped."));
1101     GdbMi frame = record.data.findChild("frame");
1102     QString file = frame.findChild("fullname").data();
1103     int line = frame.findChild("line").data().toInt();
1104     qDebug() << "HIT: " << file << line << " IN " << frame.toString()
1105         << " -- " << record.toString();
1106     q->gotoLocation(file, line, true);
1107 }
1108
1109 static bool isExitedReason(const QString &reason)
1110 {
1111     return reason == QLatin1String("exited-normally")   // inferior exited normally
1112         || reason == QLatin1String("exited-signalled")  // inferior exited because of a signal
1113         //|| reason == QLatin1String("signal-received") // inferior received signal
1114         || reason == QLatin1String("exited");           // inferior exited
1115 }
1116
1117 static bool isStoppedReason(const QString &reason)
1118 {
1119     return reason == QLatin1String("function-finished")  // -exec-finish
1120         || reason == QLatin1String("signal-received")  // handled as "isExitedReason"
1121         || reason == QLatin1String("breakpoint-hit")     // -exec-continue
1122         || reason == QLatin1String("end-stepping-range") // -exec-next, -exec-step
1123         || reason == QLatin1String("location-reached")   // -exec-until
1124         || reason == QLatin1String("access-watchpoint-trigger")
1125         || reason == QLatin1String("read-watchpoint-trigger")
1126 #ifdef Q_OS_MAC
1127         || reason.isEmpty()
1128 #endif
1129     ;
1130 }
1131
1132 void GdbEngine::handleAsyncOutput(const GdbMi &data)
1133 {
1134     const QString reason = data.findChild("reason").data();
1135
1136     if (isExitedReason(reason)) {
1137         qq->notifyInferiorExited();
1138         QString msg = "Program exited normally";
1139         if (reason == "exited") {
1140             msg = "Program exited with exit code "
1141                 + data.findChild("exit-code").toString();
1142         } else if (reason == "exited-signalled") {
1143             msg = "Program exited after receiving signal "
1144                 + data.findChild("signal-name").toString();
1145         } else if (reason == "signal-received") {
1146             msg = "Program exited after receiving signal "
1147                 + data.findChild("signal-name").toString();
1148         }
1149         q->showStatusMessage(msg);
1150         // FIXME: shouldn't this use a statis change?
1151         debugMessage("CALLING PARENT EXITDEBUGGER");
1152         q->exitDebugger();
1153         return;
1154     }
1155
1156
1157     //MAC: bool isFirstStop = data.findChild("bkptno").data() == "1";
1158     //!MAC: startSymbolName == data.findChild("frame").findChild("func")
1159     if (m_waitingForFirstBreakpointToBeHit) {
1160         // If the executable dies already that early we might get something
1161         // like stdout:49*stopped,reason="exited",exit-code="0177"
1162         // This is handled now above.
1163
1164         qq->notifyInferiorStopped();
1165         m_waitingForFirstBreakpointToBeHit = false;
1166         //
1167         // that's the "early stop"
1168         //  
1169         #if defined(Q_OS_WIN)
1170         sendCommand("info thread", GdbInfoThreads);
1171         #endif
1172         #if defined(Q_OS_LINUX)
1173         sendCommand("info proc", GdbInfoProc);
1174         #endif
1175         #if defined(Q_OS_MAC)
1176         sendCommand("info pid", GdbInfoProc);
1177         #endif
1178         reloadSourceFiles();
1179         tryLoadCustomDumpers();
1180
1181                 #ifndef Q_OS_MAC
1182         // intentionally after tryLoadCustomDumpers(),
1183         // otherwise we'd interupt solib loading.
1184         if (qq->wantsAllPluginBreakpoints()) {
1185             sendCommand("set auto-solib-add on");
1186             sendCommand("set stop-on-solib-events 0");
1187             sendCommand("sharedlibrary .*");
1188         } else if (qq->wantsSelectedPluginBreakpoints()) {
1189             sendCommand("set auto-solib-add on");
1190             sendCommand("set stop-on-solib-events 1");
1191             sendCommand("sharedlibrary "+qq->selectedPluginBreakpointsPattern());
1192         } else if (qq->wantsNoPluginBreakpoints()) {
1193             // should be like that already
1194             sendCommand("set auto-solib-add off");
1195             sendCommand("set stop-on-solib-events 0");
1196         }
1197         #endif
1198         // nicer to see a bit of the world we live in
1199         reloadModules();
1200         // this will "continue" if done
1201         m_waitingForBreakpointSynchronizationToContinue = true;
1202         //QTimer::singleShot(0, this, SLOT(attemptBreakpointSynchronization()));
1203         attemptBreakpointSynchronization();
1204         return;
1205     }
1206
1207     if (!m_commandsToRunOnTemporaryBreak.isEmpty()) {
1208         QTC_ASSERT(q->status() == DebuggerInferiorStopRequested, 
1209             qDebug() << "STATUS: " << q->status())
1210         qq->notifyInferiorStopped();
1211         q->showStatusMessage(tr("Temporarily stopped."));
1212         // FIXME: racy
1213         foreach (const GdbCookie &cmd, m_commandsToRunOnTemporaryBreak) {
1214             debugMessage(QString("RUNNING QUEUED COMMAND %1 %2")
1215                 .arg(cmd.command).arg(cmd.type));
1216             sendCommand(cmd.command, cmd.type, cmd.cookie);
1217         }
1218         sendCommand("p temporaryStop", GdbTemporaryContinue);
1219         m_commandsToRunOnTemporaryBreak.clear();
1220         q->showStatusMessage(tr("Handling queued commands."));
1221         return;
1222     }
1223
1224     QString msg = data.findChild("consolestreamoutput").data();
1225     if (msg.contains("Stopped due to shared library event") || reason.isEmpty()) {
1226         if (qq->wantsSelectedPluginBreakpoints()) {
1227             debugMessage("SHARED LIBRARY EVENT: " + data.toString());
1228             debugMessage("PATTERN: " + qq->selectedPluginBreakpointsPattern());
1229             sendCommand("sharedlibrary " + qq->selectedPluginBreakpointsPattern());
1230             continueInferior();
1231             q->showStatusMessage(tr("Loading %1...").arg(QString(data.toString())));
1232             return;
1233         }
1234         m_modulesListOutdated = true;
1235         // fall through
1236     }
1237
1238     // seen on XP after removing a breakpoint while running
1239     //  stdout:945*stopped,reason="signal-received",signal-name="SIGTRAP",
1240     //  signal-meaning="Trace/breakpoint trap",thread-id="2",
1241     //  frame={addr="0x7c91120f",func="ntdll!DbgUiConnectToDbg",
1242     //  args=[],from="C:\\WINDOWS\\system32\\ntdll.dll"}
1243     if (reason == "signal-received"
1244           && data.findChild("signal-name").toString() == "SIGTRAP") {
1245         continueInferior();
1246         return;
1247     }
1248
1249     //tryLoadCustomDumpers();
1250
1251     // jump over well-known frames
1252     static int stepCounter = 0;
1253     if (qq->skipKnownFrames()) {
1254         if (reason == "end-stepping-range" || reason == "function-finished") {
1255             GdbMi frame = data.findChild("frame");
1256             //debugMessage(frame.toString());
1257             m_currentFrame = frame.findChild("addr").data() + '%' +
1258                  frame.findChild("func").data() + '%';
1259
1260             QString funcName = frame.findChild("func").data();
1261             QString fileName = frame.findChild("file").data();
1262             if (isLeavableFunction(funcName, fileName)) {
1263                 //debugMessage("LEAVING" + funcName);
1264                 ++stepCounter;
1265                 q->stepOutExec();
1266                 //stepExec();
1267                 return;
1268             }
1269             if (isSkippableFunction(funcName, fileName)) {
1270                 //debugMessage("SKIPPING" + funcName);
1271                 ++stepCounter;
1272                 q->stepExec();
1273                 return;
1274             }
1275             //if (stepCounter)
1276             //    qDebug() << "STEPCOUNTER:" << stepCounter;
1277             stepCounter = 0;
1278         }
1279     }
1280
1281     if (isStoppedReason(reason) || reason.isEmpty()) {
1282         if (m_modulesListOutdated) {
1283             reloadModules();
1284             m_modulesListOutdated = false;
1285         }
1286         // Need another round trip
1287         if (reason == "breakpoint-hit") {
1288             q->showStatusMessage(tr("Stopped at breakpoint"));
1289             GdbMi frame = data.findChild("frame");
1290             //debugMessage("HIT BREAKPOINT: " + frame.toString());
1291             m_currentFrame = frame.findChild("addr").data() + '%' +
1292                  frame.findChild("func").data() + '%';
1293
1294             QApplication::alert(q->mainWindow(), 3000);
1295             reloadSourceFiles();
1296             sendCommand("-break-list", BreakList);
1297             QVariant var = QVariant::fromValue<GdbMi>(data);
1298             sendCommand("p 0", GdbAsyncOutput2, var);  // dummy
1299         } else {
1300             q->showStatusMessage(tr("Stopped: \"%1\"").arg(reason));
1301             handleAsyncOutput2(data);
1302         }
1303         return;
1304     }
1305
1306     debugMessage("STOPPED FOR UNKNOWN REASON: " + data.toString());
1307     // Ignore it. Will be handled with full response later in the
1308     // JumpToLine or RunToFunction handlers
1309 #if 1
1310     // FIXME: remove this special case as soon as there's a real
1311     // reason given when the temporary breakpoint is hit.
1312     // reight now we get:
1313     // 14*stopped,thread-id="1",frame={addr="0x0000000000403ce4",
1314     // func="foo",args=[{name="str",value="@0x7fff0f450460"}],
1315     // file="main.cpp",fullname="/tmp/g/main.cpp",line="37"}
1316     //
1317     // MAC yields sometimes:
1318     // stdout:3661*stopped,time={wallclock="0.00658",user="0.00142",
1319     // system="0.00136",start="1218810678.805432",end="1218810678.812011"}
1320     q->resetLocation();
1321     qq->notifyInferiorStopped();
1322     q->showStatusMessage(tr("Run to Function finished. Stopped."));
1323     GdbMi frame = data.findChild("frame");
1324     QString file = frame.findChild("fullname").data();
1325     int line = frame.findChild("line").data().toInt();
1326     qDebug() << "HIT: " << file << line << " IN " << frame.toString()
1327         << " -- " << data.toString();
1328     q->gotoLocation(file, line, true);
1329 #endif
1330 }
1331
1332
1333 void GdbEngine::handleAsyncOutput2(const GdbMi &data)
1334 {
1335     qq->notifyInferiorStopped();
1336
1337     //
1338     // Stack
1339     //
1340     qq->stackHandler()->setCurrentIndex(0);
1341     updateLocals(); // Quick shot
1342
1343     int currentId = data.findChild("thread-id").data().toInt();
1344     sendSynchronizedCommand("-stack-list-frames", StackListFrames);
1345     if (supportsThreads())
1346         sendSynchronizedCommand("-thread-list-ids", StackListThreads, currentId);
1347
1348     //
1349     // Disassembler
1350     //
1351     // Linux:
1352     //"79*stopped,reason="end-stepping-range",reason="breakpoint-hit",bkptno="1",
1353     //thread-id="1",frame={addr="0x0000000000405d8f",func="run1",
1354     //args=[{name="argc",value="1"},{name="argv",value="0x7fffb7c23058"}],
1355     //file="test1.cpp",fullname="/home/apoenitz/dev/work/test1/test1.cpp",line="261"}"
1356     // Mac: (but only sometimes)
1357     m_address = data.findChild("frame").findChild("addr").data();
1358     qq->reloadDisassembler();
1359
1360     //
1361     // Registers
1362     //
1363     qq->reloadRegisters();
1364 }
1365
1366 void GdbEngine::handleShowVersion(const GdbResultRecord &response)
1367 {
1368     //qDebug () << "VERSION 2:" << response.data.findChild("consolestreamoutput").data();
1369     //qDebug () << "VERSION:" << response.toString();
1370     debugMessage("VERSION:" + response.toString());
1371     if (response.resultClass == GdbResultDone) {
1372         m_gdbVersion = 100;
1373         m_gdbBuildVersion = -1;
1374         QString msg = response.data.findChild("consolestreamoutput").data();
1375         QRegExp supported("GNU gdb(.*) (\\d+)\\.(\\d+)(\\.(\\d+))?(-(\\d+))?");
1376         if (supported.indexIn(msg) == -1) {
1377             debugMessage("UNSUPPORTED GDB VERSION " + msg);
1378             QStringList list = msg.split("\n");
1379             while (list.size() > 2)
1380                 list.removeLast();
1381             msg = tr("The debugger you are using identifies itself as:")
1382                 + "<p><p>" + list.join("<br>") + "<p><p>"
1383                 + tr("This version is not officially supported by Qt Creator.\n"
1384                      "Debugging will most likely not work well.\n"
1385                      "Using gdb 6.7 or later is strongly recommended.");
1386 #if 0
1387             // ugly, but 'Show again' check box...
1388             static QErrorMessage *err = new QErrorMessage(m_mainWindow);
1389             err->setMinimumSize(400, 300);
1390             err->showMessage(msg);
1391 #else
1392             //QMessageBox::information(m_mainWindow, tr("Warning"), msg);
1393 #endif
1394         } else {
1395             m_gdbVersion = 10000 * supported.cap(2).toInt()
1396                          +   100 * supported.cap(3).toInt()
1397                          +     1 * supported.cap(5).toInt();
1398             m_gdbBuildVersion = supported.cap(7).toInt();
1399             debugMessage(QString("GDB VERSION: %1").arg(m_gdbVersion));
1400         }
1401         //qDebug () << "VERSION 3:" << m_gdbVersion << m_gdbBuildVersion;
1402     }
1403 }
1404
1405 void GdbEngine::handleFileExecAndSymbols
1406     (const GdbResultRecord &response)
1407 {
1408     if (response.resultClass == GdbResultDone) {
1409         //m_breakHandler->clearBreakMarkers();
1410     } else if (response.resultClass == GdbResultError) {
1411         QString msg = response.data.findChild("msg").data();
1412         QMessageBox::critical(q->mainWindow(), tr("Error"),
1413             tr("Starting executable failed:\n") + msg);
1414         QTC_ASSERT(q->status() == DebuggerInferiorRunning, /**/);
1415         interruptInferior();
1416     }
1417 }
1418
1419 void GdbEngine::handleExecRun(const GdbResultRecord &response)
1420 {
1421     if (response.resultClass == GdbResultRunning) {
1422         qq->notifyInferiorRunning();
1423         q->showStatusMessage(tr("Running..."));
1424     } else if (response.resultClass == GdbResultError) {
1425         QString msg = response.data.findChild("msg").data();
1426         if (msg == "Cannot find bounds of current function") {
1427             qq->notifyInferiorStopped();
1428             //q->showStatusMessage(tr("No debug information available. "
1429             //  "Leaving function..."));
1430             //stepOutExec();
1431         } else {
1432             QMessageBox::critical(q->mainWindow(), tr("Error"),
1433                 tr("Starting executable failed:\n") + msg);
1434             QTC_ASSERT(q->status() == DebuggerInferiorRunning, /**/);
1435             interruptInferior();
1436         }
1437     }
1438 }
1439
1440 void GdbEngine::queryFullName(const QString &fileName, QString *full)
1441 {
1442     *full = fullName(fileName);
1443 }
1444
1445 QString GdbEngine::shortName(const QString &fullName)
1446 {
1447     return m_fullToShortName.value(fullName, QString());
1448 }
1449
1450 QString GdbEngine::fullName(const QString &fileName)
1451 {
1452     //QString absName = m_manager->currentWorkingDirectory() + "/" + file; ??
1453     if (fileName.isEmpty())
1454         return QString();
1455     QString full = m_shortToFullName.value(fileName, QString());
1456     //debugMessage("RESOLVING: " + fileName + " " +  full);
1457     if (!full.isEmpty())
1458         return full;
1459     QFileInfo fi(fileName);
1460     if (!fi.isReadable())
1461         return QString();
1462     full = fi.absoluteFilePath();
1463     #ifdef Q_OS_WIN
1464     full = QDir::cleanPath(full);
1465     #endif
1466     //debugMessage("STORING: " + fileName + " " + full);
1467     m_shortToFullName[fileName] = full;
1468     m_fullToShortName[full] = fileName;
1469     return full;
1470 }
1471
1472 QString GdbEngine::fullName(const QStringList &candidates)
1473 {
1474     QString full;
1475     foreach (const QString &fileName, candidates) {
1476         full = fullName(fileName);
1477         if (!full.isEmpty())
1478             return full;
1479     }
1480     foreach (const QString &fileName, candidates) {
1481         if (!fileName.isEmpty())
1482             return fileName;
1483     }
1484     return full;
1485 }
1486
1487 void GdbEngine::shutdown()
1488 {
1489     exitDebugger();
1490 }
1491
1492 void GdbEngine::exitDebugger()
1493 {
1494     debugMessage(QString("GDBENGINE EXITDEBUFFER: %1").arg(m_gdbProc.state()));
1495     if (m_gdbProc.state() == QProcess::Starting) {
1496         debugMessage(QString("WAITING FOR GDB STARTUP TO SHUTDOWN: %1")
1497             .arg(m_gdbProc.state()));
1498         m_gdbProc.waitForStarted();
1499     }
1500     if (m_gdbProc.state() == QProcess::Running) {
1501         debugMessage(QString("WAITING FOR RUNNING GDB TO SHUTDOWN: %1")
1502             .arg(m_gdbProc.state()));
1503         interruptInferior();
1504         sendCommand("kill");
1505         sendCommand("-gdb-exit");
1506         // 20s can easily happen when loading webkit debug information
1507         m_gdbProc.waitForFinished(20000);
1508         if (m_gdbProc.state() != QProcess::Running) {
1509             debugMessage(QString("FORCING TERMINATION: %1")
1510                 .arg(m_gdbProc.state()));
1511             m_gdbProc.terminate();
1512             m_gdbProc.waitForFinished(20000);
1513         }
1514     }
1515     if (m_gdbProc.state() != QProcess::NotRunning)
1516         debugMessage("PROBLEM STOPPING DEBUGGER");
1517
1518     m_outputCollector.shutdown();
1519     initializeVariables();
1520     //q->settings()->m_debugDumpers = false;
1521 }
1522
1523
1524 int GdbEngine::currentFrame() const
1525 {
1526     return qq->stackHandler()->currentIndex();
1527 }
1528
1529
1530 bool GdbEngine::startDebugger()
1531 {
1532     QStringList gdbArgs;
1533
1534     QFileInfo fi(q->m_executable);
1535     QString fileName = '"' + fi.absoluteFilePath() + '"';
1536
1537     if (m_gdbProc.state() != QProcess::NotRunning) {
1538         debugMessage("GDB IS ALREADY RUNNING!");
1539         return false;
1540     }
1541
1542     if (!m_outputCollector.listen()) {
1543         QMessageBox::critical(q->mainWindow(), tr("Debugger Startup Failure"),
1544                               tr("Cannot set up communication with child process: %1")
1545                               .arg(m_outputCollector.errorString()));
1546         return false;
1547     }
1548
1549     gdbArgs.prepend(QLatin1String("--tty=") + m_outputCollector.serverName());
1550
1551     //gdbArgs.prepend(QLatin1String("--quiet"));
1552     gdbArgs.prepend(QLatin1String("mi"));
1553     gdbArgs.prepend(QLatin1String("-i"));
1554
1555     if (!q->m_workingDir.isEmpty())
1556         m_gdbProc.setWorkingDirectory(q->m_workingDir);
1557     if (!q->m_environment.isEmpty())
1558         m_gdbProc.setEnvironment(q->m_environment);
1559
1560     #if 0
1561     qDebug() << "Command: " << q->settings()->m_gdbCmd;
1562     qDebug() << "WorkingDirectory: " << m_gdbProc.workingDirectory();
1563     qDebug() << "ScriptFile: " << q->settings()->m_scriptFile;
1564     qDebug() << "Environment: " << m_gdbProc.environment();
1565     qDebug() << "Arguments: " << gdbArgs;
1566     qDebug() << "BuildDir: " << q->m_buildDir;
1567     qDebug() << "ExeFile: " << q->m_executable;
1568     #endif
1569
1570     q->showStatusMessage(tr("Starting Debugger"));
1571     emit gdbInputAvailable(QString(), q->settings()->m_gdbCmd + ' ' + gdbArgs.join(" "));
1572
1573     m_gdbProc.start(q->settings()->m_gdbCmd, gdbArgs);
1574     m_gdbProc.waitForStarted();
1575
1576     if (m_gdbProc.state() != QProcess::Running) {
1577         QMessageBox::critical(q->mainWindow(), tr("Debugger Startup Failure"),
1578                               tr("Cannot start debugger: %1").arg(m_gdbProc.errorString()));
1579         m_outputCollector.shutdown();
1580         return false;
1581     }
1582
1583     q->showStatusMessage(tr("Gdb Running"));
1584
1585     sendCommand("show version", GdbShowVersion);
1586     //sendCommand("-enable-timings");
1587     sendCommand("set print static-members off"); // Seemingly doesn't work.
1588     //sendCommand("define hook-stop\n-thread-list-ids\n-stack-list-frames\nend");
1589     //sendCommand("define hook-stop\nprint 4\nend");
1590     //sendCommand("define hookpost-stop\nprint 5\nend");
1591     //sendCommand("define hook-call\nprint 6\nend");
1592     //sendCommand("define hookpost-call\nprint 7\nend");
1593     //sendCommand("set print object on"); // works with CLI, but not MI
1594     //sendCommand("set step-mode on");  // we can't work with that yes
1595     //sendCommand("set exec-done-display on");
1596     //sendCommand("set print pretty on");
1597     //sendCommand("set confirm off");
1598     //sendCommand("set pagination off");
1599     sendCommand("set breakpoint pending on", BreakEnablePending);
1600     sendCommand("set print elements 10000");
1601     sendCommand("-data-list-register-names", RegisterListNames);
1602
1603     // one of the following is needed to prevent crashes in gdb on code like:
1604     //  template <class T> T foo() { return T(0); }
1605     //  int main() { return foo<int>(); }
1606     //  (gdb) call 'int foo<int>'()
1607     //  /build/buildd/gdb-6.8/gdb/valops.c:2069: internal-error:
1608     sendCommand("set overload-resolution off");
1609     //sendCommand("set demangle-style none");
1610
1611     // From the docs:
1612     //  Stop means reenter debugger if this signal happens (implies print).
1613     //  Print means print a message if this signal happens.
1614     //  Pass means let program see this signal;
1615     //  otherwise program doesn't know.
1616     //  Pass and Stop may be combined.
1617     // We need "print" as otherwise we would get no feedback whatsoever
1618     // Custom Dumper crashs which happen regularily for when accessing
1619     // uninitialized variables.
1620     sendCommand("handle SIGSEGV nopass stop print");
1621
1622     // This is useful to kill the inferior whenever gdb dies.
1623     //sendCommand("handle SIGTERM pass nostop print");
1624
1625     sendCommand("set unwindonsignal on");
1626     sendCommand("pwd", GdbQueryPwd);
1627     sendCommand("set width 0");
1628     sendCommand("set height 0");
1629
1630     #ifdef Q_OS_MAC
1631     sendCommand("-gdb-set inferior-auto-start-cfm off");
1632     sendCommand("-gdb-set sharedLibrary load-rules "
1633             "dyld \".*libSystem.*\" all "
1634             "dyld \".*libauto.*\" all "
1635             "dyld \".*AppKit.*\" all "
1636             "dyld \".*PBGDBIntrospectionSupport.*\" all "
1637             "dyld \".*Foundation.*\" all "
1638             "dyld \".*CFDataFormatters.*\" all "
1639             "dyld \".*libobjc.*\" all "
1640             "dyld \".*CarbonDataFormatters.*\" all");
1641     #endif
1642
1643     QString scriptFileName = q->settings()->m_scriptFile;
1644     if (!scriptFileName.isEmpty()) {
1645         QFile scriptFile(scriptFileName);
1646         if (scriptFile.open(QIODevice::ReadOnly)) {
1647             sendCommand("source " + scriptFileName);
1648         } else {
1649             QMessageBox::warning(q->mainWindow(),
1650             tr("Cannot find debugger initialization script"),
1651             tr("The debugger settings point to a script file at '%1' "
1652                "which is not accessible. If a script file is not needed, "
1653                "consider clearing that entry to avoid this warning. "
1654               ).arg(scriptFileName));
1655         }
1656     }
1657
1658     if (q->startMode() == DebuggerManager::AttachExternal) {
1659         sendCommand("attach " + QString::number(q->m_attachedPID));
1660     } else {
1661         // StartInternal or StartExternal
1662         emit gdbInputAvailable(QString(), QString());
1663         sendCommand("-file-exec-and-symbols " + fileName, GdbFileExecAndSymbols);
1664         //sendCommand("file " + fileName, GdbFileExecAndSymbols);
1665         #ifdef Q_OS_MAC
1666         sendCommand("sharedlibrary apply-load-rules all");
1667         #endif
1668         //setTokenBarrier();
1669         if (!q->m_processArgs.isEmpty())
1670             sendCommand("-exec-arguments " + q->m_processArgs.join(" "));
1671         #ifndef Q_OS_MAC
1672         sendCommand("set auto-solib-add off");
1673         #endif
1674         sendCommand("x/2i " + startSymbolName(), GdbStart);
1675     }
1676
1677     // set all to "pending"
1678     if (q->startMode() == DebuggerManager::AttachExternal)
1679         qq->breakHandler()->removeAllBreakpoints();
1680     else
1681         qq->breakHandler()->setAllPending();
1682
1683     return true;
1684 }
1685
1686 void GdbEngine::continueInferior()
1687 {
1688     q->resetLocation();
1689     setTokenBarrier();
1690     emit gdbInputAvailable(QString(), QString());
1691     qq->notifyInferiorRunningRequested();
1692     sendCommand("-exec-continue", GdbExecContinue);
1693 }
1694
1695 void GdbEngine::handleStart(const GdbResultRecord &response)
1696 {
1697     if (response.resultClass == GdbResultDone) {
1698         // stdout:&"x/2i _start\n"
1699         // stdout:~"0x404540 <_start>:\txor    %ebp,%ebp\n"
1700         // stdout:~"0x404542 <_start+2>:\tmov    %rdx,%r9\n"
1701         QString msg = response.data.findChild("consolestreamoutput").data();
1702         #ifdef Q_OS_MAC
1703         // this ends up in 'gettimeoftheday' or such on MacOS 10.4
1704         QRegExp needle("0x([0-9a-f]+) <.*\\+.*>:");
1705         #else
1706         QRegExp needle("0x([0-9a-f]+) <" + startSymbolName() + "\\+.*>:");
1707         #endif
1708         if (needle.lastIndexIn(msg) != -1) {
1709             //debugMessage("STREAM: " + msg + " " + needle.cap(1));
1710             sendCommand("tbreak *0x" + needle.cap(1));
1711             m_waitingForFirstBreakpointToBeHit = true;
1712             qq->notifyInferiorRunningRequested();
1713             sendCommand("-exec-run");
1714         } else {
1715             debugMessage("PARSING START ADDRESS FAILED: " + msg);
1716         }
1717     } else if (response.resultClass == GdbResultError) {
1718         debugMessage("PARSING START ADDRESS FAILED: " + response.toString());
1719     }
1720 }
1721
1722 void GdbEngine::stepExec()
1723 {
1724     setTokenBarrier();
1725     emit gdbInputAvailable(QString(), QString());
1726     qq->notifyInferiorRunningRequested();
1727     sendCommand("-exec-step", GdbExecStep);
1728 }
1729
1730 void GdbEngine::stepIExec()
1731 {
1732     setTokenBarrier();
1733     qq->notifyInferiorRunningRequested();
1734     sendCommand("-exec-step-instruction", GdbExecStepI);
1735 }
1736
1737 void GdbEngine::stepOutExec()
1738 {
1739     setTokenBarrier();
1740     qq->notifyInferiorRunningRequested();
1741     sendCommand("-exec-finish", GdbExecFinish);
1742 }
1743
1744 void GdbEngine::nextExec()
1745 {
1746     setTokenBarrier();
1747     emit gdbInputAvailable(QString(), QString());
1748     qq->notifyInferiorRunningRequested();
1749     sendCommand("-exec-next", GdbExecNext);
1750 }
1751
1752 void GdbEngine::nextIExec()
1753 {
1754     setTokenBarrier();
1755     qq->notifyInferiorRunningRequested();
1756     sendCommand("-exec-next-instruction", GdbExecNextI);
1757 }
1758
1759 void GdbEngine::runToLineExec(const QString &fileName, int lineNumber)
1760 {
1761     setTokenBarrier();
1762     qq->notifyInferiorRunningRequested();
1763     sendCommand("-exec-until " + fileName + ":" + QString::number(lineNumber));
1764 }
1765
1766 void GdbEngine::runToFunctionExec(const QString &functionName)
1767 {
1768     setTokenBarrier();
1769     sendCommand("-break-insert -t " + functionName);
1770     qq->notifyInferiorRunningRequested();
1771     sendCommand("-exec-continue", GdbExecRunToFunction);
1772 }
1773
1774 void GdbEngine::jumpToLineExec(const QString &fileName, int lineNumber)
1775 {
1776 #if 1
1777     // not available everywhere?
1778     //sendCliCommand("tbreak " + fileName + ":" + QString::number(lineNumber));
1779     sendCommand("-break-insert -t " + fileName + ":" + QString::number(lineNumber));
1780     sendCommand("jump " + fileName + ":" + QString::number(lineNumber));
1781     // will produce something like
1782     //  &"jump /home/apoenitz/dev/work/test1/test1.cpp:242"
1783     //  ~"Continuing at 0x4058f3."
1784     //  ~"run1 (argc=1, argv=0x7fffbf1f5538) at test1.cpp:242"
1785     //  ~"242\t x *= 2;"
1786     //  23^done"
1787     q->gotoLocation(fileName, lineNumber, true);
1788     //setBreakpoint();
1789     //sendCommand("jump " + fileName + ":" + QString::number(lineNumber));
1790 #else
1791     q->gotoLocation(fileName, lineNumber, true);
1792     setBreakpoint(fileName, lineNumber);
1793     sendCommand("jump " + fileName + ":" + QString::number(lineNumber));
1794 #endif
1795 }
1796
1797 /*!
1798     \fn void GdbEngine::setTokenBarrier()
1799     \brief Sets up internal structures to handle a new debugger turn.
1800
1801     This method is called at the beginnign of all step/next/finish etc.
1802     debugger functions.
1803 */
1804
1805 void GdbEngine::setTokenBarrier()
1806 {
1807     m_oldestAcceptableToken = currentToken();
1808 }
1809
1810 void GdbEngine::setDebugDumpers(bool on)
1811 {
1812     if (on) {
1813         debugMessage("SWITCHING ON DUMPER DEBUGGING");
1814         sendCommand("set unwindonsignal off");
1815         q->breakByFunction("qDumpObjectData440");
1816         //updateLocals();
1817     } else {
1818         debugMessage("SWITCHING OFF DUMPER DEBUGGING");
1819         sendCommand("set unwindonsignal on");
1820     }
1821 }
1822
1823
1824 //////////////////////////////////////////////////////////////////////
1825 //
1826 // Breakpoint specific stuff
1827 //
1828 //////////////////////////////////////////////////////////////////////
1829
1830 void GdbEngine::breakpointDataFromOutput(BreakpointData *data, const GdbMi &bkpt)
1831 {
1832     if (!bkpt.isValid())
1833         return;
1834     if (!data)
1835         return;
1836     data->pending = false;
1837     data->bpMultiple = false;
1838     data->bpCondition.clear();
1839     QStringList files;
1840     foreach (const GdbMi &child, bkpt.children()) {
1841         if (child.hasName("number")) {
1842             data->bpNumber = child.data();
1843         } else if (child.hasName("func")) {
1844             data->bpFuncName = child.data();
1845         } else if (child.hasName("addr")) {
1846             // <MULTIPLE> happens in constructors. In this case there are
1847             // _two_ fields named "addr" in the response. On Linux that is...
1848             if (child.data() == "<MULTIPLE>")
1849                 data->bpMultiple = true;
1850             else
1851                 data->bpAddress = child.data();
1852         } else if (child.hasName("file")) {
1853             files.append(child.data());
1854         } else if (child.hasName("fullname")) {
1855             QString fullName = child.data();
1856             #ifdef Q_OS_WIN
1857             fullName = QDir::cleanPath(fullName);
1858             #endif
1859             files.prepend(fullName);
1860         } else if (child.hasName("line")) {
1861             data->bpLineNumber = child.data();
1862             if (child.data().toInt())
1863                 data->markerLineNumber = child.data().toInt();
1864         } else if (child.hasName("cond")) {
1865             data->bpCondition = child.data();
1866             // gdb 6.3 likes to "rewrite" conditions. Just accept that fact.
1867             if (data->bpCondition != data->condition && data->conditionsMatch())
1868                 data->condition = data->bpCondition;
1869         }
1870         else if (child.hasName("pending")) {
1871             data->pending = true;
1872             int pos = child.data().lastIndexOf(':');
1873             if (pos > 0) {
1874                 data->bpLineNumber = child.data().mid(pos + 1);
1875                 data->markerLineNumber = child.data().mid(pos + 1).toInt();
1876                 files.prepend(child.data().left(pos));
1877             } else {
1878                 files.prepend(child.data());
1879             }
1880         }
1881     }
1882     // This field is not present.  Contents needs to be parsed from
1883     // the plain "ignore" response.
1884     //else if (child.hasName("ignore"))
1885     //    data->bpIgnoreCount = child.data();
1886
1887     QString name = fullName(files);
1888     if (data->bpFileName.isEmpty())
1889         data->bpFileName = name;
1890     if (data->markerFileName.isEmpty())
1891         data->markerFileName = name;
1892 }
1893
1894 void GdbEngine::sendInsertBreakpoint(int index)
1895 {
1896     const BreakpointData *data = qq->breakHandler()->at(index);
1897     QString where;
1898     if (data->funcName.isEmpty()) {
1899         where = data->fileName;
1900 #ifdef Q_OS_MAC
1901         // full names do not work on Mac/MI
1902         QFileInfo fi(data->fileName);
1903         where = fi.fileName();
1904         //where = fi.absoluteFilePath();
1905 #endif
1906 #ifdef Q_OS_WIN
1907         // full names do not work on Mac/MI
1908         QFileInfo fi(data->fileName);
1909         where = fi.fileName();
1910     //where = m_manager->shortName(data->fileName);
1911         //if (where.isEmpty())
1912         //    where = data->fileName;
1913 #endif
1914         // we need something like   "\"file name.cpp\":100"  to
1915         // survive the gdb command line parser with file names intact
1916         where = "\"\\\"" + where + "\\\":" + data->lineNumber + "\"";
1917     } else {
1918         where = data->funcName;
1919     }
1920
1921     // set up fallback in case of pending breakpoints which aren't handled
1922     // by the MI interface
1923 #ifdef Q_OS_LINUX
1924     QString cmd = "-break-insert ";
1925     //if (!data->condition.isEmpty())
1926     //    cmd += "-c " + data->condition + " ";
1927     cmd += where;
1928 #endif
1929 #ifdef Q_OS_MAC
1930     QString cmd = "-break-insert -l -1 ";
1931     //if (!data->condition.isEmpty())
1932     //    cmd += "-c " + data->condition + " ";
1933     cmd += where;
1934 #endif
1935 #ifdef Q_OS_WIN
1936     QString cmd = "-break-insert ";
1937     //if (!data->condition.isEmpty())
1938     //    cmd += "-c " + data->condition + " ";
1939     cmd += where;
1940 #endif
1941     debugMessage(QString("Current state: %1").arg(q->status()));
1942     sendCommand(cmd, BreakInsert, index, NeedsStop);
1943 }
1944
1945 void GdbEngine::handleBreakList(const GdbResultRecord &record)
1946 {
1947     // 45^done,BreakpointTable={nr_rows="2",nr_cols="6",hdr=[
1948     // {width="3",alignment="-1",col_name="number",colhdr="Num"}, ...
1949     // body=[bkpt={number="1",type="breakpoint",disp="keep",enabled="y",
1950     //  addr="0x000000000040109e",func="main",file="app.cpp",
1951     //  fullname="/home/apoenitz/dev/work/plugintest/app/app.cpp",
1952     //  line="11",times="1"},
1953     //  bkpt={number="2",type="breakpoint",disp="keep",enabled="y",
1954     //  addr="<PENDING>",pending="plugin.cpp:7",times="0"}] ... }
1955
1956     if (record.resultClass == GdbResultDone) {
1957         GdbMi table = record.data.findChild("BreakpointTable");
1958         if (table.isValid())
1959             handleBreakList(table);
1960     }
1961 }
1962
1963 void GdbEngine::handleBreakList(const GdbMi &table)
1964 {
1965     //qDebug() << "GdbEngine::handleOutput: table: "
1966     //  << table.toString();
1967     GdbMi body = table.findChild("body");
1968     //qDebug() << "GdbEngine::handleOutput: body: "
1969     //  << body.toString();
1970     QList<GdbMi> bkpts;
1971     if (body.isValid()) {
1972         // Non-Mac
1973         bkpts = body.children();
1974     } else {
1975         // Mac
1976         bkpts = table.children();
1977         // remove the 'hdr' and artificial items
1978         //qDebug() << "FOUND " << bkpts.size() << " BREAKPOINTS";
1979         for (int i = bkpts.size(); --i >= 0; ) {
1980             int num = bkpts.at(i).findChild("number").data().toInt();
1981             if (num <= 0) {
1982                 //qDebug() << "REMOVING " << i << bkpts.at(i).toString();
1983                 bkpts.removeAt(i);
1984             }
1985         }
1986         //qDebug() << "LEFT " << bkpts.size() << " BREAKPOINTS";
1987     }
1988
1989     BreakHandler *handler = qq->breakHandler();
1990     for (int index = 0; index != bkpts.size(); ++index) {
1991         BreakpointData temp(handler);
1992         breakpointDataFromOutput(&temp, bkpts.at(index));
1993         int found = handler->findBreakpoint(temp);
1994         if (found != -1)
1995             breakpointDataFromOutput(handler->at(found), bkpts.at(index));
1996         //else
1997             //qDebug() << "CANNOT HANDLE RESPONSE " << bkpts.at(index).toString();
1998     }
1999
2000     attemptBreakpointSynchronization();
2001     handler->updateMarkers();
2002 }
2003
2004
2005 void GdbEngine::handleBreakIgnore(const GdbResultRecord &record, int index)
2006 {
2007     // gdb 6.8:
2008     // ignore 2 0:
2009     // ~"Will stop next time breakpoint 2 is reached.\n"
2010     // 28^done
2011     // ignore 2 12:
2012     // &"ignore 2 12\n"
2013     // ~"Will ignore next 12 crossings of breakpoint 2.\n"
2014     // 29^done
2015     //
2016     // gdb 6.3 does not produce any console output
2017     BreakHandler *handler = qq->breakHandler();
2018     if (record.resultClass == GdbResultDone && index < handler->size()) {
2019         QString msg = record.data.findChild("consolestreamoutput").data();
2020         BreakpointData *data = handler->at(index);
2021         //if (msg.contains("Will stop next time breakpoint")) {
2022         //    data->bpIgnoreCount = "0";
2023         //} else if (msg.contains("Will ignore next")) {
2024         //    data->bpIgnoreCount = data->ignoreCount;
2025         //}
2026         // FIXME: this assumes it is doing the right thing...
2027         data->bpIgnoreCount = data->ignoreCount;
2028         attemptBreakpointSynchronization();
2029         handler->updateMarkers();
2030     }
2031 }
2032
2033 void GdbEngine::handleBreakCondition(const GdbResultRecord &record, int index)
2034 {
2035     BreakHandler *handler = qq->breakHandler();
2036     if (record.resultClass == GdbResultDone) {
2037         // we just assume it was successful. otherwise we had to parse
2038         // the output stream data
2039         BreakpointData *data = handler->at(index);
2040         //qDebug() << "HANDLE BREAK CONDITION " << index << data->condition;
2041         data->bpCondition = data->condition;
2042         attemptBreakpointSynchronization();
2043         handler->updateMarkers();
2044     } else if (record.resultClass == GdbResultError) {
2045         QString msg = record.data.findChild("msg").data();
2046         // happens on Mac
2047         if (1 || msg.startsWith("Error parsing breakpoint condition. "
2048                 " Will try again when we hit the breakpoint.")) {
2049             BreakpointData *data = handler->at(index);
2050             //qDebug() << "ERROR BREAK CONDITION " << index << data->condition;
2051             data->bpCondition = data->condition;
2052             attemptBreakpointSynchronization();
2053             handler->updateMarkers();
2054         }
2055     }
2056 }
2057
2058 void GdbEngine::handleBreakInsert(const GdbResultRecord &record, int index)
2059 {
2060     BreakHandler *handler = qq->breakHandler();
2061     if (record.resultClass == GdbResultDone) {
2062         //qDebug() << "HANDLE BREAK INSERT " << index;
2063 //#ifdef Q_OS_MAC
2064         // interesting only on Mac?
2065         BreakpointData *data = handler->at(index);
2066         GdbMi bkpt = record.data.findChild("bkpt");
2067         //qDebug() << "BKPT: " << bkpt.toString() << " DATA" << data->toToolTip();
2068         breakpointDataFromOutput(data, bkpt);
2069 //#endif
2070         attemptBreakpointSynchronization();
2071         handler->updateMarkers();
2072     } else if (record.resultClass == GdbResultError) {
2073         const BreakpointData *data = handler->at(index);
2074 #ifdef Q_OS_LINUX
2075         //QString where = "\"\\\"" + data->fileName + "\\\":"
2076         //    + data->lineNumber + "\"";
2077         QString where = "\"" + data->fileName + "\":"
2078             + data->lineNumber;
2079         sendCommand("break " + where, BreakInsert1, index);
2080 #endif
2081 #ifdef Q_OS_MAC
2082         QFileInfo fi(data->fileName);
2083         QString where = "\"" + fi.fileName() + "\":"
2084             + data->lineNumber;
2085         sendCommand("break " + where, BreakInsert1, index);
2086 #endif
2087 #ifdef Q_OS_WIN
2088         QFileInfo fi(data->fileName);
2089         QString where = "\"" + fi.fileName() + "\":"
2090             + data->lineNumber;
2091         //QString where = m_data->fileName + QLatin1Char(':') + data->lineNumber;
2092         sendCommand("break " + where, BreakInsert1, index);
2093 #endif
2094     }
2095 }
2096
2097 void GdbEngine::extractDataFromInfoBreak(const QString &output, BreakpointData *data)
2098 {
2099     data->bpFileName = "<MULTIPLE>";
2100
2101     //qDebug() << output;
2102     if (output.isEmpty())
2103         return;
2104     // "Num     Type           Disp Enb Address            What
2105     // 4       breakpoint     keep y   <MULTIPLE>         0x00000000004066ad
2106     // 4.1                         y     0x00000000004066ad in CTorTester
2107     //  at /data5/dev/ide/main/tests/manual/gdbdebugger/simple/app.cpp:124
2108     // - or - 
2109     // everything on a single line on Windows for constructors of classes
2110     // within namespaces.
2111     // Sometimes the path is relative too.
2112   
2113     QRegExp re("MULTIPLE.*(0x[0-9a-f]+) in (.*)\\s+at (.*):([\\d]+)([^\\d]|$)");
2114     re.setMinimal(true);
2115
2116     if (re.indexIn(output) != -1) {
2117         data->bpAddress = re.cap(1);
2118         data->bpFuncName = re.cap(2).trimmed();
2119         data->bpLineNumber = re.cap(4);
2120         QString full = fullName(re.cap(3));
2121         data->markerLineNumber = data->bpLineNumber.toInt();
2122         data->markerFileName = full;
2123         data->bpFileName = full;
2124         //qDebug() << "FOUND BREAKPOINT\n" << output
2125         //    << re.cap(1) << "\n" << re.cap(2) << "\n"
2126         //    << re.cap(3) << "\n" << re.cap(4) << "\n";
2127     } else {
2128         qDebug() << "COULD NOT MATCH " << re.pattern() << " AND " << output;
2129         data->bpNumber = "<unavailable>";
2130     }
2131 }
2132
2133 void GdbEngine::handleBreakInfo(const GdbResultRecord &record, int bpNumber)
2134 {
2135     BreakHandler *handler = qq->breakHandler();
2136     if (record.resultClass == GdbResultDone) {
2137         // Old-style output for multiple breakpoints, presumably in a
2138         // constructor
2139         int found = handler->findBreakpoint(bpNumber);
2140         if (found != -1) {
2141             QString str = record.data.findChild("consolestreamoutput").data();
2142             extractDataFromInfoBreak(str, handler->at(found));
2143             handler->updateMarkers();
2144             attemptBreakpointSynchronization(); // trigger "ready"
2145         }
2146     }
2147 }
2148
2149 void GdbEngine::handleBreakInsert1(const GdbResultRecord &record, int index)
2150 {
2151     BreakHandler *handler = qq->breakHandler();
2152     if (record.resultClass == GdbResultDone) {
2153         // Pending breakpoints in dylibs on Mac only?
2154         BreakpointData *data = handler->at(index);
2155         GdbMi bkpt = record.data.findChild("bkpt");
2156         breakpointDataFromOutput(data, bkpt);
2157         attemptBreakpointSynchronization(); // trigger "ready"
2158         handler->updateMarkers();
2159     } else if (record.resultClass == GdbResultError) {
2160         qDebug() << "INSERTING BREAKPOINT WITH BASE NAME FAILED. GIVING UP";
2161         BreakpointData *data = handler->at(index);
2162         data->bpNumber = "<unavailable>";
2163         attemptBreakpointSynchronization(); // trigger "ready"
2164         handler->updateMarkers();
2165     }
2166 }
2167
2168 void GdbEngine::attemptBreakpointSynchronization()
2169 {
2170     // Non-lethal check for nested calls
2171     static bool inBreakpointSychronization = false;
2172     QTC_ASSERT(!inBreakpointSychronization, /**/);
2173     inBreakpointSychronization = true;
2174
2175     BreakHandler *handler = qq->breakHandler();
2176
2177     foreach (BreakpointData *data, handler->takeRemovedBreakpoints()) {
2178         QString bpNumber = data->bpNumber;
2179         debugMessage(QString("DELETING BP %1 IN %2").arg(bpNumber)
2180             .arg(data->markerFileName));
2181         if (!bpNumber.trimmed().isEmpty())
2182             sendCommand("-break-delete " + bpNumber, BreakDelete, QVariant(),
2183                 NeedsStop);
2184         delete data;
2185     }
2186
2187     bool updateNeeded = false;
2188
2189     for (int index = 0; index != handler->size(); ++index) {
2190         BreakpointData *data = handler->at(index);
2191         // multiple breakpoints?
2192         if (data->bpMultiple && data->bpFileName.isEmpty()) {
2193             sendCommand(QString("info break %1").arg(data->bpNumber),
2194                 BreakInfo, data->bpNumber.toInt());
2195             updateNeeded = true;
2196             break;
2197         }
2198     }
2199
2200     for (int index = 0; index != handler->size(); ++index) {
2201         BreakpointData *data = handler->at(index);
2202         // unset breakpoints?
2203         if (data->bpNumber.isEmpty()) {
2204             data->bpNumber = " ";
2205             sendInsertBreakpoint(index);
2206             //qDebug() << "UPDATE NEEDED BECAUSE OF UNKNOWN BREAKPOINT";
2207             updateNeeded = true;
2208             break;
2209         }
2210     }
2211
2212     if (!updateNeeded) {
2213         for (int index = 0; index != handler->size(); ++index) {
2214             BreakpointData *data = handler->at(index);
2215             // update conditions if needed
2216             if (data->bpNumber.toInt() && data->condition != data->bpCondition
2217                    && !data->conditionsMatch()) {
2218                 sendCommand(QString("condition %1 %2").arg(data->bpNumber)
2219                     .arg(data->condition), BreakCondition, index);
2220                 //qDebug() << "UPDATE NEEDED BECAUSE OF CONDITION"
2221                 //    << data->condition << data->bpCondition;
2222                 updateNeeded = true;
2223                 break;
2224             }
2225             // update ignorecount if needed
2226             if (data->bpNumber.toInt() && data->ignoreCount != data->bpIgnoreCount) {
2227                 sendCommand(QString("ignore %1 %2").arg(data->bpNumber)
2228                     .arg(data->ignoreCount), BreakIgnore, index);
2229                 updateNeeded = true;
2230                 break;
2231             }
2232         }
2233     }
2234
2235     for (int index = 0; index != handler->size(); ++index) {
2236         // happens sometimes on Mac. Brush over symptoms
2237         BreakpointData *data = handler->at(index);
2238         if (data->markerFileName.startsWith("../")) {
2239             data->markerFileName = fullName(data->markerFileName);
2240             handler->updateMarkers();
2241         }
2242     }
2243
2244     if (!updateNeeded && m_waitingForBreakpointSynchronizationToContinue) {
2245         m_waitingForBreakpointSynchronizationToContinue = false;
2246         // we continue the execution
2247         continueInferior();
2248     }
2249
2250     inBreakpointSychronization = false;
2251 }
2252
2253
2254 //////////////////////////////////////////////////////////////////////
2255 //
2256 // Disassembler specific stuff
2257 //
2258 //////////////////////////////////////////////////////////////////////
2259
2260 void GdbEngine::reloadDisassembler()
2261 {
2262     emit sendCommand("disassemble", DisassemblerList, m_address);
2263 }
2264
2265 void GdbEngine::handleDisassemblerList(const GdbResultRecord &record,
2266     const QString &cookie)
2267 {
2268     QList<DisassemblerLine> lines;
2269     static const QString pad = QLatin1String("    ");
2270     int currentLine = -1;
2271     if (record.resultClass == GdbResultDone) {
2272         QString res = record.data.findChild("consolestreamoutput").data();
2273         QTextStream ts(&res, QIODevice::ReadOnly);
2274         while (!ts.atEnd()) {
2275             //0x0000000000405fd8 <_ZN11QTextStreamD1Ev@plt+0>:
2276             //    jmpq   *2151890(%rip)    # 0x6135b0 <_GLOBAL_OFFSET_TABLE_+640>
2277             //0x0000000000405fde <_ZN11QTextStreamD1Ev@plt+6>:
2278             //    pushq  $0x4d
2279             //0x0000000000405fe3 <_ZN11QTextStreamD1Ev@plt+11>:
2280             //    jmpq   0x405af8 <_init+24>
2281             //0x0000000000405fe8 <_ZN9QHashData6rehashEi@plt+0>:
2282             //    jmpq   *2151882(%rip)    # 0x6135b8 <_GLOBAL_OFFSET_TABLE_+648>
2283             QString str = ts.readLine();
2284             if (!str.startsWith(QLatin1String("0x"))) {
2285                 //qDebug() << "IGNORING DISASSEMBLER" << str;
2286                 continue;
2287             }
2288             DisassemblerLine line;
2289             QTextStream ts(&str, QIODevice::ReadOnly);
2290             ts >> line.address >> line.symbol;
2291             line.mnemonic = ts.readLine().trimmed();
2292             if (line.symbol.endsWith(QLatin1Char(':')))
2293                 line.symbol.chop(1);
2294             line.addressDisplay = line.address + pad;
2295             if (line.addressDisplay.startsWith(QLatin1String("0x00000000")))
2296                 line.addressDisplay.replace(2, 8, QString());
2297             line.symbolDisplay = line.symbol + pad;
2298
2299             if (line.address == cookie)
2300                 currentLine = lines.size();
2301
2302             lines.append(line);
2303         }
2304     } else {
2305         DisassemblerLine line;
2306         line.addressDisplay = tr("<could not retreive module information>");
2307         lines.append(line);
2308     }
2309
2310     qq->disassemblerHandler()->setLines(lines);
2311     if (currentLine != -1)
2312         qq->disassemblerHandler()->setCurrentLine(currentLine);
2313 }
2314
2315
2316 //////////////////////////////////////////////////////////////////////
2317 //
2318 // Modules specific stuff
2319 //
2320 //////////////////////////////////////////////////////////////////////
2321
2322 void GdbEngine::loadSymbols(const QString &moduleName)
2323 {
2324     // FIXME: gdb does not understand quoted names here (tested with 6.8)
2325     sendCommand("sharedlibrary " + dotEscape(moduleName));
2326     reloadModules();
2327 }
2328
2329 void GdbEngine::loadAllSymbols()
2330 {
2331     sendCommand("sharedlibrary .*");
2332     reloadModules();
2333 }
2334
2335 void GdbEngine::reloadModules()
2336 {
2337     sendCommand("info shared", ModulesList, QVariant());
2338 }
2339
2340 void GdbEngine::handleModulesList(const GdbResultRecord &record)
2341 {
2342     QList<Module> modules;
2343     if (record.resultClass == GdbResultDone) {
2344         QString data = record.data.findChild("consolestreamoutput").data();
2345         QTextStream ts(&data, QIODevice::ReadOnly);
2346         while (!ts.atEnd()) {
2347             QString line = ts.readLine();
2348             if (!line.startsWith("0x"))
2349                 continue;
2350             Module module;
2351             QString symbolsRead;
2352             QTextStream ts(&line, QIODevice::ReadOnly);
2353             ts >> module.startAddress >> module.endAddress >> symbolsRead;
2354             module.moduleName = ts.readLine().trimmed();
2355             module.symbolsRead = (symbolsRead == "Yes");
2356             modules.append(module);
2357         }
2358     }
2359     qq->modulesHandler()->setModules(modules);
2360 }
2361
2362
2363 //////////////////////////////////////////////////////////////////////
2364 //
2365 // Source files specific stuff
2366 //
2367 //////////////////////////////////////////////////////////////////////
2368
2369 void GdbEngine::reloadSourceFiles()
2370 {
2371     sendCommand("-file-list-exec-source-files", GdbQuerySources);
2372 }
2373
2374
2375 //////////////////////////////////////////////////////////////////////
2376 //
2377 // Stack specific stuff
2378 //
2379 //////////////////////////////////////////////////////////////////////
2380
2381 void GdbEngine::handleStackSelectThread(const GdbResultRecord &record, int)
2382 {
2383     Q_UNUSED(record);
2384     //qDebug("FIXME: StackHandler::handleOutput: SelectThread");
2385     q->showStatusMessage(tr("Retrieving data for stack view..."), 3000);
2386     sendCommand("-stack-list-frames", StackListFrames);
2387 }
2388
2389
2390 void GdbEngine::handleStackListFrames(const GdbResultRecord &record)
2391 {
2392     QList<StackFrame> stackFrames;
2393
2394     const GdbMi stack = record.data.findChild("stack");
2395     QString dummy = stack.toString();
2396     if (!stack.isValid()) {
2397         qDebug() << "FIXME: stack: " << stack.toString();
2398         return;
2399     }
2400
2401     int topFrame = -1;
2402
2403     for (int i = 0; i != stack.childCount(); ++i) {
2404         //qDebug() << "HANDLING FRAME: " << stack.childAt(i).toString();
2405         const GdbMi frameMi = stack.childAt(i);
2406         StackFrame frame;
2407         frame.level = i;
2408         QStringList files;
2409         files.append(frameMi.findChild("fullname").data());
2410         files.append(frameMi.findChild("file").data());
2411         frame.file = fullName(files);
2412         frame.function = frameMi.findChild("func").data();
2413         frame.from = frameMi.findChild("from").data();
2414         frame.line = frameMi.findChild("line").data().toInt();
2415         frame.address = frameMi.findChild("addr").data();
2416
2417         stackFrames.append(frame);
2418
2419 #ifdef Q_OS_WIN
2420         const bool isBogus =
2421             // Assume this is wrong and points to some strange stl_algobase
2422             // implementation. Happens on Karsten's XP system with Gdb 5.50
2423             (frame.file.endsWith("/bits/stl_algobase.h") && frame.line == 150)
2424             // Also wrong. Happens on Vista with Gdb 5.50
2425                || (frame.function == "operator new" && frame.line == 151);
2426
2427         // immediately leave bogus frames
2428         if (topFrame == -1 && isBogus) {
2429             sendCommand("-exec-finish");
2430             return;
2431         }
2432
2433 #endif
2434
2435         // Initialize top frame to the first valid frame
2436         const bool isValid = !frame.file.isEmpty() && !frame.function.isEmpty();
2437         if (isValid && topFrame == -1)
2438             topFrame = i;
2439     }
2440
2441     qq->stackHandler()->setFrames(stackFrames);
2442
2443 #if 0
2444     if (0 && topFrame != -1) {
2445         // updates of locals already triggered early
2446         const StackFrame &frame = qq->stackHandler()->currentFrame();
2447         bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable();
2448         if (usable)
2449             q->gotoLocation(frame.file, frame.line, true);
2450         else
2451             qDebug() << "FULL NAME NOT USABLE 0: " << frame.file;
2452     } else {
2453         activateFrame(topFrame);
2454     }
2455 #else
2456     if (topFrame != -1) {
2457         // updates of locals already triggered early
2458         const StackFrame &frame = qq->stackHandler()->currentFrame();
2459         bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable();
2460         if (usable)
2461             q->gotoLocation(frame.file, frame.line, true);
2462         else
2463             qDebug() << "FULL NAME NOT USABLE 0: " << frame.file << topFrame;
2464     }
2465 #endif
2466 }
2467
2468 void GdbEngine::selectThread(int index)
2469 {
2470     //reset location arrow
2471     q->resetLocation();
2472
2473     ThreadsHandler *threadsHandler = qq->threadsHandler();
2474     threadsHandler->setCurrentThread(index);
2475
2476     QList<ThreadData> threads = threadsHandler->threads();
2477     QTC_ASSERT(index < threads.size(), return);
2478     int id = threads.at(index).id;
2479     q->showStatusMessage(tr("Retrieving data for stack view..."), 10000);
2480     sendCommand(QLatin1String("-thread-select ") + QString::number(id),
2481         StackSelectThread);
2482 }
2483
2484 void GdbEngine::activateFrame(int frameIndex)
2485 {
2486     if (q->status() != DebuggerInferiorStopped)
2487         return;
2488
2489     StackHandler *stackHandler = qq->stackHandler();
2490     int oldIndex = stackHandler->currentIndex();
2491     //qDebug() << "ACTIVATE FRAME: " << frameIndex << oldIndex
2492     //    << stackHandler->currentIndex();
2493
2494     QTC_ASSERT(frameIndex < stackHandler->stackSize(), return);
2495
2496     if (oldIndex != frameIndex) {
2497         // Assuming this always succeeds saves a roundtrip.
2498         // Otherwise the lines below would need to get triggered
2499         // after a response to this -stack-select-frame here.
2500         sendCommand("-stack-select-frame " + QString::number(frameIndex));
2501
2502         stackHandler->setCurrentIndex(frameIndex);
2503         updateLocals();
2504     }
2505
2506     const StackFrame &frame = stackHandler->currentFrame();
2507
2508     bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable();
2509     if (usable)
2510         q->gotoLocation(frame.file, frame.line, true);
2511     else
2512         qDebug() << "FULL NAME NOT USABLE: " << frame.file;
2513 }
2514
2515 void GdbEngine::handleStackListThreads(const GdbResultRecord &record, int id)
2516 {
2517     // "72^done,{thread-ids={thread-id="2",thread-id="1"},number-of-threads="2"}
2518     const QList<GdbMi> items = record.data.findChild("thread-ids").children();
2519     QList<ThreadData> threads;
2520     int currentIndex = -1;
2521     for (int index = 0, n = items.size(); index != n; ++index) {
2522         ThreadData thread;
2523         thread.id = items.at(index).data().toInt();
2524         threads.append(thread);
2525         if (thread.id == id) {
2526             //qDebug() << "SETTING INDEX TO: " << index << " ID: "<< id << "RECOD: "<< record.toString();
2527             currentIndex = index;
2528         }
2529     }
2530     ThreadsHandler *threadsHandler = qq->threadsHandler();
2531     threadsHandler->setThreads(threads);
2532     threadsHandler->setCurrentThread(currentIndex);
2533 }
2534
2535
2536 //////////////////////////////////////////////////////////////////////
2537 //
2538 // Register specific stuff
2539 //
2540 //////////////////////////////////////////////////////////////////////
2541
2542 void GdbEngine::reloadRegisters()
2543 {
2544     QString format = qq->registerHandler()->model()->property(PROPERTY_REGISTER_FORMAT).toString();
2545     sendCommand("-data-list-register-values " + format, RegisterListValues);
2546 }
2547
2548 void GdbEngine::handleRegisterListNames(const GdbResultRecord &record)
2549 {
2550     if (record.resultClass != GdbResultDone)
2551         return;
2552
2553     QList<Register> registers;
2554     foreach (const GdbMi &item, record.data.findChild("register-names").children())
2555         registers.append(Register(item.data()));
2556
2557     qq->registerHandler()->setRegisters(registers);
2558 }
2559
2560 void GdbEngine::handleRegisterListValues(const GdbResultRecord &record)
2561 {
2562     if (record.resultClass != GdbResultDone)
2563         return;
2564
2565     QList<Register> registers = qq->registerHandler()->registers();
2566
2567     // 24^done,register-values=[{number="0",value="0xf423f"},...]
2568     foreach (const GdbMi &item, record.data.findChild("register-values").children()) {
2569         int index = item.findChild("number").data().toInt();
2570         if (index < registers.size()) {
2571             Register &reg = registers[index];
2572             QString value = item.findChild("value").data();
2573             reg.changed = (value != reg.value);
2574             if (reg.changed)
2575                 reg.value = value;
2576         }
2577     }
2578     qq->registerHandler()->setRegisters(registers);
2579 }
2580
2581
2582 //////////////////////////////////////////////////////////////////////
2583 //
2584 // Thread specific stuff
2585 //
2586 //////////////////////////////////////////////////////////////////////
2587
2588 bool GdbEngine::supportsThreads() const
2589 {
2590     // 6.3 crashes happily on -thread-list-ids. So don't use it.
2591     // The test below is a semi-random pick, 6.8 works fine
2592     return m_gdbVersion > 60500;
2593 }
2594
2595 //////////////////////////////////////////////////////////////////////
2596 //
2597 // Tooltip specific stuff
2598 //
2599 //////////////////////////////////////////////////////////////////////
2600
2601 static WatchData m_toolTip;
2602 static QString m_toolTipExpression;
2603 static QPoint m_toolTipPos;
2604 static QMap<QString, WatchData> m_toolTipCache;
2605
2606 static bool hasLetterOrNumber(const QString &exp)
2607 {
2608     for (int i = exp.size(); --i >= 0; )
2609         if (exp[i].isLetterOrNumber() || exp[i] == '_')
2610             return true;
2611     return false;
2612 }
2613
2614 static bool hasSideEffects(const QString &exp)
2615 {
2616     // FIXME: complete?
2617     return exp.contains("-=")
2618         || exp.contains("+=")
2619         || exp.contains("/=")
2620         || exp.contains("*=")
2621         || exp.contains("&=")
2622         || exp.contains("|=")
2623         || exp.contains("^=")
2624         || exp.contains("--")
2625         || exp.contains("++");
2626 }
2627
2628 static bool isKeyWord(const QString &exp)
2629 {
2630     // FIXME: incomplete
2631     return exp == QLatin1String("class")
2632         || exp == QLatin1String("const")
2633         || exp == QLatin1String("do")
2634         || exp == QLatin1String("if")
2635         || exp == QLatin1String("return")
2636         || exp == QLatin1String("struct")
2637         || exp == QLatin1String("template")
2638         || exp == QLatin1String("void")
2639         || exp == QLatin1String("volatile")
2640         || exp == QLatin1String("while");
2641 }
2642
2643 void GdbEngine::setToolTipExpression(const QPoint &pos, const QString &exp0)
2644 {
2645     //qDebug() << "SET TOOLTIP EXP" << pos << exp0;
2646     if (q->status() != DebuggerInferiorStopped) {
2647         //qDebug() << "SUPPRESSING DEBUGGER TOOLTIP, INFERIOR NOT STOPPED";
2648         return;
2649     }
2650     
2651     if (q->settings()->m_debugDumpers) {
2652         // minimize interference
2653         return;
2654     }
2655
2656     m_toolTipPos = pos;
2657     m_toolTipExpression = exp0;
2658     QString exp = exp0;
2659 /*
2660     if (m_toolTip.isTypePending()) {
2661         qDebug() << "suppressing duplicated tooltip creation";
2662         return;
2663     }
2664 */
2665     if (m_toolTipCache.contains(exp)) {
2666         const WatchData & data = m_toolTipCache[exp];
2667         // FIXME: qq->watchHandler()->collapseChildren(data.iname);
2668         insertData(data);
2669         return;
2670     }
2671
2672     QToolTip::hideText();
2673     if (exp.isEmpty() || exp.startsWith("#"))  {
2674         QToolTip::hideText();
2675         return;
2676     }
2677
2678     if (!hasLetterOrNumber(exp)) {
2679         QToolTip::showText(m_toolTipPos,
2680             "'" + exp + "' contains no identifier");
2681         return;
2682     }
2683
2684     if (isKeyWord(exp))
2685         return;
2686
2687     if (exp.startsWith('"') && exp.endsWith('"'))  {
2688         QToolTip::showText(m_toolTipPos, "String literal " + exp);
2689         return;
2690     }
2691
2692     if (exp.startsWith("++") || exp.startsWith("--"))
2693         exp = exp.mid(2);
2694
2695     if (exp.endsWith("++") || exp.endsWith("--"))
2696         exp = exp.mid(2);
2697
2698     if (exp.startsWith("<") || exp.startsWith("["))
2699         return;
2700
2701     if (hasSideEffects(exp)) {
2702         QToolTip::showText(m_toolTipPos,
2703             "Cowardly refusing to evaluate expression '" + exp
2704                 + "' with potential side effects");
2705         return;
2706     }
2707
2708     // Gdb crashes when creating a variable object with the name
2709     // of the type of 'this'
2710 /*
2711     for (int i = 0; i != m_currentLocals.childCount(); ++i) {
2712         if (m_currentLocals.childAt(i).exp == "this") {
2713             qDebug() << "THIS IN ROW " << i;
2714             if (m_currentLocals.childAt(i).type.startsWith(exp)) {
2715                 QToolTip::showText(m_toolTipPos,
2716                     exp + ": type of current 'this'");
2717                 qDebug() << " TOOLTIP CRASH SUPPRESSED";
2718                 return;
2719             }
2720             break;
2721         }
2722     }
2723 */
2724
2725     //if (m_manager->status() != DebuggerInferiorStopped)
2726     //    return;
2727
2728     // FIXME: 'exp' can contain illegal characters
2729     m_toolTip = WatchData();
2730     //m_toolTip.level = 0;
2731    // m_toolTip.row = 0;
2732    // m_toolTip.parentIndex = 2;
2733     m_toolTip.exp = exp;
2734     m_toolTip.name = exp;
2735     m_toolTip.iname = tooltipIName;
2736     insertData(m_toolTip);
2737     updateWatchModel2();
2738 }
2739
2740
2741 //////////////////////////////////////////////////////////////////////
2742 //
2743 // Watch specific stuff
2744 //
2745 //////////////////////////////////////////////////////////////////////
2746
2747 static const QString strNotInScope = QLatin1String("<not in scope>");
2748
2749 static bool isPointerType(const QString &type)
2750 {
2751     return type.endsWith("*") || type.endsWith("* const");
2752 }
2753
2754 static bool isAccessSpecifier(const QString &str)
2755 {
2756     static const QStringList items =
2757         QStringList() << "private" << "protected" << "public";
2758     return items.contains(str);
2759 }
2760
2761 static bool startsWithDigit(const QString &str)
2762 {
2763     return !str.isEmpty() && str[0] >= '0' && str[0] <= '9';
2764 }
2765
2766 QString stripPointerType(QString type)
2767 {
2768     if (type.endsWith("*"))
2769         type.chop(1);
2770     if (type.endsWith("* const"))
2771         type.chop(7);
2772     if (type.endsWith(' '))
2773         type.chop(1);
2774     return type;
2775 }
2776
2777 static QString gdbQuoteTypes(const QString &type)
2778 {
2779     // gdb does not understand sizeof(Core::IFile*).
2780     // "sizeof('Core::IFile*')" is also not acceptable,
2781     // it needs to be "sizeof('Core::IFile'*)"
2782     //
2783     // We never will have a perfect solution here (even if we had a full blown
2784     // C++ parser as we do not have information on what is a type and what is
2785     // a vriable name. So "a<b>::c" could either be two comparisons of values
2786     // 'a', 'b' and '::c', or a nested type 'c' in a template 'a<b>'. We
2787     // assume here it is the latter.
2788     //return type;
2789
2790     // (*('myns::QPointer<myns::QObject>*'*)0x684060)" is not acceptable
2791     // (*('myns::QPointer<myns::QObject>'**)0x684060)" is acceptable
2792     if (isPointerType(type))
2793         return gdbQuoteTypes(stripPointerType(type)) + "*";
2794
2795     QString accu;
2796     QString result;
2797     int templateLevel = 0;
2798     for (int i = 0; i != type.size(); ++i) {
2799         QChar c = type.at(i);
2800         if (c.isLetterOrNumber() || c == '_' || c == ':' || c == ' ') {
2801             accu += c;
2802         } else if (c == '<') {
2803             ++templateLevel;
2804             accu += c;
2805         } else if (c == '<') {
2806             --templateLevel;
2807             accu += c;
2808         } else if (templateLevel > 0) {
2809             accu += c;
2810         } else {
2811             if (accu.contains(':') || accu.contains('<'))
2812                 result += '\'' + accu + '\'';
2813             else
2814                 result += accu;
2815             accu.clear();
2816             result += c;
2817         }
2818     }
2819     if (accu.contains(':') || accu.contains('<'))
2820         result += '\'' + accu + '\'';
2821     else
2822         result += accu;
2823     //qDebug() << "GDB_QUOTING" << type << " TO " << result;
2824
2825     return result;
2826 }
2827
2828 static void setWatchDataValue(WatchData &data, const GdbMi &mi,
2829     int encoding = 0)
2830 {
2831     if (mi.isValid()) {
2832         QByteArray ba;
2833         switch (encoding) {
2834             case 0: // unencoded 8 bit data
2835                 ba = mi.data();
2836                 break;
2837             case 1: //  base64 encoded 8 bit data
2838                 ba = QByteArray::fromBase64(mi.data());
2839                 ba = '"' + ba + '"';
2840                 break;
2841             case 2: //  base64 encoded 16 bit data
2842                 ba = QByteArray::fromBase64(mi.data());
2843                 ba = QString::fromUtf16((ushort *)ba.data(), ba.size() / 2).toUtf8();
2844                 ba = '"' + ba + '"';
2845                 break;
2846             case 3: //  base64 encoded 32 bit data
2847                 ba = QByteArray::fromBase64(mi.data());
2848                 ba = QString::fromUcs4((uint *)ba.data(), ba.size() / 4).toUtf8();
2849                 ba = '"' + ba + '"';
2850                 break;
2851         }
2852        data.setValue(ba);
2853     } else {
2854        data.setValueNeeded();
2855     }
2856 }
2857
2858 static void setWatchDataEditValue(WatchData &data, const GdbMi &mi)
2859 {
2860     if (mi.isValid())
2861         data.editvalue = mi.data();
2862 }
2863
2864 static void setWatchDataValueToolTip(WatchData &data, const GdbMi &mi)
2865 {
2866     if (mi.isValid())
2867         data.setValueToolTip(mi.data());
2868 }
2869
2870 static void setWatchDataChildCount(WatchData &data, const GdbMi &mi)
2871 {
2872     if (mi.isValid()) {
2873         data.childCount = mi.data().toInt();
2874         data.setChildCountUnneeded();
2875         if (data.childCount == 0)
2876             data.setChildrenUnneeded();
2877     } else {
2878         data.childCount = -1;
2879     }
2880 }
2881
2882 static void setWatchDataValueDisabled(WatchData &data, const GdbMi &mi)
2883 {
2884     if (mi.data() == "true")
2885         data.valuedisabled = true;
2886     else if (mi.data() == "false")
2887         data.valuedisabled = false;
2888 }
2889
2890 static void setWatchDataExpression(WatchData &data, const GdbMi &mi)
2891 {
2892     if (mi.isValid())
2893         data.exp = "(" + mi.data() + ")";
2894 }
2895
2896 static void setWatchDataAddress(WatchData &data, const GdbMi &mi)
2897 {
2898     if (mi.isValid()) {
2899         data.addr = mi.data();
2900         if (data.exp.isEmpty())
2901             data.exp = "(*(" + gdbQuoteTypes(data.type) + "*)" + data.addr + ")";
2902     }
2903 }
2904
2905 static bool extractTemplate(const QString &type, QString *tmplate, QString *inner)
2906 {
2907     // Input "Template<Inner1,Inner2,...>::Foo" will return "Template::Foo" in
2908     // 'tmplate' and "Inner1@Inner2@..." etc in 'inner'. Result indicates
2909     // whether parsing was successful
2910     int level = 0;
2911     bool skipSpace = false;
2912     for (int i = 0; i != type.size(); ++i) {
2913         QChar c = type[i];
2914         if (c == ' ' && skipSpace) {
2915             skipSpace = false;
2916         } else if (c == '<') {
2917             *(level == 0 ? tmplate : inner) += c;
2918             ++level;
2919         } else if (c == '>') {
2920             --level;
2921             *(level == 0 ? tmplate : inner) += c;
2922         } else if (c == ',') {
2923             *inner += (level == 1) ? '@' : ',';
2924             skipSpace = true;
2925         } else {
2926             *(level == 0 ? tmplate : inner) += c;
2927         }
2928     }
2929     *tmplate = tmplate->trimmed();
2930     *tmplate = tmplate->remove("<>");
2931     *inner = inner->trimmed();
2932     //qDebug() << "EXTRACT TEMPLATE: " << *tmplate << *inner << " FROM " << type;
2933     return !inner->isEmpty();
2934 }
2935
2936 static QString extractTypeFromPTypeOutput(const QString &str)
2937 {
2938     int pos0 = str.indexOf('=');
2939     int pos1 = str.indexOf('{');
2940     int pos2 = str.lastIndexOf('}');
2941     QString res = str;
2942     if (pos0 != -1 && pos1 != -1 && pos2 != -1)
2943         res = str.mid(pos0 + 2, pos1 - 1 - pos0)
2944             + " ... " + str.right(str.size() - pos2);
2945     return res.simplified();
2946 }
2947
2948 static bool isIntOrFloatType(const QString &type)
2949 {
2950     static const QStringList types = QStringList()
2951         << "char" << "int" << "short" << "float" << "double" << "long"
2952         << "bool" << "signed char" << "unsigned" << "unsigned char"
2953         << "unsigned int" << "unsigned long" << "long long"
2954         << "unsigned long long";
2955     return types.contains(type);
2956 }
2957
2958 static QString sizeofTypeExpression(const QString &type)
2959 {
2960     if (type.endsWith('*'))
2961         return "sizeof(void*)";
2962     if (type.endsWith('>'))
2963         return "sizeof(" + type + ")";
2964     return "sizeof(" + gdbQuoteTypes(type) + ")";
2965 }
2966
2967 void GdbEngine::setUseCustomDumpers(bool on)
2968 {
2969     //qDebug() << "SWITCHING ON/OFF DUMPER DEBUGGING:" << on;
2970     Q_UNUSED(on);
2971     // FIXME: a bit too harsh, but otherwise the treeview sometimes look funny
2972     //m_expandedINames.clear();
2973     updateLocals();
2974 }
2975
2976 bool GdbEngine::isCustomValueDumperAvailable(const QString &type) const
2977 {
2978     DebuggerSettings *s = q->settings();
2979     if (!s->m_useCustomDumpers)
2980         return false;
2981     if (s->m_debugDumpers && qq->stackHandler()->isDebuggingDumpers())
2982         return false;
2983     if (m_dataDumperState != DataDumperAvailable)
2984         return false;
2985
2986     // simple types
2987     if (m_availableSimpleDumpers.contains(type))
2988         return true;
2989
2990     // templates
2991     QString tmplate;
2992     QString inner;
2993     if (!extractTemplate(type, &tmplate, &inner))
2994         return false;
2995     return m_availableSimpleDumpers.contains(tmplate);
2996 }
2997
2998 void GdbEngine::runCustomDumper(const WatchData & data0, bool dumpChildren)
2999 {
3000     WatchData data = data0;
3001     QTC_ASSERT(!data.exp.isEmpty(), return);
3002     QString tmplate;
3003     QString inner;
3004     bool isTemplate = extractTemplate(data.type, &tmplate, &inner);
3005     QStringList inners = inner.split('@');
3006     if (inners.at(0).isEmpty())
3007         inners.clear();
3008     for (int i = 0; i != inners.size(); ++i)
3009         inners[i] = inners[i].simplified();
3010
3011     QString outertype = isTemplate ? tmplate : data.type;
3012     // adjust the data extract
3013     if (outertype == m_namespace + "QWidget")
3014         outertype = m_namespace + "QObject";
3015
3016     QString extraArgs[4];
3017     extraArgs[0] = "0";
3018     extraArgs[1] = "0";
3019     extraArgs[2] = "0";
3020     extraArgs[3] = "0";
3021     int extraArgCount = 0;
3022
3023     // "generic" template dumpers: passing sizeof(argument)
3024     // gives already most information the dumpers need
3025     foreach (const QString &arg, inners)
3026         extraArgs[extraArgCount++] = sizeofTypeExpression(arg);
3027
3028     // in rare cases we need more or less:
3029     if (outertype == m_namespace + "QObject") {
3030         extraArgs[0] = "(char*)&((('"
3031             + m_namespace + "QObjectPrivate'*)&"
3032             + data.exp + ")->children)-(char*)&" + data.exp;
3033     } else if (outertype == m_namespace + "QVector") {
3034         extraArgs[1] = "(char*)&(("
3035             + data.exp + ").d->array)-(char*)" + data.exp + ".d";
3036     } else if (outertype == m_namespace + "QObjectSlot"
3037             || outertype == m_namespace + "QObjectSignal") {
3038         // we need the number out of something like
3039         // iname="local.ob.slots.[2]deleteLater()"
3040         int lastOpened = data.iname.lastIndexOf('[');
3041         int lastClosed = data.iname.lastIndexOf(']');
3042         QString slotNumber = "-1";
3043         if (lastOpened != -1 && lastClosed != -1)
3044             slotNumber = data.iname.mid(lastOpened + 1, lastClosed - lastOpened - 1);
3045         extraArgs[0] = slotNumber;
3046     } else if (outertype == m_namespace + "QMap" || outertype == m_namespace + "QMultiMap") {
3047         QString nodetype;
3048         if (m_qtVersion >= (4 << 16) + (5 << 8) + 0) {
3049             nodetype  = m_namespace + "QMapNode";
3050             nodetype += data.type.mid(outertype.size());
3051         } else {
3052             // FIXME: doesn't work for QMultiMap
3053             nodetype  = data.type + "::Node"; 
3054         }
3055         //qDebug() << "OUTERTYPE: " << outertype << " NODETYPE: " << nodetype
3056         //    << "QT VERSION" << m_qtVersion << ((4 << 16) + (5 << 8) + 0);
3057         extraArgs[2] = sizeofTypeExpression(nodetype);
3058         extraArgs[3] = "(size_t)&(('" + nodetype + "'*)0)->value";
3059     } else if (outertype == m_namespace + "QMapNode") {
3060         extraArgs[2] = sizeofTypeExpression(data.type);
3061         extraArgs[3] = "(size_t)&(('" + data.type + "'*)0)->value";
3062     } else if (outertype == "std::vector") {
3063         //qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
3064         if (inners.at(0) == "bool") {
3065             outertype = "std::vector::bool";
3066         } else {
3067             //extraArgs[extraArgCount++] = sizeofTypeExpression(data.type);
3068             //extraArgs[extraArgCount++] = "(size_t)&(('" + data.type + "'*)0)->value";
3069         }
3070     } else if (outertype == "std::deque") {
3071         // remove 'std::allocator<...>':
3072         extraArgs[1] = "0";
3073     } else if (outertype == "std::stack") {
3074         // remove 'std::allocator<...>':
3075         extraArgs[1] = "0";
3076     } else if (outertype == "std::map") {
3077         // We don't want the comparator and the allocator confuse gdb.
3078         // But we need the offset of the second item in the value pair.
3079         // We read the type of the pair from the allocator argument because
3080         // that gets the constness "right" (in the sense that gdb can
3081         // read it back;
3082         QString pairType = inners.at(3);
3083         // remove 'std::allocator<...>':
3084         pairType = pairType.mid(15, pairType.size() - 15 - 2);
3085         extraArgs[2] = "(size_t)&(('" + pairType + "'*)0)->second";
3086         extraArgs[3] = "0";
3087     } else if (outertype == "std::basic_string") {
3088         //qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
3089         if (inners.at(0) == "char") {
3090             outertype = "std::string";
3091         } else if (inners.at(0) == "wchar_t") {
3092             outertype = "std::wstring";
3093         }
3094         extraArgs[0] = "0";
3095         extraArgs[1] = "0";
3096         extraArgs[2] = "0";
3097         extraArgs[3] = "0";
3098     }
3099
3100     //int protocol = (data.iname.startsWith("watch") && data.type == "QImage") ? 3 : 2;
3101     //int protocol = data.iname.startsWith("watch") ? 3 : 2;
3102     int protocol = 2;
3103     //int protocol = isDisplayedIName(data.iname) ? 3 : 2;
3104
3105     QString addr;
3106     if (data.addr.startsWith("0x"))
3107         addr = "(void*)" + data.addr;
3108     else
3109         addr = "&(" + data.exp + ")";
3110
3111     QByteArray params;
3112     params.append(outertype);
3113     params.append('\0');
3114     params.append(data.iname);
3115     params.append('\0');
3116     params.append(data.exp);
3117     params.append('\0');
3118     params.append(inner);
3119     params.append('\0');
3120     params.append(data.iname);
3121     params.append('\0');
3122
3123     sendWatchParameters(params);
3124
3125     QString cmd ="call "
3126             + QString("qDumpObjectData440(")
3127             + QString::number(protocol)
3128             + ',' + "%1+1"                // placeholder for token
3129             + ',' + addr
3130             + ',' + (dumpChildren ? "1" : "0")
3131             + ',' + extraArgs[0]
3132             + ',' + extraArgs[1]
3133             + ',' + extraArgs[2]
3134             + ',' + extraArgs[3] + ')';
3135
3136     //qDebug() << "CMD: " << cmd;
3137
3138     QVariant var;
3139     var.setValue(data);
3140     sendSynchronizedCommand(cmd, WatchDumpCustomValue1, var);
3141
3142     q->showStatusMessage(
3143         tr("Retrieving data for watch view (%1 requests pending)...")
3144             .arg(m_pendingRequests + 1), 10000);
3145
3146     // retrieve response
3147     sendSynchronizedCommand("p (char*)qDumpOutBuffer", WatchDumpCustomValue2, var);
3148 }
3149
3150 void GdbEngine::createGdbVariable(const WatchData &data)
3151 {
3152     sendSynchronizedCommand("-var-delete \"" + data.iname + '"');
3153     QString exp = data.exp;
3154     if (exp.isEmpty() && data.addr.startsWith("0x"))
3155         exp = "*(" + gdbQuoteTypes(data.type) + "*)" + data.addr;
3156     QVariant val = QVariant::fromValue<WatchData>(data);
3157     sendSynchronizedCommand("-var-create \"" + data.iname + '"' + " * "
3158         + '"' + exp + '"', WatchVarCreate, val);
3159 }
3160
3161 void GdbEngine::updateSubItem(const WatchData &data0)
3162 {
3163     WatchData data = data0;
3164     #if DEBUG_SUBITEM
3165     qDebug() << "UPDATE SUBITEM: " << data.toString();
3166     #endif
3167     QTC_ASSERT(data.isValid(), return);
3168
3169     // in any case we need the type first
3170     if (data.isTypeNeeded()) {
3171         // This should only happen if we don't have a variable yet.
3172         // Let's play safe, though.
3173         if (!data.variable.isEmpty()) {
3174             // Update: It does so for out-of-scope watchers.
3175             #if 1
3176             qDebug() << "FIXME: GdbEngine::updateSubItem: "
3177                  << data.toString() << "should not happen";
3178             #else
3179             data.setType("<out of scope>");
3180             data.setValue("<out of scope>");
3181             data.setChildCount(0);
3182             insertData(data);
3183             return;
3184             #endif
3185         }
3186         // The WatchVarCreate handler will receive type information
3187         // and re-insert a WatchData item with correct type, so
3188         // we will not re-enter this bit.
3189         // FIXME: Concurrency issues?
3190         createGdbVariable(data);
3191         return;
3192     }
3193
3194     // we should have a type now. this is relied upon further below
3195     QTC_ASSERT(!data.type.isEmpty(), return);
3196
3197     // a common case that can be easily solved
3198     if (data.isChildrenNeeded() && isPointerType(data.type)
3199         && !isCustomValueDumperAvailable(data.type)) {
3200         // We sometimes know what kind of children pointers have
3201         #if DEBUG_SUBITEM
3202         qDebug() << "IT'S A POINTER";
3203         #endif
3204 #if 1
3205         WatchData data1;
3206         data1.iname = data.iname + ".*";
3207         data1.name = "*" + data.name;
3208         data1.exp = "(*(" + data.exp + "))";
3209         data1.type = stripPointerType(data.type);
3210         data1.setValueNeeded();
3211         insertData(data1);
3212         data.setChildrenUnneeded();
3213         insertData(data);
3214 #else
3215         // Try automatic dereferentiation
3216         data.exp = "*(" + data.exp + ")";
3217         data.type = data.type + "."; // FIXME: fragile HACK to avoid recursion
3218         insertData(data);
3219 #endif
3220         return;
3221     }
3222
3223     if (data.isValueNeeded() && isCustomValueDumperAvailable(data.type)) {
3224         #if DEBUG_SUBITEM
3225         qDebug() << "UPDATE SUBITEM: CUSTOMVALUE";
3226         #endif
3227         runCustomDumper(data, qq->watchHandler()->isExpandedIName(data.iname));
3228         return;
3229     }
3230
3231 /*
3232     if (data.isValueNeeded() && data.exp.isEmpty()) {
3233         #if DEBUG_SUBITEM
3234         qDebug() << "UPDATE SUBITEM: NO EXPRESSION?";
3235         #endif
3236         data.setError("<no expression given>");
3237         insertData(data);
3238         return;
3239     }
3240 */
3241
3242     if (data.isValueNeeded() && data.variable.isEmpty()) {
3243         #if DEBUG_SUBITEM
3244         qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR VALUE";
3245         #endif
3246         createGdbVariable(data);
3247         // the WatchVarCreate handler will re-insert a WatchData
3248         // item, with valueNeeded() set.
3249         return;
3250     }
3251
3252     if (data.isValueNeeded()) {
3253         QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
3254         #if DEBUG_SUBITEM
3255         qDebug() << "UPDATE SUBITEM: VALUE";
3256         #endif
3257         QString cmd = "-var-evaluate-expression \"" + data.iname + "\"";
3258         sendSynchronizedCommand(cmd, WatchEvaluateExpression,
3259             QVariant::fromValue(data));
3260         return;
3261     }
3262
3263     if (data.isChildrenNeeded() && isCustomValueDumperAvailable(data.type)) {
3264         #if DEBUG_SUBITEM
3265         qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN";
3266         #endif
3267         runCustomDumper(data, true);
3268         return;
3269     }
3270
3271     if (data.isChildrenNeeded() && data.variable.isEmpty()) {
3272         #if DEBUG_SUBITEM
3273         qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDREN";
3274         #endif
3275         createGdbVariable(data);
3276         // the WatchVarCreate handler will re-insert a WatchData
3277         // item, with childrenNeeded() set.
3278         return;
3279     }
3280
3281     if (data.isChildrenNeeded()) {
3282         QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
3283         QString cmd = "-var-list-children --all-values \"" + data.variable + "\"";
3284         sendSynchronizedCommand(cmd, WatchVarListChildren, QVariant::fromValue(data));
3285         return;
3286     }
3287
3288     if (data.isChildCountNeeded() && isCustomValueDumperAvailable(data.type)) {
3289         #if DEBUG_SUBITEM
3290         qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN";
3291         #endif
3292         runCustomDumper(data, qq->watchHandler()->isExpandedIName(data.iname));
3293         return;
3294     }
3295
3296     if (data.isChildCountNeeded() && data.variable.isEmpty()) {
3297         #if DEBUG_SUBITEM
3298         qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDCOUNT";
3299         #endif
3300         createGdbVariable(data);
3301         // the WatchVarCreate handler will re-insert a WatchData
3302         // item, with childrenNeeded() set.
3303         return;
3304     }
3305
3306     if (data.isChildCountNeeded()) {
3307         QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
3308         QString cmd = "-var-list-children --all-values \"" + data.variable + "\"";
3309         sendCommand(cmd, WatchVarListChildren, QVariant::fromValue(data));
3310         return;
3311     }
3312
3313     qDebug() << "FIXME: UPDATE SUBITEM: " << data.toString();
3314     QTC_ASSERT(false, return);
3315 }
3316
3317 void GdbEngine::updateWatchModel()
3318 {
3319     m_pendingRequests = 0;
3320     PENDING_DEBUG("EXTERNAL TRIGGERING UPDATE WATCH MODEL");
3321     updateWatchModel2();
3322 }
3323
3324 void GdbEngine::updateWatchModel2()
3325 {
3326     PENDING_DEBUG("UPDATE WATCH MODEL");
3327     QList<WatchData> incomplete = qq->watchHandler()->takeCurrentIncompletes();
3328     //QTC_ASSERT(incomplete.isEmpty(), /**/);
3329     if (!incomplete.isEmpty()) {
3330         #if DEBUG_PENDING
3331         qDebug() << "##############################################";
3332         qDebug() << "UPDATE MODEL, FOUND INCOMPLETES:";
3333         foreach (const WatchData &data, incomplete)
3334             qDebug() << data.toString();
3335         #endif
3336
3337         // Bump requests to avoid model rebuilding during the nested
3338         // updateWatchModel runs.
3339         ++m_pendingRequests;
3340         foreach (const WatchData &data, incomplete)
3341             updateSubItem(data);
3342         PENDING_DEBUG("INTERNAL TRIGGERING UPDATE WATCH MODEL");
3343         updateWatchModel2();
3344         --m_pendingRequests;
3345
3346         return;
3347     }
3348
3349     if (m_pendingRequests > 0) {
3350         PENDING_DEBUG("UPDATE MODEL, PENDING: " << m_pendingRequests);
3351         return;
3352     }
3353
3354     PENDING_DEBUG("REBUILDING MODEL");
3355     emit gdbInputAvailable(QString(),
3356         "[" + currentTime() + "]    <Rebuild Watchmodel>");
3357     q->showStatusMessage(tr("Finished retrieving data."), 400);
3358     qq->watchHandler()->rebuildModel();
3359
3360     if (!m_toolTipExpression.isEmpty()) {
3361         WatchData *data = qq->watchHandler()->findData(tooltipIName);
3362         if (data) {
3363             //m_toolTipCache[data->exp] = *data;
3364             QToolTip::showText(m_toolTipPos,
3365                     "(" + data->type + ") " + data->exp + " = " + data->value);
3366         } else {
3367             QToolTip::showText(m_toolTipPos,
3368                 "Cannot evaluate expression: " + m_toolTipExpression);
3369         }
3370     }
3371 }
3372
3373 void GdbEngine::handleQueryDataDumper1(const GdbResultRecord &record)
3374 {
3375     Q_UNUSED(record);
3376 }
3377
3378 void GdbEngine::handleQueryDataDumper2(const GdbResultRecord &record)
3379 {
3380     //qDebug() << "DATA DUMPER TRIAL:" << record.toString();
3381     GdbMi output = record.data.findChild("consolestreamoutput");
3382     QByteArray out = output.data();
3383     out = out.mid(out.indexOf('"') + 2); // + 1 is success marker
3384     out = out.left(out.lastIndexOf('"'));
3385     //out.replace('\'', '"');
3386     out.replace("\\", "");
3387     out = "dummy={" + out + "}";
3388     //qDebug() << "OUTPUT: " << out;
3389
3390     GdbMi contents;
3391     contents.fromString(out);
3392     GdbMi simple = contents.findChild("dumpers");
3393     m_namespace = contents.findChild("namespace").data();
3394     GdbMi qtversion = contents.findChild("qtversion");
3395     if (qtversion.children().size() == 3) {
3396         m_qtVersion = (qtversion.childAt(0).data().toInt() << 16)
3397                     + (qtversion.childAt(1).data().toInt() << 8)
3398                     + qtversion.childAt(2).data().toInt();
3399         //qDebug() << "FOUND QT VERSION: " << qtversion.toString() << m_qtVersion;
3400     } else {
3401         m_qtVersion = 0;
3402     }
3403    
3404     //qDebug() << "CONTENTS: " << contents.toString();
3405     //qDebug() << "SIMPLE DUMPERS: " << simple.toString();
3406     m_availableSimpleDumpers.clear();
3407     foreach (const GdbMi &item, simple.children())
3408         m_availableSimpleDumpers.append(item.data());
3409     if (m_availableSimpleDumpers.isEmpty()) {
3410         m_dataDumperState = DataDumperUnavailable;
3411         QMessageBox::warning(q->mainWindow(),
3412             tr("Cannot find special data dumpers"),
3413             tr("The debugged binary does not contain information needed for "
3414                     "nice display of Qt data types.\n\n"
3415                     "You might want to try including the file\n\n"
3416                     ".../share/qtcreator/gdbmacros/gdbmacros.cpp\n\n"
3417                     "into your project directly.")
3418                 );
3419     } else {
3420         m_dataDumperState = DataDumperAvailable;
3421     }
3422     //qDebug() << "DATA DUMPERS AVAILABLE" << m_availableSimpleDumpers;
3423 }
3424
3425 void GdbEngine::sendWatchParameters(const QByteArray &params0)
3426 {
3427     QByteArray params = params0;
3428     params.append('\0');
3429     char buf[50];
3430     sprintf(buf, "set {char[%d]} qDumpInBuffer = {", params.size());
3431     QByteArray encoded;
3432     encoded.append(buf);
3433     for (int i = 0; i != params.size(); ++i) {
3434         sprintf(buf, "%d,", int(params[i]));
3435         encoded.append(buf);
3436     }
3437     encoded[encoded.size() - 1] = '}';
3438
3439     sendCommand(encoded);
3440 }
3441
3442 void GdbEngine::handleVarAssign()
3443 {
3444     // everything might have changed, force re-evaluation
3445     // FIXME: Speed this up by re-using variables and only
3446     // marking values as 'unknown'
3447     updateLocals();
3448 }
3449
3450 void GdbEngine::setWatchDataType(WatchData &data, const GdbMi &mi)
3451 {
3452     if (mi.isValid()) {
3453         if (!data.framekey.isEmpty())
3454             m_varToType[data.framekey] = mi.data();
3455         data.setType(mi.data());
3456     } else if (data.type.isEmpty()) {
3457         data.setTypeNeeded();
3458     }
3459 }
3460
3461 void GdbEngine::handleVarCreate(const GdbResultRecord &record,
3462     const WatchData &data0)
3463 {
3464     WatchData data = data0;
3465     // happens e.g. when we already issued a var-evaluate command
3466     if (!data.isValid())
3467         return;
3468     //qDebug() << "HANDLE VARIABLE CREATION: " << data.toString();
3469     if (record.resultClass == GdbResultDone) {
3470         data.variable = data.iname;
3471         setWatchDataType(data, record.data.findChild("type"));
3472         if (isCustomValueDumperAvailable(data.type)) {
3473             // we do not trust gdb if we have a custom dumper
3474             if (record.data.findChild("children").isValid())
3475                 data.setChildrenUnneeded();
3476             else if (qq->watchHandler()->isExpandedIName(data.iname))
3477                 data.setChildrenNeeded();
3478             insertData(data);
3479         } else {
3480             if (record.data.findChild("children").isValid())
3481                 data.setChildrenUnneeded();
3482             else if (qq->watchHandler()->isExpandedIName(data.iname))
3483                 data.setChildrenNeeded();
3484             setWatchDataChildCount(data, record.data.findChild("numchild"));
3485             //if (data.isValueNeeded() && data.childCount > 0)
3486             //    data.setValue(QByteArray());
3487             insertData(data);
3488         }
3489     } else if (record.resultClass == GdbResultError) {
3490         data.setError(record.data.findChild("msg").data());
3491         if (data.isWatcher()) {
3492             data.value = strNotInScope;
3493             data.type = " ";
3494             data.setAllUnneeded();
3495             data.setChildCount(0);
3496             data.valuedisabled = true;
3497             insertData(data);
3498         }
3499     }
3500 }
3501
3502 void GdbEngine::handleEvaluateExpression(const GdbResultRecord &record,
3503     const WatchData &data0)
3504 {
3505     WatchData data = data0;
3506     QTC_ASSERT(data.isValid(), qDebug() << "HUH?");
3507     if (record.resultClass == GdbResultDone) {
3508         //if (col == 0)
3509         //    data.name = record.data.findChild("value").data();
3510         //else
3511             setWatchDataValue(data, record.data.findChild("value"));
3512     } else if (record.resultClass == GdbResultError) {
3513         data.setError(record.data.findChild("msg").data());
3514     }
3515     //qDebug() << "HANDLE EVALUATE EXPRESSION: " << data.toString();
3516     insertData(data);
3517     //updateWatchModel2();
3518 }
3519
3520 void GdbEngine::handleDumpCustomSetup(const GdbResultRecord &record)
3521 {
3522     //qDebug() << "CUSTOM SETUP RESULT: " << record.toString();
3523     if (record.resultClass == GdbResultDone) {
3524     } else if (record.resultClass == GdbResultError) {
3525         QString msg = record.data.findChild("msg").data();
3526         //qDebug() << "CUSTOM DUMPER SETUP ERROR MESSAGE: " << msg;
3527         q->showStatusMessage(tr("Custom dumper setup: %1").arg(msg), 10000);
3528     }
3529 }
3530
3531 void GdbEngine::handleDumpCustomValue1(const GdbResultRecord &record,
3532     const WatchData &data0)
3533 {
3534     WatchData data = data0;
3535     QTC_ASSERT(data.isValid(), return);
3536     if (record.resultClass == GdbResultDone) {
3537         // ignore this case, data will follow
3538     } else if (record.resultClass == GdbResultError) {
3539         // Record an extra result, as the socket result will be lost
3540         // in transmission
3541         //--m_pendingRequests;
3542         QString msg = record.data.findChild("msg").data();
3543         //qDebug() << "CUSTOM DUMPER ERROR MESSAGE: " << msg;
3544 #ifdef QT_DEBUG
3545         // Make debugging of dumpers easier
3546         if (q->settings()->m_debugDumpers
3547                 && msg.startsWith("The program being debugged stopped while")
3548                 && msg.contains("qDumpObjectData440")) {
3549             // Fake full stop
3550             sendCommand("p 0", GdbAsyncOutput2);  // dummy
3551             return;
3552         }
3553 #endif
3554         //if (msg.startsWith("The program being debugged was sig"))
3555         //    msg = strNotInScope;
3556         //if (msg.startsWith("The program being debugged stopped while"))
3557         //    msg = strNotInScope;
3558         //data.setError(msg);
3559         //insertData(data);
3560     }
3561 }
3562
3563 void GdbEngine::handleDumpCustomValue2(const GdbResultRecord &record,
3564     const WatchData &data0)
3565 {
3566     WatchData data = data0;
3567     QTC_ASSERT(data.isValid(), return);
3568     //qDebug() << "CUSTOM VALUE RESULT: " << record.toString();
3569     //qDebug() << "FOR DATA: " << data.toString() << record.resultClass;
3570     if (record.resultClass != GdbResultDone) {
3571         qDebug() << "STRANGE CUSTOM DUMPER RESULT DATA: " << data.toString();
3572         return;
3573     }
3574
3575     GdbMi output = record.data.findChild("consolestreamoutput");
3576     QByteArray out = output.data();
3577
3578     int markerPos = out.indexOf('"') + 1; // position of 'success marker'
3579     if (markerPos == -1 || out.at(markerPos) == 'f') {  // 't' or 'f'
3580         // custom dumper produced no output
3581         data.setError(strNotInScope);
3582         insertData(data);
3583         return;
3584     }
3585
3586     out = out.mid(markerPos +  1);
3587     out = out.left(out.lastIndexOf('"'));
3588     out.replace("\\", "");
3589     out = "dummy={" + out + "}";
3590     
3591     GdbMi contents;
3592     contents.fromString(out);
3593     //qDebug() << "CONTENTS" << contents.toString(true);
3594     if (!contents.isValid()) {
3595         data.setError(strNotInScope);
3596         insertData(data);
3597         return;
3598     }
3599
3600     setWatchDataType(data, contents.findChild("type"));
3601     setWatchDataValue(data, contents.findChild("value"),
3602         contents.findChild("valueencoded").data().toInt());
3603     setWatchDataAddress(data, contents.findChild("addr"));
3604     setWatchDataChildCount(data, contents.findChild("numchild"));
3605     setWatchDataValueToolTip(data, contents.findChild("valuetooltip"));
3606     setWatchDataValueDisabled(data, contents.findChild("valuedisabled"));
3607     setWatchDataEditValue(data, contents.findChild("editvalue"));
3608     if (qq->watchHandler()->isDisplayedIName(data.iname)) {
3609         GdbMi editvalue = contents.findChild("editvalue");
3610         if (editvalue.isValid()) {
3611             setWatchDataEditValue(data, editvalue);
3612             qq->watchHandler()->showEditValue(data);
3613         }
3614     }
3615     if (!qq->watchHandler()->isExpandedIName(data.iname))
3616         data.setChildrenUnneeded();
3617     GdbMi children = contents.findChild("children");
3618     if (children.isValid() || !qq->watchHandler()->isExpandedIName(data.iname))
3619         data.setChildrenUnneeded();
3620     data.setValueUnneeded();
3621
3622     // try not to repeat data too often
3623     WatchData childtemplate;
3624     setWatchDataType(childtemplate, contents.findChild("childtype"));
3625     setWatchDataChildCount(childtemplate, contents.findChild("childnumchild"));
3626     //qDebug() << "DATA: " << data.toString();
3627     insertData(data);
3628     foreach (GdbMi item, children.children()) {
3629         WatchData data1 = childtemplate;
3630         data1.name = item.findChild("name").data();
3631         data1.iname = data.iname + "." + data1.name;
3632         if (!data1.name.isEmpty() && data1.name.at(0).isDigit())
3633             data1.name = '[' + data1.name + ']';
3634         QString key = item.findChild("key").data();
3635         if (!key.isEmpty()) {
3636             if (item.findChild("keyencoded").data()[0] == '1') {
3637                 key = '"' + QByteArray::fromBase64(key.toUtf8()) + '"';
3638                 if (key.size() > 13) {
3639                     key = key.left(12);
3640                     key += "...";
3641                 }
3642             }
3643             //data1.name += " (" + key + ")";
3644             data1.name = key;
3645         }
3646         setWatchDataType(data1, item.findChild("type"));
3647         setWatchDataExpression(data1, item.findChild("exp"));
3648         setWatchDataChildCount(data1, item.findChild("numchild"));
3649         setWatchDataValue(data1, item.findChild("value"),
3650             item.findChild("valueencoded").data().toInt());
3651         setWatchDataAddress(data1, item.findChild("addr"));
3652         setWatchDataValueToolTip(data1, item.findChild("valuetooltip"));
3653         setWatchDataValueDisabled(data1, item.findChild("valuedisabled"));
3654         if (!qq->watchHandler()->isExpandedIName(data1.iname))
3655             data1.setChildrenUnneeded();
3656         //qDebug() << "HANDLE CUSTOM SUBCONTENTS:" << data1.toString();
3657         insertData(data1);
3658     }
3659 }
3660
3661 void GdbEngine::updateLocals()
3662 {
3663     setTokenBarrier();
3664
3665     m_pendingRequests = 0;
3666
3667     PENDING_DEBUG("\nRESET PENDING");
3668     m_toolTipCache.clear();
3669     m_toolTipExpression.clear();
3670     qq->watchHandler()->reinitializeWatchers();
3671
3672     int level = currentFrame();
3673     // '2' is 'list with type and value'
3674     QString cmd = QString("-stack-list-arguments 2 %1 %2").arg(level).arg(level);
3675     sendSynchronizedCommand(cmd, StackListArguments);                 // stage 1/2
3676     // '2' is 'list with type and value'
3677     sendSynchronizedCommand("-stack-list-locals 2", StackListLocals); // stage 2/2
3678
3679     //tryLoadCustomDumpers();
3680 }
3681
3682 void GdbEngine::handleStackListArguments(const GdbResultRecord &record)
3683 {
3684     // stage 1/2
3685
3686     // Linux:
3687     // 12^done,stack-args=
3688     //   [frame={level="0",args=[
3689     //     {name="argc",type="int",value="1"},
3690     //     {name="argv",type="char **",value="(char **) 0x7..."}]}]
3691     // Mac:
3692     // 78^done,stack-args=
3693     //    {frame={level="0",args={
3694     //      varobj=
3695     //        {exp="this",value="0x38a2fab0",name="var21",numchild="3",
3696     //             type="CurrentDocumentFind *  const",typecode="PTR",
3697     //             dynamic_type="",in_scope="true",block_start_addr="0x3938e946",
3698     //             block_end_addr="0x3938eb2d"},
3699     //      varobj=
3700     //         {exp="before",value="@0xbfffb9f8: {d = 0x3a7f2a70}",
3701     //              name="var22",numchild="1",type="const QString  ...} }}}
3702     //
3703     // In both cases, iterating over the children of stack-args/frame/args
3704     // is ok.
3705     m_currentFunctionArgs.clear();
3706     if (record.resultClass == GdbResultDone) {
3707         const GdbMi list = record.data.findChild("stack-args");
3708         const GdbMi frame = list.findChild("frame");
3709         const GdbMi args = frame.findChild("args");
3710         m_currentFunctionArgs = args.children();
3711     } else if (record.resultClass == GdbResultError) {
3712         qDebug() << "FIXME: GdbEngine::handleStackListArguments: should not happen";
3713     }
3714 }
3715
3716 void GdbEngine::handleStackListLocals(const GdbResultRecord &record)
3717 {
3718     // stage 2/2
3719
3720     // There could be shadowed variables
3721     QList<GdbMi> locals = record.data.findChild("locals").children();
3722     locals += m_currentFunctionArgs;
3723
3724     setLocals(locals);
3725 }
3726
3727 void GdbEngine::setLocals(const QList<GdbMi> &locals) 
3728
3729     //qDebug() << m_varToType;
3730     QMap<QString, int> seen;
3731
3732     foreach (const GdbMi &item, locals) {
3733         // Local variables of inlined code are reported as 
3734         // 26^done,locals={varobj={exp="this",value="",name="var4",exp="this",
3735         // numchild="1",type="const QtSharedPointer::Basic<CPlusPlus::..."
3736         // We do not want these at all. Current hypotheses is that those
3737         // "spurious" locals have _two_ "exp" field. Try to filter them:
3738         #ifdef Q_OS_MAC
3739         int numExps = 0;
3740         foreach (const GdbMi &child, item.children())
3741             numExps += int(child.name() == "exp");
3742         if (numExps > 1)
3743             continue;
3744         QString name = item.findChild("exp").data();
3745         #else
3746         QString name = item.findChild("name").data();
3747         #endif
3748         int n = seen.value(name);
3749         if (n) {
3750             seen[name] = n + 1;
3751             WatchData data;
3752             data.iname = "local." + name + QString::number(n + 1);
3753             data.name = name + QString(" <shadowed %1>").arg(n);
3754             //data.setValue("<shadowed>");
3755             setWatchDataValue(data, item.findChild("value"));
3756             data.setType("<shadowed>");
3757             data.setChildCount(0);
3758             insertData(data);
3759         } else {
3760             seen[name] = 1;
3761             WatchData data;
3762             data.iname = "local." + name;
3763             data.name = name;
3764             data.exp = name;
3765             data.framekey = m_currentFrame + data.name;
3766             setWatchDataType(data, item.findChild("type"));
3767             // set value only directly if it is simple enough, otherwise
3768             // pass through the insertData() machinery
3769             if (isIntOrFloatType(data.type) || isPointerType(data.type))
3770                 setWatchDataValue(data, item.findChild("value"));
3771             if (!qq->watchHandler()->isExpandedIName(data.iname))
3772                 data.setChildrenUnneeded();
3773             if (isPointerType(data.type) || data.name == "this")
3774                 data.setChildCount(1);
3775             if (0 && m_varToType.contains(data.framekey)) {
3776                 qDebug() << "RE-USING " << m_varToType.value(data.framekey);
3777                 data.setType(m_varToType.value(data.framekey));
3778             }
3779             insertData(data);
3780         }
3781     }
3782 }
3783
3784 void GdbEngine::insertData(const WatchData &data0)
3785 {
3786     //qDebug() << "INSERT DATA" << data0.toString();
3787     WatchData data = data0;
3788     if (data.value.startsWith("mi_cmd_var_create:")) {
3789         qDebug() << "BOGUS VALUE: " << data.toString();
3790         return;
3791     }
3792     qq->watchHandler()->insertData(data);
3793 }
3794
3795 void GdbEngine::handleTypeContents(const QString &output)
3796 {
3797     // output.startsWith("type = ") == true
3798     // "type = int"
3799     // "type = class QString {"
3800     // "type = class QStringList : public QList<QString> {"
3801     QString tip;
3802     QString className;
3803     if (output.startsWith("type = class")) {
3804         int posBrace = output.indexOf('{');
3805         QString head = output.mid(13, posBrace - 13 - 1);
3806         int posColon = head.indexOf(": public");
3807         if (posColon == -1)
3808             posColon = head.indexOf(": protected");
3809         if (posColon == -1)
3810             posColon = head.indexOf(": private");
3811         if (posColon == -1) {
3812             className = head;
3813             tip = "class " + className + " { ... }";
3814         } else {
3815             className = head.left(posColon - 1);
3816             tip = "class " + head + " { ... }";
3817         }
3818         //qDebug() << "posColon: " << posColon;
3819         //qDebug() << "posBrace: " << posBrace;
3820         //qDebug() << "head: " << head;
3821     } else {
3822         className = output.mid(7);
3823         tip = className;
3824     }
3825     //qDebug() << "output: " << output.left(100) + "...";
3826     //qDebug() << "className: " << className;
3827     //qDebug() << "tip: " << tip;
3828     //m_toolTip.type = className;
3829     m_toolTip.type.clear();
3830     m_toolTip.value = tip;
3831 }
3832
3833 void GdbEngine::handleVarListChildrenHelper(const GdbMi &item,
3834     const WatchData &parent)
3835 {
3836     //qDebug() <<  "VAR_LIST_CHILDREN: PARENT 2" << parent.toString();
3837     //qDebug() <<  "VAR_LIST_CHILDREN: APPENDEE " << data.toString();
3838     QByteArray exp = item.findChild("exp").data();
3839     QByteArray name = item.findChild("name").data();
3840     if (isAccessSpecifier(exp)) {
3841         // suppress 'private'/'protected'/'public' level
3842         WatchData data;
3843         data.variable = name;
3844         data.iname = parent.iname;
3845         //data.iname = data.variable;
3846         data.exp = parent.exp;
3847         data.setTypeUnneeded();
3848         data.setValueUnneeded();
3849         data.setChildCountUnneeded();
3850         data.setChildrenUnneeded();
3851         //qDebug() << "DATA" << data.toString();
3852         QString cmd = "-var-list-children --all-values \"" + data.variable + "\"";
3853         //iname += '.' + exp;
3854         sendSynchronizedCommand(cmd, WatchVarListChildren, QVariant::fromValue(data));
3855     } else if (item.findChild("numchild").data() == "0") {
3856         // happens for structs without data, e.g. interfaces.
3857         WatchData data;
3858         data.iname = parent.iname + '.' + exp;
3859         data.name = exp;
3860         data.variable = name;
3861         setWatchDataType(data, item.findChild("type"));
3862         setWatchDataValue(data, item.findChild("value"));
3863         setWatchDataAddress(data, item.findChild("addr"));
3864         data.setChildCount(0);
3865         insertData(data);
3866     } else if (parent.iname.endsWith('.')) {
3867         // Happens with anonymous unions
3868         WatchData data;
3869         data.iname = name;
3870         QString cmd = "-var-list-children --all-values \"" + data.variable + "\"";
3871         sendSynchronizedCommand(cmd, WatchVarListChildren, QVariant::fromValue(data));
3872     } else if (exp == "staticMetaObject") {
3873         //    && item.findChild("type").data() == "const QMetaObject")
3874         // FIXME: Namespaces?
3875         // { do nothing }    FIXME: make coinfigurable?
3876         // special "clever" hack to avoid clutter in the GUI.
3877         // I am not sure this is a good idea...
3878     } else {
3879         WatchData data;
3880         data.iname = parent.iname + '.' + exp;
3881         data.variable = name;
3882         setWatchDataType(data, item.findChild("type"));
3883         setWatchDataValue(data, item.findChild("value"));
3884         setWatchDataAddress(data, item.findChild("addr"));
3885         setWatchDataChildCount(data, item.findChild("numchild"));
3886         if (!qq->watchHandler()->isExpandedIName(data.iname))
3887             data.setChildrenUnneeded();
3888
3889         data.name = exp;
3890         if (isPointerType(parent.type) && data.type == exp) {
3891             data.exp = "*(" + parent.exp + ")";
3892             data.name = "*" + parent.name;
3893         } else if (data.type == exp) {
3894             // A type we derive from? gdb crashes when creating variables here
3895             data.exp = parent.exp;
3896         } else if (exp.startsWith("*")) {
3897             // A pointer
3898             data.exp = "*(" + parent.exp + ")";
3899         } else if (startsWithDigit(exp)) {
3900             // An array. No variables needed?
3901             data.name = "[" + data.name + "]";
3902             data.exp = parent.exp + "[" + exp + "]";
3903         } else if (0 && parent.name.endsWith('.')) {
3904             // Happens with anonymous unions
3905             data.exp = parent.exp + exp;
3906             //data.name = "<anonymous union>";
3907         } else if (exp.isEmpty()) {
3908             // Happens with anonymous unions
3909             data.exp = parent.exp;
3910             data.name = "<n/a>";
3911             data.iname = parent.iname + ".@";
3912             data.type = "<anonymous union>";
3913         } else {
3914             // A structure. Hope there's nothing else...
3915             data.exp = parent.exp + '.' + exp;
3916         }
3917
3918         if (isCustomValueDumperAvailable(data.type)) {
3919             // we do not trust gdb if we have a custom dumper
3920             data.setValueNeeded();
3921             data.setChildCountNeeded();
3922         }
3923
3924         //qDebug() <<  "VAR_LIST_CHILDREN: PARENT 3" << parent.toString();
3925         //qDebug() <<  "VAR_LIST_CHILDREN: APPENDEE " << data.toString();
3926         insertData(data);
3927     }
3928 }
3929
3930 void GdbEngine::handleVarListChildren(const GdbResultRecord &record,
3931     const WatchData &data0)
3932 {
3933     //WatchResultCounter dummy(this, WatchVarListChildren);
3934     WatchData data = data0;
3935     if (!data.isValid())
3936         return;
3937     if (record.resultClass == GdbResultDone) {
3938         //qDebug() <<  "VAR_LIST_CHILDREN: PARENT " << data.toString();
3939         GdbMi children = record.data.findChild("children");
3940
3941         foreach (const GdbMi &child, children.children())
3942             handleVarListChildrenHelper(child, data);
3943
3944         if (!isAccessSpecifier(data.variable.split('.').takeLast())) {
3945             data.setChildrenUnneeded();
3946             insertData(data);
3947         }
3948     } else if (record.resultClass == GdbResultError) {
3949         data.setError(record.data.findChild("msg").data());
3950     } else {
3951         data.setError("Unknown error: " + record.toString());
3952     }
3953 }
3954
3955 void GdbEngine::handleToolTip(const GdbResultRecord &record,
3956         const QString &what)
3957 {
3958     //qDebug() << "HANDLE TOOLTIP: " << what << m_toolTip.toString();
3959     //    << "record: " << record.toString();
3960     if (record.resultClass == GdbResultError) {
3961         QString msg = record.data.findChild("msg").data();
3962         if (what == "create") {
3963             sendCommand("ptype " + m_toolTip.exp, WatchToolTip, "ptype");
3964             return;
3965         }
3966         if (what == "evaluate") {
3967             if (msg.startsWith("Cannot look up value of a typedef")) {
3968                 m_toolTip.value = m_toolTip.exp + " is a typedef.";
3969                 //return;
3970             }
3971         }
3972     } else if (record.resultClass == GdbResultDone) {
3973         if (what == "create") {
3974             setWatchDataType(m_toolTip, record.data.findChild("type"));
3975             setWatchDataChildCount(m_toolTip, record.data.findChild("numchild"));
3976             if (isCustomValueDumperAvailable(m_toolTip.type))
3977                 runCustomDumper(m_toolTip, false);
3978             else
3979                 q->showStatusMessage(tr("Retrieving data for tooltip..."), 10000);
3980                 sendCommand("-data-evaluate-expression " + m_toolTip.exp,
3981                     WatchToolTip, "evaluate");
3982                 //sendToolTipCommand("-var-evaluate-expression tooltip")
3983             return;
3984         }
3985         if (what == "evaluate") {
3986             m_toolTip.value = m_toolTip.type + ' ' + m_toolTip.exp
3987                    + " = " + record.data.findChild("value").data();
3988             //return;
3989         }
3990         if (what == "ptype") {
3991             GdbMi mi = record.data.findChild("consolestreamoutput");
3992             m_toolTip.value = extractTypeFromPTypeOutput(mi.data());
3993             //return;
3994         }
3995     }
3996
3997     m_toolTip.iname = tooltipIName;
3998     m_toolTip.setChildrenUnneeded();
3999     m_toolTip.setChildCountUnneeded();
4000     insertData(m_toolTip);
4001     qDebug() << "DATA INSERTED";
4002     QTimer::singleShot(0, this, SLOT(updateWatchModel2()));
4003     qDebug() << "HANDLE TOOLTIP END";
4004 }
4005
4006 #if 0
4007 void GdbEngine::handleChangedItem(QStandardItem *item)
4008 {
4009     // HACK: Just store the item for the slot
4010     //  handleChangedItem(QWidget *widget) below.
4011     QModelIndex index = item->index().sibling(item->index().row(), 0);
4012     //WatchData data = m_currentSet.takeData(iname);
4013     //m_editedData = inameFromItem(m_model.itemFromIndex(index)).exp;
4014     //qDebug() << "HANDLE CHANGED EXPRESSION: " << m_editedData;
4015 }
4016 #endif
4017
4018 void GdbEngine::assignValueInDebugger(const QString &expression, const QString &value)
4019 {
4020     sendCommand("-var-delete assign");
4021     sendCommand("-var-create assign * " + expression);
4022     sendCommand("-var-assign assign " + value, WatchVarAssign);
4023 }
4024
4025 void GdbEngine::tryLoadCustomDumpers()
4026 {
4027     if (m_dataDumperState != DataDumperUninitialized)
4028         return;
4029
4030     PENDING_DEBUG("TRY LOAD CUSTOM DUMPERS");
4031     m_dataDumperState = DataDumperUnavailable; 
4032
4033 #if defined(Q_OS_LINUX)
4034     QString lib = q->m_buildDir + "/qtc-gdbmacros/libgdbmacros.so";
4035     if (QFileInfo(lib).exists()) {
4036         m_dataDumperState = DataDumperLoadTried;
4037         //sendCommand("p dlopen");
4038         QString flag = QString::number(RTLD_NOW);
4039         sendCommand("sharedlibrary libc"); // for malloc
4040         sendCommand("sharedlibrary libdl"); // for dlopen
4041         sendCommand("call (void)dlopen(\"" + lib + "\", " + flag + ")",
4042             WatchDumpCustomSetup);
4043         // some older systems like CentOS 4.6 prefer this:
4044         sendCommand("call (void)__dlopen(\"" + lib + "\", " + flag + ")",
4045             WatchDumpCustomSetup);
4046         sendCommand("sharedlibrary " + dotEscape(lib));
4047     }
4048 #endif
4049 #if defined(Q_OS_MAC)
4050     QString lib = q->m_buildDir + "/qtc-gdbmacros/libgdbmacros.dylib";
4051     if (QFileInfo(lib).exists()) {
4052         m_dataDumperState = DataDumperLoadTried;
4053         //sendCommand("sharedlibrary libc"); // for malloc
4054         //sendCommand("sharedlibrary libdl"); // for dlopen
4055         QString flag = QString::number(RTLD_NOW);
4056         sendCommand("call (void)dlopen(\"" + lib + "\", " + flag + ")",
4057             WatchDumpCustomSetup);
4058         //sendCommand("sharedlibrary " + dotEscape(lib));
4059     }
4060 #endif
4061 #if defined(Q_OS_WIN)
4062     QString lib = q->m_buildDir + "/qtc-gdbmacros/debug/gdbmacros.dll";
4063     if (QFileInfo(lib).exists()) {
4064         m_dataDumperState = DataDumperLoadTried;
4065         sendCommand("sharedlibrary .*"); // for LoadLibraryA
4066         //sendCommand("handle SIGSEGV pass stop print");
4067         //sendCommand("set unwindonsignal off");
4068         sendCommand("call LoadLibraryA(\"" + lib + "\")",
4069             WatchDumpCustomSetup);
4070         sendCommand("sharedlibrary " + dotEscape(lib));
4071     }
4072 #endif
4073
4074     if (m_dataDumperState == DataDumperLoadTried) {
4075         // retreive list of dumpable classes
4076         sendCommand("call qDumpObjectData440(1,%1+1,0,0,0,0,0,0)",
4077             GdbQueryDataDumper1);
4078         sendCommand("p (char*)qDumpOutBuffer", GdbQueryDataDumper2);
4079     } else {
4080         gdbOutputAvailable("", QString("DEBUG HELPER LIBRARY IS NOT USABLE: "
4081             " %1  EXISTS: %2, EXECUTABLE: %3").arg(lib)
4082             .arg(QFileInfo(lib).exists())
4083             .arg(QFileInfo(lib).isExecutable()));
4084     }
4085 }
4086
4087
4088 IDebuggerEngine *createGdbEngine(DebuggerManager *parent)
4089 {
4090     return new GdbEngine(parent);
4091 }
4092