gdbengine.cpp 156 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 "termgdbadapter.h"
41
#include "remotegdbadapter.h"
42
#include "trkgdbadapter.h"
con's avatar
con committed
43

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

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

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

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

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

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

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

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

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

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

106 107
#define CB(callback) &GdbEngine::callback, STRINGIFY(callback)

hjk's avatar
hjk committed
108
static bool stateAcceptsGdbCommands(DebuggerState state)
hjk's avatar
hjk committed
109
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
    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
133

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

140
// reads a MI-encoded item frome the consolestream
hjk's avatar
hjk committed
141
static bool parseConsoleStream(const GdbResponse &response, GdbMi *contents)
142
{
hjk's avatar
hjk committed
143
    GdbMi output = response.data.findChild("consolestreamoutput");
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
    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
163
static QByteArray parsePlainConsoleStream(const GdbResponse &response)
164
{
hjk's avatar
hjk committed
165
    GdbMi output = response.data.findChild("consolestreamoutput");
166 167 168 169 170 171 172 173 174 175
    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
176 177 178 179 180 181
///////////////////////////////////////////////////////////////////////
//
// GdbEngine
//
///////////////////////////////////////////////////////////////////////

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

194 195 196
    // Needs no resetting in initializeVariables()
    m_busy = false;

197 198
    connect(theDebuggerAction(AutoDerefPointers), SIGNAL(valueChanged(QVariant)),
            this, SLOT(setAutoDerefPointers(QVariant)));
199
}
200

201
void GdbEngine::connectDebuggingHelperActions()
202
{
203 204 205 206 207 208
    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()));
209 210
}
   
211 212 213 214 215 216 217
void GdbEngine::disconnectDebuggingHelperActions()
{
    disconnect(theDebuggerAction(UseDebuggingHelpers), 0, this, 0);
    disconnect(theDebuggerAction(DebugDebuggingHelpers), 0, this, 0);
    disconnect(theDebuggerAction(RecheckDebuggingHelpers), 0, this, 0);
}

218 219 220 221
DebuggerStartMode GdbEngine::startMode() const
{
    QTC_ASSERT(!m_startParameters.isNull(), return NoStartMode);
    return m_startParameters->startMode;
con's avatar
con committed
222 223
}

224 225 226 227 228
QMainWindow *GdbEngine::mainWindow() const
{
    return m_manager->mainWindow();
}

con's avatar
con committed
229 230
GdbEngine::~GdbEngine()
{
hjk's avatar
hjk committed
231
    // prevent sending error messages afterwards
232
    disconnect(&m_gdbProc, 0, this, 0);
233
    delete m_gdbAdapter;
234
    m_gdbAdapter = 0;
hjk's avatar
hjk committed
235 236
}

237
void GdbEngine::connectAdapter()
con's avatar
con committed
238
{
hjk's avatar
hjk committed
239 240
    connect(m_gdbAdapter, SIGNAL(adapterStarted()),
        this, SLOT(handleAdapterStarted()));
241 242
    connect(m_gdbAdapter, SIGNAL(adapterStartFailed(QString,QString)),
        this, SLOT(handleAdapterStartFailed(QString,QString)));
hjk's avatar
hjk committed
243

244 245 246
    connect(m_gdbAdapter, SIGNAL(inferiorPrepared()),
        this, SLOT(handleInferiorPrepared()));

hjk's avatar
hjk committed
247 248 249
    connect(m_gdbAdapter, SIGNAL(inferiorStartFailed(QString)),
        this, SLOT(handleInferiorStartFailed(QString)));

250 251
    connect(m_gdbAdapter, SIGNAL(adapterCrashed(QString)),
        this, SLOT(handleAdapterCrashed(QString)));
252
}
253

254 255
void GdbEngine::initializeVariables()
{
256
    m_debuggingHelperState = DebuggingHelperUninitialized;
257
    m_gdbVersion = 100;
hjk's avatar
hjk committed
258
    m_gdbBuildVersion = -1;
259
    m_isMacGdb = false;
hjk's avatar
hjk committed
260
    m_isSynchroneous = false;
261 262 263 264 265

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

266 267
    m_modulesListOutdated = m_sourcesListOutdated = true;
    m_sourcesListUpdating = false;
268 269 270
    m_oldestAcceptableToken = -1;
    m_outputCodec = QTextCodec::codecForLocale();
    m_pendingRequests = 0;
271
    m_commandsDoneCallback = 0;
hjk's avatar
hjk committed
272
    m_commandsToRunOnTemporaryBreak.clear();
273
    m_cookieForToken.clear();
274 275 276 277 278 279

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

    m_inbuffer.clear();

280 281 282 283
    // ConverterState has no reset() function.
    m_outputCodecState.~ConverterState();
    new (&m_outputCodecState) QTextCodec::ConverterState();

284 285
    m_currentFunctionArgs.clear();
    m_currentFrame.clear();
286
    m_dumperHelper.clear();
287 288
}

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

#if 0
static void dump(const char *first, const char *middle, const QString & to)
{
    QByteArray ba(first, middle - first);
320
    Q_UNUSED(to)
con's avatar
con committed
321 322 323 324 325 326 327 328 329 330 331
    // 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

332 333
void GdbEngine::readDebugeeOutput(const QByteArray &data)
{
334
    m_manager->showApplicationOutput(m_outputCodec->toUnicode(
335 336 337
            data.constData(), data.length(), &m_outputCodecState));
}

hjk's avatar
hjk committed
338 339
void GdbEngine::debugMessage(const QString &msg)
{
340
    gdbOutputAvailable(LogDebug, msg);
hjk's avatar
hjk committed
341 342
}

343
void GdbEngine::handleResponse(const QByteArray &buff)
con's avatar
con committed
344 345 346
{
    static QTime lastTime;

347
    if (theDebuggerBoolSetting(LogTimeStamps))
348 349
        gdbOutputAvailable(LogTime, currentTime());
    gdbOutputAvailable(LogOutput, QString::fromLocal8Bit(buff, buff.length()));
con's avatar
con committed
350 351 352 353 354

#if 0
    qDebug() // << "#### start response handling #### "
        << currentTime()
        << lastTime.msecsTo(QTime::currentTime()) << "ms,"
355 356
        << "buf:" << buff.left(1500) << "..."
        //<< "buf:" << buff
357
        << "size:" << buff.size();
con's avatar
con committed
358
#else
359
    //qDebug() << "buf:" << buff;
con's avatar
con committed
360 361 362 363
#endif

    lastTime = QTime::currentTime();

364 365
    if (buff.isEmpty() || buff == "(gdb) ")
        return;
con's avatar
con committed
366

367 368 369
    const char *from = buff.constData();
    const char *to = from + buff.size();
    const char *inner;
con's avatar
con committed
370

371 372 373 374
    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
375
            break;
376 377 378
    if (from != inner) {
        token = QByteArray(from, inner - from).toInt();
        from = inner;
379
        //qDebug() << "found token" << token;
380
    }
con's avatar
con committed
381

hjk's avatar
hjk committed
382
    // next char decides kind of response
383 384 385 386 387 388 389 390 391 392 393 394 395 396
    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
397

hjk's avatar
hjk committed
398
            GdbMi result;
399 400
            while (from != to) {
                GdbMi data;
hjk's avatar
hjk committed
401
                if (*from != ',') {
hjk's avatar
hjk committed
402 403
                    // happens on archer where we get 
                    // 23^running <NL> *running,thread-id="all" <NL> (gdb) 
hjk's avatar
hjk committed
404
                    result.m_type = GdbMi::Tuple;
hjk's avatar
hjk committed
405 406 407 408 409
                    break;
                }
                ++from; // skip ','
                data.parseResultOrValue(from, to);
                if (data.isValid()) {
hjk's avatar
hjk committed
410 411 412
                    //qDebug() << "parsed result:" << data.toString();
                    result.m_children += data;
                    result.m_type = GdbMi::Tuple;
con's avatar
con committed
413 414
                }
            }
415
            if (asyncClass == "stopped") {
416
                handleStopResponse(result);
417 418
            } else if (asyncClass == "running") {
                // Archer has 'thread-id="all"' here
419 420 421 422
            } 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",
423
                // symbols-loaded="0"
hjk's avatar
hjk committed
424
                QByteArray id = result.findChild("id").data();
425
                if (!id.isEmpty())
hjk's avatar
hjk committed
426
                    showStatusMessage(tr("Library %1 loaded.").arg(_(id)));
427
                m_modulesListOutdated = m_sourcesListOutdated = true;
hjk's avatar
hjk committed
428 429 430 431
            } 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
432
                QByteArray id = result.findChild("id").data();
hjk's avatar
hjk committed
433
                showStatusMessage(tr("Library %1 unloaded.").arg(_(id)));
434
                m_modulesListOutdated = m_sourcesListOutdated = true;
435 436
            } else if (asyncClass == "thread-group-created") {
                // Archer has "{id="28902"}" 
hjk's avatar
hjk committed
437
                QByteArray id = result.findChild("id").data();
hjk's avatar
hjk committed
438
                showStatusMessage(tr("Thread group %1 created.").arg(_(id)));
439 440 441
                int pid = id.toInt();
                if (pid != inferiorPid())
                    handleInferiorPidChanged(pid);
442 443
            } else if (asyncClass == "thread-created") {
                //"{id="1",group-id="28902"}" 
hjk's avatar
hjk committed
444
                QByteArray id = result.findChild("id").data();
hjk's avatar
hjk committed
445
                showStatusMessage(tr("Thread %1 created.").arg(_(id)));
446 447
            } else if (asyncClass == "thread-group-exited") {
                // Archer has "{id="28902"}" 
hjk's avatar
hjk committed
448
                QByteArray id = result.findChild("id").data();
hjk's avatar
hjk committed
449
                showStatusMessage(tr("Thread group %1 exited.").arg(_(id)));
450 451
            } else if (asyncClass == "thread-exited") {
                //"{id="1",group-id="28902"}" 
hjk's avatar
hjk committed
452 453
                QByteArray id = result.findChild("id").data();
                QByteArray groupid = result.findChild("group-id").data();
hjk's avatar
hjk committed
454
                showStatusMessage(tr("Thread %1 in group %2 exited.")
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
455
                    .arg(_(id)).arg(_(groupid)));
hjk's avatar
hjk committed
456
            } else if (asyncClass == "thread-selected") {
hjk's avatar
hjk committed
457
                QByteArray id = result.findChild("id").data();
hjk's avatar
hjk committed
458
                showStatusMessage(tr("Thread %1 selected.").arg(_(id)));
hjk's avatar
hjk committed
459
                //"{id="2"}" 
460
            #if defined(Q_OS_MAC)
461 462
            } else if (asyncClass == "shlibs-updated") {
                // MAC announces updated libs
463
                m_modulesListOutdated = m_sourcesListOutdated = true;
464 465 466 467 468 469 470
            } 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=""}}
471
                m_modulesListOutdated = m_sourcesListOutdated = true;
472 473
            #endif
            } else {
474
                qDebug() << "IGNORED ASYNC OUTPUT"
hjk's avatar
hjk committed
475
                    << asyncClass << result.toString();
476
            }
477 478
            break;
        }
479

480
        case '~': {
481 482
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingConsoleStreamOutput += data;
483 484 485 486 487 488 489

            // 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
490 491 492
                // Mac: [Switching to process 9294 local thread 0x2e03] or
                // [Switching to process 31773]
                static QRegExp re3(_("Switching to process ([0-9]+)"));
493 494 495 496 497
                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));
498 499
                else if (re3.indexIn(_(data)) != -1)
                    maybeHandleInferiorPidChanged(re3.cap(1));
500
            }
501 502

            // Show some messages to give the impression something happens.
503
            if (data.startsWith("Reading symbols from ")) {
504
                showStatusMessage(tr("Reading %1...").arg(_(data.mid(21))), 1000);
505
                m_modulesListOutdated = m_sourcesListOutdated = true;
506 507 508
            } else if (data.startsWith("[New ") || data.startsWith("[Thread ")) {
                if (data.endsWith('\n'))
                    data.chop(1);
509
                showStatusMessage(_(data), 1000);
510
            }
511 512
            break;
        }
513

514
        case '@': {
515
            readDebugeeOutput(GdbMi::parseCString(from, to));
516
            break;
con's avatar
con committed
517 518
        }

519 520 521 522 523 524
        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
525
                manager()->showApplicationOutput(_(data.mid(9))); // cut "warning: "
con's avatar
con committed
526 527 528
            break;
        }

529
        case '^': {
hjk's avatar
hjk committed
530
            GdbResponse response;
con's avatar
con committed
531

hjk's avatar
hjk committed
532
            response.token = token;
con's avatar
con committed
533

534 535 536
            for (inner = from; inner != to; ++inner)
                if (*inner < 'a' || *inner > 'z')
                    break;
con's avatar
con committed
537

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
538
            QByteArray resultClass = QByteArray::fromRawData(from, inner - from);
539
            if (resultClass == "done") {
hjk's avatar
hjk committed
540
                response.resultClass = GdbResultDone;
541
            } else if (resultClass == "running") {
542 543 544 545 546
                if (state() == InferiorStopped) { // Result of manual command.
                    m_manager->resetLocation();
                    setTokenBarrier();
                    setState(InferiorRunningRequested);
                }
547 548
                setState(InferiorRunning);
                showStatusMessage(tr("Running..."));
hjk's avatar
hjk committed
549
                response.resultClass = GdbResultRunning;
550
            } else if (resultClass == "connected") {
hjk's avatar
hjk committed
551
                response.resultClass = GdbResultConnected;
552
            } else if (resultClass == "error") {
hjk's avatar
hjk committed
553
                response.resultClass = GdbResultError;
554
            } else if (resultClass == "exit") {
hjk's avatar
hjk committed
555
                response.resultClass = GdbResultExit;
556
            } else {
hjk's avatar
hjk committed
557
                response.resultClass = GdbResultUnknown;
558
            }
con's avatar
con committed
559

560 561
            from = inner;
            if (from != to) {
hjk's avatar
hjk committed
562 563
                if (*from == ',') {
                    ++from;
hjk's avatar
hjk committed
564 565 566
                    response.data.parseTuple_helper(from, to);
                    response.data.m_type = GdbMi::Tuple;
                    response.data.m_name = "data";
hjk's avatar
hjk committed
567 568
                } else {
                    // Archer has this
hjk's avatar
hjk committed
569 570
                    response.data.m_type = GdbMi::Tuple;
                    response.data.m_name = "data";
con's avatar
con committed
571 572
                }
            }
573 574 575

            //qDebug() << "\nLOG STREAM:" + m_pendingLogStreamOutput;
            //qDebug() << "\nCONSOLE STREAM:" + m_pendingConsoleStreamOutput;
hjk's avatar
hjk committed
576
            response.data.setStreamOutput("logstreamoutput",
577
                m_pendingLogStreamOutput);
hjk's avatar
hjk committed
578
            response.data.setStreamOutput("consolestreamoutput",
579 580 581 582
                m_pendingConsoleStreamOutput);
            m_pendingLogStreamOutput.clear();
            m_pendingConsoleStreamOutput.clear();

583
            handleResultRecord(&response);
584 585 586 587 588
            break;
        }
        default: {
            qDebug() << "UNKNOWN RESPONSE TYPE" << c;
            break;
con's avatar
con committed
589 590 591 592 593 594
        }
    }
}

void GdbEngine::readGdbStandardError()
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
595
    qWarning() << "Unexpected gdb stderr:" << m_gdbProc.readAllStandardError();
con's avatar
con committed
596 597 598 599
}

void GdbEngine::readGdbStandardOutput()
{
600 601
    int newstart = 0;
    int scan = m_inbuffer.size();
con's avatar
con committed
602

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
603
    m_inbuffer.append(m_gdbProc.readAllStandardOutput());
con's avatar
con committed
604

605 606 607 608
    // This can trigger when a dialog starts a nested event loop
    if (m_busy)
        return;

609 610
    while (newstart < m_inbuffer.size()) {
        int start = newstart;
611
        int end = m_inbuffer.indexOf('\n', scan);
612 613 614 615 616
        if (end < 0) {
            m_inbuffer.remove(0, start);
            return;
        }
        newstart = end + 1;
617
        scan = newstart;
618 619
        if (end == start)
            continue;
620
        #if defined(Q_OS_WIN)
621 622 623 624 625
        if (m_inbuffer.at(end - 1) == '\r') {
            --end;
            if (end == start)
                continue;
        }
626
        #endif
627
        m_busy = true;
628
        handleResponse(QByteArray::fromRawData(m_inbuffer.constData() + start, end - start));
629
        m_busy = false;
con's avatar
con committed
630
    }
631
    m_inbuffer.clear();
con's avatar
con committed
632 633 634 635
}

void GdbEngine::interruptInferior()
{
636
    QTC_ASSERT(state() == InferiorRunning, qDebug() << state(); return);
con's avatar
con committed
637

hjk's avatar
hjk committed
638 639 640
    setState(InferiorStopping);
    showStatusMessage(tr("Stop requested..."), 5000);

641
    debugMessage(_("TRYING TO INTERRUPT INFERIOR"));
hjk's avatar
hjk committed
642
    m_gdbAdapter->interruptInferior();
con's avatar
con committed
643 644 645 646
}

void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
{
647
    const qint64 pid = pid0.toLongLong();
con's avatar
con committed
648
    if (pid == 0) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
649
        debugMessage(_("Cannot parse PID from %1").arg(pid0));
con's avatar
con committed
650 651
        return;
    }
hjk's avatar
hjk committed
652
    if (pid == inferiorPid())
con's avatar
con committed
653
        return;
654 655
    debugMessage(_("FOUND PID %1").arg(pid));    

656
    handleInferiorPidChanged(pid);
657 658
    if (m_dumperInjectionLoad)
        tryLoadDebuggingHelpers();
con's avatar
con committed
659 660
}

hjk's avatar
hjk committed
661 662
void GdbEngine::postCommand(const QString &command, AdapterCallback callback,
                            const char *callbackName, const QVariant &cookie)
663 664 665 666 667 668 669
{
    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
670 671 672
{
    GdbCommand cmd;
    cmd.command = command;
673
    cmd.flags = flags;
hjk's avatar
hjk committed
674 675 676 677 678 679
    cmd.adapterCallback = callback;
    cmd.callbackName = callbackName;
    cmd.cookie = cookie;
    postCommandHelper(cmd);
}

680
void GdbEngine::postCommand(const QString &command, GdbCommandCallback callback,
681 682
                            const char *callbackName, const QVariant &cookie)
{
683
    postCommand(command, NoFlags, callback, callbackName, cookie);
684 685
}

686
void GdbEngine::postCommand(const QString &command, GdbCommandFlags flags,
687 688
                            GdbCommandCallback callback, const char *callbackName,
                            const QVariant &cookie)
hjk's avatar
hjk committed
689 690 691 692 693 694 695 696 697 698 699
{
    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
700
{
hjk's avatar
hjk committed
701
    if (!stateAcceptsGdbCommands(state())) {
hjk's avatar
hjk committed
702
        PENDING_DEBUG(_("NO GDB PROCESS RUNNING, CMD IGNORED: ") + cmd.command);
703 704
        debugMessage(_("NO GDB PROCESS RUNNING, CMD IGNORED: %1 %2")
            .arg(cmd.command).arg(state()));
con's avatar
con committed
705 706 707
        return;
    }

hjk's avatar
hjk committed
708
    if (cmd.flags & RebuildModel) {
con's avatar
con committed
709
        ++m_pendingRequests;
710 711
        PENDING_DEBUG("   MODEL:" << cmd.command << "=>" << cmd.callbackName
                      << "INCREMENTS PENDING TO" << m_pendingRequests);
con's avatar
con committed
712
    } else {
713 714
        PENDING_DEBUG("   OTHER (IN):" << cmd.command << "=>" << cmd.callbackName
                      << "LEAVES PENDING AT" << m_pendingRequests);
con's avatar
con committed
715 716
    }

717
    if ((cmd.flags & NeedsStop) || !m_commandsToRunOnTemporaryBreak.isEmpty()) {
718 719
        if (state() == InferiorStopped
            || state() == InferiorStarting || state() == AdapterStarted) {
hjk's avatar
hjk committed
720 721 722 723 724 725
            // Can be safely sent now.
            flushCommand(cmd);
        } else {
            // Queue the commands that we cannot send at once.
            debugMessage(_("QUEUING COMMAND ") + cmd.command);
            m_commandsToRunOnTemporaryBreak.append(cmd);
726 727 728 729
            if (state() != InferiorStopping) {
                showStatusMessage(tr("Stopping temporarily."), 1000);
                interruptInferior(); // FIXME: race condition between gdb and kill()
            }
hjk's avatar
hjk committed
730
        }
hjk's avatar
hjk committed
731
    } else if (!cmd.command.isEmpty()) {
732
        flushCommand(cmd);
con's avatar
con committed
733 734 735
    }
}

hjk's avatar
hjk committed
736
void GdbEngine::flushCommand(const GdbCommand &cmd0)
737
{
hjk's avatar
hjk committed
738
    GdbCommand cmd = cmd0;
hjk's avatar
hjk committed
739
    if (state() == DebuggerNotReady) {
740
        gdbInputAvailable(LogInput, cmd.command);
741 742 743 744
        debugMessage(_("GDB PROCESS NOT RUNNING, PLAIN CMD IGNORED: ") + cmd.command);
        return;
    }

745
    ++currentToken();
746
    cmd.postTime = QTime::currentTime();
747 748
    m_cookieForToken[currentToken()] = cmd;
    cmd.command = QString::number(currentToken()) + cmd.command;
749
    if (cmd.flags & EmbedToken)
750
        cmd.command = cmd.command.arg(currentToken());
751
    gdbInputAvailable(LogInput, cmd.command);
752 753

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

756
void GdbEngine::handleResultRecord(GdbResponse *response)
con's avatar
con committed
757
{
hjk's avatar
hjk committed
758
    //qDebug() << "TOKEN:" << response.token
759
    //    << " ACCEPTABLE:" << m_oldestAcceptableToken;
hjk's avatar
hjk committed
760
    //qDebug() << "\nRESULT" << response.token << response.toString();
con's avatar
con committed
761

762
    int token = response->token;
con's avatar
con committed
763 764 765
    if (token == -1)
        return;

766
    if (!m_cookieForToken.contains(token)) {
767 768 769 770
        // 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.
771 772
        debugMessage(_("COOKIE FOR TOKEN %1 ALREADY EATEN. "
            "TWO RESPONSES FOR ONE COMMAND?").arg(token));
773 774
        if (response->resultClass == GdbResultError) {
            QByteArray msg = response->data.findChild("msg").data();
775 776 777 778 779 780 781
            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."));
782
                shutdown();
783 784 785
            } 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.
786 787
                setState(InferiorStopping);
                setState(InferiorStopped);
788 789 790 791 792 793 794 795 796 797 798
            } 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)));
799
            }
800 801 802 803
        }
        return;
    }

804
    GdbCommand cmd = m_cookieForToken.take(token);
805
    if (theDebuggerBoolSetting(LogTimeStamps)) {
806
        gdbOutputAvailable(LogTime, _("Response time: %1: %2 s")
807 808 809
            .arg(cmd.command)
            .arg(cmd.postTime.msecsTo(QTime::currentTime()) / 1000.));
    }
con's avatar
con committed
810

811
    if (response->token < m_oldestAcceptableToken && (cmd.flags & Discardable)) {
812
        //debugMessage(_("### SKIPPING OLD RESULT") + response.toString());
con's avatar
con committed
813 814 815
        return;
    }

816
    response->cookie = cmd.cookie;
hjk's avatar
hjk committed
817

818 819 820 821 822
    if (response->resultClass != GdbResultError &&
        response->resultClass != ((cmd.flags & RunRequest) ? GdbResultRunning :
                                  (cmd.flags & ExitRequest) ? GdbResultExit :
                                  GdbResultDone)) {
        QString rsp = _(GdbResponse::stringFromResultClass(response->resultClass));
823 824
        qWarning() << "UNEXPECTED RESPONSE " << rsp << " TO COMMAND" << cmd.command << " AT " __FILE__ ":" STRINGIFY(__LINE__);
        debugMessage(_("UNEXPECTED RESPONSE %1 TO COMMAND %2").arg(rsp).arg(cmd.command));
825 826
    } else {
        if (cmd.callback)
827
            (this->*cmd.callback)(*response);
828
        else if (cmd.adapterCallback)
829
            (m_gdbAdapter->*cmd.adapterCallback)(*response);
830
    }
con's avatar
con committed
831

832
    if (cmd.flags & RebuildModel) {
con's avatar
con committed
833
        --m_pendingRequests;
834 835
        PENDING_DEBUG("   WATCH" << cmd.command << "=>" << cmd.callbackName
                      << "DECREMENTS PENDING TO" << m_pendingRequests);
836
        if (m_pendingRequests <= 0) {
837
            PENDING_DEBUG("\n\n ... AND TRIGGERS MODEL UPDATE\n");
hjk's avatar
hjk committed
838
            rebuildModel();
839
        }
con's avatar
con committed
840
    } else {
841 842
        PENDING_DEBUG("   OTHER (OUT):" << cmd.command << "=>" << cmd.callbackName
                      << "LEAVES PENDING AT" << m_pendingRequests);
con's avatar
con committed
843
    }
844

845 846
    // Continue only if there are no commands wire anymore, so this will
    // be fully synchroneous.
847 848 849 850
    // 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.