OSDN Git Service

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