gdbengine.cpp 157 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2 3 4
**
** This file is part of Qt Creator
**
5
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
con's avatar
con committed
8
**
9
** Commercial Usage
10
**
11 12 13 14
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
15
**
16
** GNU Lesser General Public License Usage
17
**
18 19 20 21 22 23
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24
**
25
** If you are unsure which license is appropriate for your use, please
hjk's avatar
hjk committed
26
** contact the sales department at http://qt.nokia.com/contact.
con's avatar
con committed
27
**
28
**************************************************************************/
con's avatar
con committed
29

30 31
#define QT_NO_CAST_FROM_ASCII

con's avatar
con committed
32
#include "gdbengine.h"
33
#include "gdboptionspage.h"
34 35
#include "trkoptions.h"
#include "trkoptionspage.h"
36

37 38
#include "attachgdbadapter.h"
#include "coregdbadapter.h"
hjk's avatar
hjk committed
39
#include "plaingdbadapter.h"
40
#include "remotegdbadapter.h"
41
#include "trkgdbadapter.h"
con's avatar
con committed
42

43
#include "watchutils.h"
44
#include "debuggeractions.h"
45
#include "debuggeragents.h"
con's avatar
con committed
46 47
#include "debuggerconstants.h"
#include "debuggermanager.h"
48
#include "debuggertooltip.h"
49
#include "debuggerstringutils.h"
con's avatar
con committed
50 51 52 53 54 55 56
#include "gdbmi.h"

#include "breakhandler.h"
#include "moduleshandler.h"
#include "registerhandler.h"
#include "stackhandler.h"
#include "watchhandler.h"
57
#include "sourcefileswindow.h"
con's avatar
con committed
58

59
#include "debuggerdialogs.h"
con's avatar
con committed
60

hjk's avatar
hjk committed
61
#include <utils/qtcassert.h>
62
#include <utils/fancymainwindow.h>
63
#include <texteditor/itexteditor.h>
64
#include <projectexplorer/toolchain.h>
65
#include <coreplugin/icore.h>
hjk's avatar
hjk committed
66

con's avatar
con committed
67 68 69
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
70
#include <QtCore/QMetaObject>
con's avatar
con committed
71 72
#include <QtCore/QTime>
#include <QtCore/QTimer>
73
#include <QtCore/QTextStream>
con's avatar
con committed
74 75

#include <QtGui/QAction>
76
#include <QtCore/QCoreApplication>
con's avatar
con committed
77 78 79
#include <QtGui/QLabel>
#include <QtGui/QMainWindow>
#include <QtGui/QMessageBox>
80 81
#include <QtGui/QDialogButtonBox>
#include <QtGui/QPushButton>
82
#ifdef Q_OS_WIN
83
#    include "shared/sharedlibraryinjector.h"
84
#endif
con's avatar
con committed
85

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
86
#ifdef Q_OS_UNIX
con's avatar
con committed
87 88 89
#include <unistd.h>
#include <dlfcn.h>
#endif
hjk's avatar
hjk committed
90
#include <ctype.h>
con's avatar
con committed
91

92 93
namespace Debugger {
namespace Internal {
con's avatar
con committed
94 95

//#define DEBUG_PENDING  1
96
//#define DEBUG_SUBITEM  1
con's avatar
con committed
97 98 99 100

#if DEBUG_PENDING
#   define PENDING_DEBUG(s) qDebug() << s
#else
Roberto Raggi's avatar
Roberto Raggi committed
101
#   define PENDING_DEBUG(s)
con's avatar
con committed
102 103
#endif

104 105
#define CB(callback) &GdbEngine::callback, STRINGIFY(callback)

hjk's avatar
hjk committed
106
static bool stateAcceptsGdbCommands(DebuggerState state)
hjk's avatar
hjk committed
107
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
    switch (state) {
    case AdapterStarting:
    case AdapterStarted:
    case AdapterStartFailed:
    case InferiorUnrunnable:
    case InferiorStarting:
    case InferiorStartFailed:
    case InferiorRunningRequested:
    case InferiorRunning:
    case InferiorStopping:
    case InferiorStopped:
    case InferiorShuttingDown:
    case InferiorShutDown:
    case InferiorShutdownFailed:
        return true;
    case DebuggerNotReady:
    case EngineStarting:
    case InferiorStopFailed:
    case EngineShuttingDown:
        break;
    }
    return false;
}
hjk's avatar
hjk committed
131

con's avatar
con committed
132 133 134 135 136 137
static int &currentToken()
{
    static int token = 0;
    return token;
}

138
// reads a MI-encoded item frome the consolestream
hjk's avatar
hjk committed
139
static bool parseConsoleStream(const GdbResponse &response, GdbMi *contents)
140
{
hjk's avatar
hjk committed
141
    GdbMi output = response.data.findChild("consolestreamoutput");
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
    QByteArray out = output.data();

    int markerPos = out.indexOf('"') + 1; // position of 'success marker'
    if (markerPos == 0 || out.at(markerPos) == 'f') {  // 't' or 'f'
        // custom dumper produced no output
        return false;
    }

    out = out.mid(markerPos +  1);
    out = out.left(out.lastIndexOf('"'));
    // optimization: dumper output never needs real C unquoting
    out.replace('\\', "");
    out = "dummy={" + out + "}";

    contents->fromString(out);
    //qDebug() << "CONTENTS" << contents->toString(true);
    return contents->isValid();
}

hjk's avatar
hjk committed
161
static QByteArray parsePlainConsoleStream(const GdbResponse &response)
162
{
hjk's avatar
hjk committed
163
    GdbMi output = response.data.findChild("consolestreamoutput");
164 165 166 167 168 169 170 171 172 173
    QByteArray out = output.data();
    // FIXME: proper decoding needed
    if (out.endsWith("\\n"))
        out.chop(2);
    while (out.endsWith('\n') || out.endsWith(' '))
        out.chop(1);
    int pos = out.indexOf(" = ");
    return out.mid(pos + 3);
}

con's avatar
con committed
174 175 176 177 178 179
///////////////////////////////////////////////////////////////////////
//
// GdbEngine
//
///////////////////////////////////////////////////////////////////////

hjk's avatar
hjk committed
180 181
GdbEngine::GdbEngine(DebuggerManager *manager) :
    IDebuggerEngine(manager),
182
#ifdef Q_OS_WIN // Do injection loading with MinGW (call loading does not work with 64bit)
hjk's avatar
hjk committed
183
    m_dumperInjectionLoad(true)
184
#else
hjk's avatar
hjk committed
185
    m_dumperInjectionLoad(false)
186
#endif
con's avatar
con committed
187
{
188 189
    m_trkOptions = QSharedPointer<TrkOptions>(new TrkOptions);
    m_trkOptions->fromSettings(Core::ICore::instance()->settings());
hjk's avatar
hjk committed
190
    m_gdbAdapter = 0;
191

192 193
    connect(theDebuggerAction(AutoDerefPointers), SIGNAL(valueChanged(QVariant)),
            this, SLOT(setAutoDerefPointers(QVariant)));
194
}
195

196
void GdbEngine::connectDebuggingHelperActions()
197
{
198 199 200 201 202 203
    connect(theDebuggerAction(UseDebuggingHelpers), SIGNAL(valueChanged(QVariant)),
            this, SLOT(setUseDebuggingHelpers(QVariant)));
    connect(theDebuggerAction(DebugDebuggingHelpers), SIGNAL(valueChanged(QVariant)),
            this, SLOT(setDebugDebuggingHelpers(QVariant)));
    connect(theDebuggerAction(RecheckDebuggingHelpers), SIGNAL(triggered()),
            this, SLOT(recheckDebuggingHelperAvailability()));
204 205
}
   
206 207 208 209 210 211 212
void GdbEngine::disconnectDebuggingHelperActions()
{
    disconnect(theDebuggerAction(UseDebuggingHelpers), 0, this, 0);
    disconnect(theDebuggerAction(DebugDebuggingHelpers), 0, this, 0);
    disconnect(theDebuggerAction(RecheckDebuggingHelpers), 0, this, 0);
}

213 214 215 216
DebuggerStartMode GdbEngine::startMode() const
{
    QTC_ASSERT(!m_startParameters.isNull(), return NoStartMode);
    return m_startParameters->startMode;
con's avatar
con committed
217 218
}

219 220 221 222 223
QMainWindow *GdbEngine::mainWindow() const
{
    return m_manager->mainWindow();
}

con's avatar
con committed
224 225
GdbEngine::~GdbEngine()
{
hjk's avatar
hjk committed
226
    // prevent sending error messages afterwards
227
    delete m_gdbAdapter;
hjk's avatar
hjk committed
228 229
}

230
void GdbEngine::connectAdapter()
con's avatar
con committed
231
{
hjk's avatar
hjk committed
232 233
    connect(m_gdbAdapter, SIGNAL(adapterStarted()),
        this, SLOT(handleAdapterStarted()));
234 235
    connect(m_gdbAdapter, SIGNAL(adapterStartFailed(QString,QString)),
        this, SLOT(handleAdapterStartFailed(QString,QString)));
hjk's avatar
hjk committed
236 237 238 239

    connect(m_gdbAdapter, SIGNAL(inferiorStartFailed(QString)),
        this, SLOT(handleInferiorStartFailed(QString)));

240 241
    connect(m_gdbAdapter, SIGNAL(adapterCrashed(QString)),
        this, SLOT(handleAdapterCrashed(QString)));
242
}
243

244 245
void GdbEngine::initializeVariables()
{
246
    m_debuggingHelperState = DebuggingHelperUninitialized;
247
    m_gdbVersion = 100;
hjk's avatar
hjk committed
248
    m_gdbBuildVersion = -1;
hjk's avatar
hjk committed
249
    m_isSynchroneous = false;
250 251 252 253 254 255 256 257 258

    m_fullToShortName.clear();
    m_shortToFullName.clear();
    m_varToType.clear();

    m_modulesListOutdated = true;
    m_oldestAcceptableToken = -1;
    m_outputCodec = QTextCodec::codecForLocale();
    m_pendingRequests = 0;
259
    m_commandsDoneCallback = 0;
hjk's avatar
hjk committed
260
    m_commandsToRunOnTemporaryBreak.clear();
261
    m_cookieForToken.clear();
262 263 264 265 266 267

    m_pendingConsoleStreamOutput.clear();
    m_pendingLogStreamOutput.clear();

    m_inbuffer.clear();

268 269 270 271
    // ConverterState has no reset() function.
    m_outputCodecState.~ConverterState();
    new (&m_outputCodecState) QTextCodec::ConverterState();

272 273
    m_currentFunctionArgs.clear();
    m_currentFrame.clear();
274
    m_dumperHelper.clear();
275 276
}

277
QString GdbEngine::errorMessage(QProcess::ProcessError error)
con's avatar
con committed
278 279 280
{
    switch (error) {
        case QProcess::FailedToStart:
281
            return tr("The Gdb process failed to start. Either the "
con's avatar
con committed
282
                "invoked program '%1' is missing, or you may have insufficient "
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
283
                "permissions to invoke the program.")
hjk's avatar
hjk committed
284
                .arg(theDebuggerStringSetting(GdbLocation));
con's avatar
con committed
285
        case QProcess::Crashed:
286
            return tr("The Gdb process crashed some time after starting "
con's avatar
con committed
287 288
                "successfully.");
        case QProcess::Timedout:
289
            return tr("The last waitFor...() function timed out. "
con's avatar
con committed
290 291 292
                "The state of QProcess is unchanged, and you can try calling "
                "waitFor...() again.");
        case QProcess::WriteError:
293
            return tr("An error occurred when attempting to write "
con's avatar
con committed
294 295 296
                "to the Gdb process. For example, the process may not be running, "
                "or it may have closed its input channel.");
        case QProcess::ReadError:
297
            return tr("An error occurred when attempting to read from "
con's avatar
con committed
298 299
                "the Gdb process. For example, the process may not be running.");
        default:
300
            return tr("An unknown error in the Gdb process occurred. ");
con's avatar
con committed
301 302 303 304 305 306 307
    }
}

#if 0
static void dump(const char *first, const char *middle, const QString & to)
{
    QByteArray ba(first, middle - first);
308
    Q_UNUSED(to)
con's avatar
con committed
309 310 311 312 313 314 315 316 317 318 319
    // note that qDebug cuts off output after a certain size... (bug?)
    qDebug("\n>>>>> %s\n%s\n====\n%s\n<<<<<\n",
        qPrintable(currentTime()),
        qPrintable(QString(ba).trimmed()),
        qPrintable(to.trimmed()));
    //qDebug() << "";
    //qDebug() << qPrintable(currentTime())
    //    << " Reading response:  " << QString(ba).trimmed() << "\n";
}
#endif

320 321
void GdbEngine::readDebugeeOutput(const QByteArray &data)
{
322
    m_manager->showApplicationOutput(m_outputCodec->toUnicode(
323 324 325
            data.constData(), data.length(), &m_outputCodecState));
}

hjk's avatar
hjk committed
326 327
void GdbEngine::debugMessage(const QString &msg)
{
328
    gdbOutputAvailable(LogDebug, msg);
hjk's avatar
hjk committed
329 330
}

331
void GdbEngine::handleResponse(const QByteArray &buff)
con's avatar
con committed
332 333 334
{
    static QTime lastTime;

335
    if (theDebuggerBoolSetting(LogTimeStamps))
336 337
        gdbOutputAvailable(LogTime, currentTime());
    gdbOutputAvailable(LogOutput, QString::fromLocal8Bit(buff, buff.length()));
con's avatar
con committed
338 339 340 341 342

#if 0
    qDebug() // << "#### start response handling #### "
        << currentTime()
        << lastTime.msecsTo(QTime::currentTime()) << "ms,"
343 344
        << "buf:" << buff.left(1500) << "..."
        //<< "buf:" << buff
345
        << "size:" << buff.size();
con's avatar
con committed
346
#else
347
    //qDebug() << "buf:" << buff;
con's avatar
con committed
348 349 350 351
#endif

    lastTime = QTime::currentTime();

352 353
    if (buff.isEmpty() || buff == "(gdb) ")
        return;
con's avatar
con committed
354

355 356 357
    const char *from = buff.constData();
    const char *to = from + buff.size();
    const char *inner;
con's avatar
con committed
358

359 360 361 362
    int token = -1;
    // token is a sequence of numbers
    for (inner = from; inner != to; ++inner)
        if (*inner < '0' || *inner > '9')
con's avatar
con committed
363
            break;
364 365 366
    if (from != inner) {
        token = QByteArray(from, inner - from).toInt();
        from = inner;
367
        //qDebug() << "found token" << token;
368
    }
con's avatar
con committed
369

hjk's avatar
hjk committed
370
    // next char decides kind of response
371 372 373 374 375 376 377 378 379 380 381 382 383 384
    const char c = *from++;
    //qDebug() << "CODE:" << c;
    switch (c) {
        case '*':
        case '+':
        case '=': {
            QByteArray asyncClass;
            for (; from != to; ++from) {
                const char c = *from;
                if (!isNameChar(c))
                    break;
                asyncClass += *from;
            }
            //qDebug() << "ASYNCCLASS" << asyncClass;
con's avatar
con committed
385

hjk's avatar
hjk committed
386
            GdbMi result;
387 388
            while (from != to) {
                GdbMi data;
hjk's avatar
hjk committed
389
                if (*from != ',') {
hjk's avatar
hjk committed
390 391
                    // happens on archer where we get 
                    // 23^running <NL> *running,thread-id="all" <NL> (gdb) 
hjk's avatar
hjk committed
392
                    result.m_type = GdbMi::Tuple;
hjk's avatar
hjk committed
393 394 395 396 397
                    break;
                }
                ++from; // skip ','
                data.parseResultOrValue(from, to);
                if (data.isValid()) {
hjk's avatar
hjk committed
398 399 400
                    //qDebug() << "parsed result:" << data.toString();
                    result.m_children += data;
                    result.m_type = GdbMi::Tuple;
con's avatar
con committed
401 402
                }
            }
403
            if (asyncClass == "stopped") {
404
                handleStopResponse(result);
405 406
            } else if (asyncClass == "running") {
                // Archer has 'thread-id="all"' here
407 408 409 410
            } else if (asyncClass == "library-loaded") {
                // Archer has 'id="/usr/lib/libdrm.so.2",
                // target-name="/usr/lib/libdrm.so.2",
                // host-name="/usr/lib/libdrm.so.2",
411
                // symbols-loaded="0"
hjk's avatar
hjk committed
412
                QByteArray id = result.findChild("id").data();
413
                if (!id.isEmpty())
hjk's avatar
hjk committed
414
                    showStatusMessage(tr("Library %1 loaded.").arg(_(id)));
hjk's avatar
hjk committed
415 416 417 418
            } else if (asyncClass == "library-unloaded") {
                // Archer has 'id="/usr/lib/libdrm.so.2",
                // target-name="/usr/lib/libdrm.so.2",
                // host-name="/usr/lib/libdrm.so.2"
hjk's avatar
hjk committed
419
                QByteArray id = result.findChild("id").data();
hjk's avatar
hjk committed
420
                showStatusMessage(tr("Library %1 unloaded.").arg(_(id)));
421 422
            } else if (asyncClass == "thread-group-created") {
                // Archer has "{id="28902"}" 
hjk's avatar
hjk committed
423
                QByteArray id = result.findChild("id").data();
hjk's avatar
hjk committed
424
                showStatusMessage(tr("Thread group %1 created.").arg(_(id)));
425 426 427
                int pid = id.toInt();
                if (pid != inferiorPid())
                    handleInferiorPidChanged(pid);
428 429
            } else if (asyncClass == "thread-created") {
                //"{id="1",group-id="28902"}" 
hjk's avatar
hjk committed
430
                QByteArray id = result.findChild("id").data();
hjk's avatar
hjk committed
431
                showStatusMessage(tr("Thread %1 created.").arg(_(id)));
432 433
            } else if (asyncClass == "thread-group-exited") {
                // Archer has "{id="28902"}" 
hjk's avatar
hjk committed
434
                QByteArray id = result.findChild("id").data();
hjk's avatar
hjk committed
435
                showStatusMessage(tr("Thread group %1 exited.").arg(_(id)));
436 437
            } else if (asyncClass == "thread-exited") {
                //"{id="1",group-id="28902"}" 
hjk's avatar
hjk committed
438 439
                QByteArray id = result.findChild("id").data();
                QByteArray groupid = result.findChild("group-id").data();
hjk's avatar
hjk committed
440
                showStatusMessage(tr("Thread %1 in group %2 exited.")
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
441
                    .arg(_(id)).arg(_(groupid)));
hjk's avatar
hjk committed
442
            } else if (asyncClass == "thread-selected") {
hjk's avatar
hjk committed
443
                QByteArray id = result.findChild("id").data();
hjk's avatar
hjk committed
444
                showStatusMessage(tr("Thread %1 selected.").arg(_(id)));
hjk's avatar
hjk committed
445
                //"{id="2"}" 
446
            #if defined(Q_OS_MAC)
447 448 449 450 451 452 453 454 455 456 457
            } else if (asyncClass == "shlibs-updated") {
                // MAC announces updated libs
            } else if (asyncClass == "shlibs-added") {
                // MAC announces added libs
                // {shlib-info={num="2", name="libmathCommon.A_debug.dylib",
                // kind="-", dyld-addr="0x7f000", reason="dyld", requested-state="Y",
                // state="Y", path="/usr/lib/system/libmathCommon.A_debug.dylib",
                // description="/usr/lib/system/libmathCommon.A_debug.dylib",
                // loaded_addr="0x7f000", slide="0x7f000", prefix=""}}
            #endif
            } else {
458
                qDebug() << "IGNORED ASYNC OUTPUT"
hjk's avatar
hjk committed
459
                    << asyncClass << result.toString();
460
            }
461 462
            break;
        }
463

464
        case '~': {
465 466
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingConsoleStreamOutput += data;
467 468 469 470 471 472 473

            // Parse pid from noise.
            if (!inferiorPid()) {
                // Linux/Mac gdb: [New [Tt]hread 0x545 (LWP 4554)]
                static QRegExp re1(_("New .hread 0x[0-9a-f]+ \\(LWP ([0-9]*)\\)"));
                // MinGW 6.8: [New thread 2437.0x435345]
                static QRegExp re2(_("New .hread ([0-9]+)\\.0x[0-9a-f]*"));
hjk's avatar
hjk committed
474 475 476
                // Mac: [Switching to process 9294 local thread 0x2e03] or
                // [Switching to process 31773]
                static QRegExp re3(_("Switching to process ([0-9]+)"));
477 478 479 480 481
                QTC_ASSERT(re1.isValid() && re2.isValid(), return);
                if (re1.indexIn(_(data)) != -1)
                    maybeHandleInferiorPidChanged(re1.cap(1));
                else if (re2.indexIn(_(data)) != -1)
                    maybeHandleInferiorPidChanged(re2.cap(1));
482 483
                else if (re3.indexIn(_(data)) != -1)
                    maybeHandleInferiorPidChanged(re3.cap(1));
484
            }
485 486

            // Show some messages to give the impression something happens.
hjk's avatar
hjk committed
487
            if (data.startsWith("Reading symbols from "))
488
                showStatusMessage(tr("Reading %1...").arg(_(data.mid(21))), 1000);
489 490 491
            if (data.endsWith('\n'))
                data.chop(1);
            if (data.startsWith("[New ") || data.startsWith("[Thread "))
492
                showStatusMessage(_(data), 1000);
493 494
            break;
        }
495

496
        case '@': {
497
            readDebugeeOutput(GdbMi::parseCString(from, to));
498
            break;
con's avatar
con committed
499 500
        }

501 502 503 504 505 506
        case '&': {
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingLogStreamOutput += data;
            // On Windows, the contents seem to depend on the debugger
            // version and/or OS version used.
            if (data.startsWith("warning:"))
hjk's avatar
hjk committed
507
                manager()->showApplicationOutput(_(data.mid(9))); // cut "warning: "
con's avatar
con committed
508 509 510
            break;
        }

511
        case '^': {
hjk's avatar
hjk committed
512
            GdbResponse response;
con's avatar
con committed
513

hjk's avatar
hjk committed
514
            response.token = token;
con's avatar
con committed
515

516 517 518
            for (inner = from; inner != to; ++inner)
                if (*inner < 'a' || *inner > 'z')
                    break;
con's avatar
con committed
519

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
520
            QByteArray resultClass = QByteArray::fromRawData(from, inner - from);
521
            if (resultClass == "done") {
hjk's avatar
hjk committed
522
                response.resultClass = GdbResultDone;
523 524 525
            } else if (resultClass == "running") {
                setState(InferiorRunning);
                showStatusMessage(tr("Running..."));
hjk's avatar
hjk committed
526
                response.resultClass = GdbResultRunning;
527
            } else if (resultClass == "connected") {
hjk's avatar
hjk committed
528
                response.resultClass = GdbResultConnected;
529
            } else if (resultClass == "error") {
hjk's avatar
hjk committed
530
                response.resultClass = GdbResultError;
531
            } else if (resultClass == "exit") {
hjk's avatar
hjk committed
532
                response.resultClass = GdbResultExit;
533
            } else {
hjk's avatar
hjk committed
534
                response.resultClass = GdbResultUnknown;
535
            }
con's avatar
con committed
536

537 538
            from = inner;
            if (from != to) {
hjk's avatar
hjk committed
539 540
                if (*from == ',') {
                    ++from;
hjk's avatar
hjk committed
541 542 543
                    response.data.parseTuple_helper(from, to);
                    response.data.m_type = GdbMi::Tuple;
                    response.data.m_name = "data";
hjk's avatar
hjk committed
544 545
                } else {
                    // Archer has this
hjk's avatar
hjk committed
546 547
                    response.data.m_type = GdbMi::Tuple;
                    response.data.m_name = "data";
con's avatar
con committed
548 549
                }
            }
550 551 552

            //qDebug() << "\nLOG STREAM:" + m_pendingLogStreamOutput;
            //qDebug() << "\nCONSOLE STREAM:" + m_pendingConsoleStreamOutput;
hjk's avatar
hjk committed
553
            response.data.setStreamOutput("logstreamoutput",
554
                m_pendingLogStreamOutput);
hjk's avatar
hjk committed
555
            response.data.setStreamOutput("consolestreamoutput",
556 557 558 559
                m_pendingConsoleStreamOutput);
            m_pendingLogStreamOutput.clear();
            m_pendingConsoleStreamOutput.clear();

hjk's avatar
hjk committed
560
            handleResultRecord(response);
561 562 563 564 565
            break;
        }
        default: {
            qDebug() << "UNKNOWN RESPONSE TYPE" << c;
            break;
con's avatar
con committed
566 567 568 569 570 571
        }
    }
}

void GdbEngine::readGdbStandardError()
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
572
    qWarning() << "Unexpected gdb stderr:" << m_gdbProc.readAllStandardError();
con's avatar
con committed
573 574 575 576
}

void GdbEngine::readGdbStandardOutput()
{
577 578
    int newstart = 0;
    int scan = m_inbuffer.size();
con's avatar
con committed
579

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
580
    m_inbuffer.append(m_gdbProc.readAllStandardOutput());
con's avatar
con committed
581

582 583
    while (newstart < m_inbuffer.size()) {
        int start = newstart;
584
        int end = m_inbuffer.indexOf('\n', scan);
585 586 587 588 589
        if (end < 0) {
            m_inbuffer.remove(0, start);
            return;
        }
        newstart = end + 1;
590
        scan = newstart;
591 592
        if (end == start)
            continue;
593
        #if defined(Q_OS_WIN)
594 595 596 597 598
        if (m_inbuffer.at(end - 1) == '\r') {
            --end;
            if (end == start)
                continue;
        }
599
        #endif
600
        handleResponse(QByteArray::fromRawData(m_inbuffer.constData() + start, end - start));
con's avatar
con committed
601
    }
602
    m_inbuffer.clear();
con's avatar
con committed
603 604 605 606
}

void GdbEngine::interruptInferior()
{
hjk's avatar
hjk committed
607
    QTC_ASSERT(state() == InferiorRunning, qDebug() << state());
608

hjk's avatar
hjk committed
609
    if (state() == DebuggerNotReady) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
610
        debugMessage(_("TRYING TO INTERRUPT INFERIOR WITHOUT RUNNING GDB"));
611
        shutdown();
con's avatar
con committed
612
        return;
hjk's avatar
hjk committed
613
    }
con's avatar
con committed
614

hjk's avatar
hjk committed
615 616 617
    setState(InferiorStopping);
    showStatusMessage(tr("Stop requested..."), 5000);

618
    debugMessage(_("TRYING TO INTERUPT INFERIOR"));
hjk's avatar
hjk committed
619
    m_gdbAdapter->interruptInferior();
con's avatar
con committed
620 621 622 623
}

void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
{
624
    const qint64 pid = pid0.toLongLong();
con's avatar
con committed
625
    if (pid == 0) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
626
        debugMessage(_("Cannot parse PID from %1").arg(pid0));
con's avatar
con committed
627 628
        return;
    }
hjk's avatar
hjk committed
629
    if (pid == inferiorPid())
con's avatar
con committed
630
        return;
631 632
    debugMessage(_("FOUND PID %1").arg(pid));    

633
    handleInferiorPidChanged(pid);
634 635
    if (m_dumperInjectionLoad)
        tryLoadDebuggingHelpers();
con's avatar
con committed
636 637
}

hjk's avatar
hjk committed
638 639
void GdbEngine::postCommand(const QString &command, AdapterCallback callback,
                            const char *callbackName, const QVariant &cookie)
640 641 642 643 644 645 646
{
    postCommand(command, NoFlags, callback, callbackName, cookie);
}

void GdbEngine::postCommand(const QString &command, GdbCommandFlags flags,
                            AdapterCallback callback,
                            const char *callbackName, const QVariant &cookie)
hjk's avatar
hjk committed
647 648 649
{
    GdbCommand cmd;
    cmd.command = command;
650
    cmd.flags = flags;
hjk's avatar
hjk committed
651 652 653 654 655 656
    cmd.adapterCallback = callback;
    cmd.callbackName = callbackName;
    cmd.cookie = cookie;
    postCommandHelper(cmd);
}

657
void GdbEngine::postCommand(const QString &command, GdbCommandCallback callback,
658 659
                            const char *callbackName, const QVariant &cookie)
{
660
    postCommand(command, NoFlags, callback, callbackName, cookie);
661 662
}

663
void GdbEngine::postCommand(const QString &command, GdbCommandFlags flags,
664 665
                            GdbCommandCallback callback, const char *callbackName,
                            const QVariant &cookie)
hjk's avatar
hjk committed
666 667 668 669 670 671 672 673 674 675 676
{
    GdbCommand cmd;
    cmd.command = command;
    cmd.flags = flags;
    cmd.callback = callback;
    cmd.callbackName = callbackName;
    cmd.cookie = cookie;
    postCommandHelper(cmd);
}

void GdbEngine::postCommandHelper(const GdbCommand &cmd)
con's avatar
con committed
677
{
hjk's avatar
hjk committed
678
    if (!stateAcceptsGdbCommands(state())) {
hjk's avatar
hjk committed
679
        PENDING_DEBUG(_("NO GDB PROCESS RUNNING, CMD IGNORED: ") + cmd.command);
680 681
        debugMessage(_("NO GDB PROCESS RUNNING, CMD IGNORED: %1 %2")
            .arg(cmd.command).arg(state()));
con's avatar
con committed
682 683 684
        return;
    }

hjk's avatar
hjk committed
685
    if (cmd.flags & RebuildModel) {
con's avatar
con committed
686
        ++m_pendingRequests;
hjk's avatar
hjk committed
687
        PENDING_DEBUG("   CALLBACK" << cmd.callbackName
688
            << "INCREMENTS PENDING TO:" << m_pendingRequests << cmd.command);
con's avatar
con committed
689
    } else {
hjk's avatar
hjk committed
690
        PENDING_DEBUG("   UNKNOWN CALLBACK" << cmd.callbackName
691
            << "LEAVES PENDING AT:" << m_pendingRequests << cmd.command);
con's avatar
con committed
692 693
    }

hjk's avatar
hjk committed
694
    if (cmd.flags & NeedsStop) {
695
        if (state() == InferiorStopped || state() == AdapterStarted) {
hjk's avatar
hjk committed
696 697 698 699 700 701 702 703 704 705
            // Can be safely sent now.
            flushCommand(cmd);
        } else {
            // Queue the commands that we cannot send at once.
            showStatusMessage(tr("Stopping temporarily."), 1000);
            qDebug() << _("QUEUING COMMAND ") + cmd.command;
            debugMessage(_("QUEUING COMMAND ") + cmd.command);
            m_commandsToRunOnTemporaryBreak.append(cmd);
            interruptInferior();
        }
hjk's avatar
hjk committed
706
    } else if (!cmd.command.isEmpty()) {
707
        flushCommand(cmd);
con's avatar
con committed
708 709 710
    }
}

hjk's avatar
hjk committed
711
void GdbEngine::flushCommand(const GdbCommand &cmd0)
712
{
hjk's avatar
hjk committed
713
    GdbCommand cmd = cmd0;
hjk's avatar
hjk committed
714
    if (state() == DebuggerNotReady) {
715
        gdbInputAvailable(LogInput, cmd.command);
716 717 718 719
        debugMessage(_("GDB PROCESS NOT RUNNING, PLAIN CMD IGNORED: ") + cmd.command);
        return;
    }

720
    ++currentToken();
721
    cmd.postTime = QTime::currentTime();
722 723
    m_cookieForToken[currentToken()] = cmd;
    cmd.command = QString::number(currentToken()) + cmd.command;
724
    if (cmd.flags & EmbedToken)
725
        cmd.command = cmd.command.arg(currentToken());
726
    gdbInputAvailable(LogInput, cmd.command);
727 728

    m_gdbAdapter->write(cmd.command.toLatin1() + "\r\n");
729 730
}

hjk's avatar
hjk committed
731
void GdbEngine::handleResultRecord(const GdbResponse &response)
con's avatar
con committed
732
{
hjk's avatar
hjk committed
733
    //qDebug() << "TOKEN:" << response.token
734
    //    << " ACCEPTABLE:" << m_oldestAcceptableToken;
hjk's avatar
hjk committed
735
    //qDebug() << "\nRESULT" << response.token << response.toString();
con's avatar
con committed
736

hjk's avatar
hjk committed
737
    int token = response.token;
con's avatar
con committed
738 739 740
    if (token == -1)
        return;

741
    if (!m_cookieForToken.contains(token)) {
742 743 744 745
        // In theory this should not happen (rather the error should be
        // reported in the "first" response to the command) in practice it
        // does. We try to handle a few situations we are aware of gracefully.
        // Ideally, this code should not be present at all.
746 747
        debugMessage(_("COOKIE FOR TOKEN %1 ALREADY EATEN. "
            "TWO RESPONSES FOR ONE COMMAND?").arg(token));
hjk's avatar
hjk committed
748 749
        if (response.resultClass == GdbResultError) {
            QByteArray msg = response.data.findChild("msg").data();
750 751 752 753 754 755 756
            if (msg == "Cannot find new threads: generic error") {
                // Handle a case known to occur on Linux/gdb 6.8 when debugging moc
                // with helpers enabled. In this case we get a second response with
                // msg="Cannot find new threads: generic error"
                showMessageBox(QMessageBox::Critical,
                    tr("Executable failed"), QString::fromLocal8Bit(msg));
                showStatusMessage(tr("Process failed to start."));
757
                shutdown();
758 759 760
            } else if (msg == "\"finish\" not meaningful in the outermost frame.") { 
                // Handle a case known to appear on gdb 6.4 symbianelf when
                // the stack is cut due to access to protected memory.
761 762
                setState(InferiorStopping);
                setState(InferiorStopped);
763 764 765 766 767 768 769 770 771 772 773
            } else if (msg.startsWith("Cannot find bounds of current function")) {
                // Happens when running "-exec-next" in a function for which
                // there is no debug information. Divert to "-exec-next-step"
                setState(InferiorStopping);
                setState(InferiorStopped);
                nextIExec();
            } else {
                showMessageBox(QMessageBox::Critical,
                    tr("Executable failed"), QString::fromLocal8Bit(msg));
                showStatusMessage(tr("Executable failed: %1")
                    .arg(QString::fromLocal8Bit(msg)));
774
            }
775 776 777 778
        }
        return;
    }

779
    GdbCommand cmd = m_cookieForToken.take(token);
780
    if (theDebuggerBoolSetting(LogTimeStamps)) {
781
        gdbOutputAvailable(LogTime, _("Response time: %1: %2 s")
782 783 784
            .arg(cmd.command)
            .arg(cmd.postTime.msecsTo(QTime::currentTime()) / 1000.));
    }
con's avatar
con committed
785

hjk's avatar
hjk committed
786
    if (response.token < m_oldestAcceptableToken && (cmd.flags & Discardable)) {
787
        //debugMessage(_("### SKIPPING OLD RESULT") + response.toString());
con's avatar
con committed
788 789 790
        return;
    }

hjk's avatar
hjk committed
791 792 793
    GdbResponse responseWithCookie = response;
    responseWithCookie.cookie = cmd.cookie;

794
    if (response.resultClass != GdbResultError &&
795 796 797 798 799 800
        response.resultClass != ((cmd.flags & RunRequest) ? GdbResultRunning :
                                 (cmd.flags & ExitRequest) ? GdbResultExit :
                                 GdbResultDone)) {
        QString rsp = _(GdbResponse::stringFromResultClass(response.resultClass));
        qWarning() << "UNEXPECTED RESPONSE " << rsp << " TO COMMAND" << cmd.command << " AT " __FILE__ ":" STRINGIFY(__LINE__);
        debugMessage(_("UNEXPECTED RESPONSE %1 TO COMMAND %2").arg(rsp).arg(cmd.command));
801 802 803 804 805 806
    } else {
        if (cmd.callback)
            (this->*cmd.callback)(responseWithCookie);
        else if (cmd.adapterCallback)
            (m_gdbAdapter->*cmd.adapterCallback)(responseWithCookie);
    }
con's avatar
con committed
807

808
    if (cmd.flags & RebuildModel) {
con's avatar
con committed
809
        --m_pendingRequests;
hjk's avatar
hjk committed
810
        PENDING_DEBUG("   TYPE " << cmd.callbackName << " DECREMENTS PENDING TO: "
con's avatar
con committed
811
            << m_pendingRequests << cmd.command);
812
        if (m_pendingRequests <= 0) {
hjk's avatar
hjk committed
813 814
            PENDING_DEBUG("\n\n ....  AND TRIGGERS MODEL UPDATE\n");
            rebuildModel();
815
        }
con's avatar
con committed
816
    } else {
hjk's avatar
hjk committed
817
        PENDING_DEBUG("   UNKNOWN TYPE " << cmd.callbackName << " LEAVES PENDING AT: "
con's avatar
con committed
818 819
            << m_pendingRequests << cmd.command);
    }
820

821 822
    // Continue only if there are no commands wire anymore, so this will
    // be fully synchroneous.
823 824 825 826
    // This is somewhat inefficient, as it makes the last command synchronous.
    // An optimization would be requesting the continue immediately when the
    // event loop is entered, and let individual commands have a flag to suppress
    // that behavior.
827
    if (m_commandsDoneCallback && m_cookieForToken.isEmpty()) {
828
        debugMessage(_("ALL COMMANDS DONE; INVOKING CALLBACK"));
829 830
        CommandsDoneCallback cont = m_commandsDoneCallback;
        m_commandsDoneCallback = 0;
hjk's avatar
hjk committed
831 832 833
        (this->*cont)();
    } else {
        PENDING_DEBUG("MISSING TOKENS: " << m_cookieForToken.keys());
834
    }
con's avatar
con committed
835 836 837 838
}

void GdbEngine::executeDebuggerCommand(const QString &command)
{
hjk's avatar
hjk committed
839
    if (state() == DebuggerNotReady) {
840
        debugMessage(_("GDB PROCESS NOT RUNNING, PLAIN CMD IGNORED: ") + command);
con's avatar
con committed
841 842 843
        return;
    }

hjk's avatar
hjk committed
844
    m_gdbAdapter->write(command.toLatin1() + "\r\n");
con's avatar
con committed
845 846
}

847
// Called from CoreAdapter and AttachAdapter
848
void GdbEngine::updateAll()
849
{
850
    QTC_ASSERT(state() == InferiorUnrunnable || state() == InferiorStopped, /**/);
851
    tryLoadDebuggingHelpers();
hjk's avatar
hjk committed
852 853
    postCommand(_("-stack-list-frames"), WatchUpdate, CB(handleStackListFrames),
        QVariant::fromValue<StackCookie>(StackCookie(false, true)));
854
    manager()->stackHandler()->setCurrentIndex(0);
855
    if (supportsThreads())
856
        postCommand(_("-thread-list-ids"), WatchUpdate, CB(handleStackListThreads), 0);
hjk's avatar
hjk committed
857
    manager()->reloadRegisters();
hjk's avatar
hjk committed
858
    updateLocals(); 
859 860
}