gdbengine.cpp 152 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"
con's avatar
con committed
49 50 51 52 53 54 55
#include "gdbmi.h"

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

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

hjk's avatar
hjk committed
60
#include <utils/qtcassert.h>
61
#include <texteditor/itexteditor.h>
62
#include <coreplugin/icore.h>
hjk's avatar
hjk committed
63

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

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

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
83
#ifdef Q_OS_UNIX
con's avatar
con committed
84 85 86
#include <unistd.h>
#include <dlfcn.h>
#endif
hjk's avatar
hjk committed
87
#include <ctype.h>
con's avatar
con committed
88

hjk's avatar
hjk committed
89
// FIXME: temporary hack to evalute tbreak based step-over behaviour
hjk's avatar
hjk committed
90 91 92
static QString lastFile;
static int lastLine;

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 104
#endif

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
105 106
#define STRINGIFY_INTERNAL(x) #x
#define STRINGIFY(x) STRINGIFY_INTERNAL(x)
107 108
#define CB(callback) &GdbEngine::callback, STRINGIFY(callback)

hjk's avatar
hjk committed
109 110 111 112 113 114 115 116 117 118 119 120
static bool stateAcceptsGdbCommands(GdbAdapterState state)
{
    return state == AdapterStarted
        || state == InferiorPreparing
        || state == InferiorPrepared
        || state == InferiorStarting
        || state == InferiorStarted
        || state == InferiorShuttingDown
        || state == InferiorShutDown
        || state == AdapterShuttingDown;
};

con's avatar
con committed
121 122 123 124 125 126
static int &currentToken()
{
    static int token = 0;
    return token;
}

127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
// reads a MI-encoded item frome the consolestream
static bool parseConsoleStream(const GdbResultRecord &record, GdbMi *contents)
{
    GdbMi output = record.data.findChild("consolestreamoutput");
    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();
}

static QByteArray parsePlainConsoleStream(const GdbResultRecord &record)
{
    GdbMi output = record.data.findChild("consolestreamoutput");
    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
163 164 165 166 167 168
///////////////////////////////////////////////////////////////////////
//
// GdbEngine
//
///////////////////////////////////////////////////////////////////////

hjk's avatar
hjk committed
169
GdbEngine::GdbEngine(DebuggerManager *parent) :
170
#ifdef Q_OS_WIN // Do injection loading with MinGW (call loading does not work with 64bit)
171
    m_dumperInjectionLoad(true),
172
#else
173
    m_dumperInjectionLoad(false),
174
#endif
hjk's avatar
hjk committed
175
    m_manager(parent),
176
    qq(parent->engineInterface())
con's avatar
con committed
177
{
hjk's avatar
hjk committed
178
    m_gdbAdapter = 0;
179 180 181 182
    QSharedPointer<TrkOptions> options(new TrkOptions);
    options->fromSettings(Core::ICore::instance()->settings());
    m_plainAdapter = new PlainGdbAdapter(this);
    m_trkAdapter = new TrkGdbAdapter(this, options);
183 184
    m_remoteAdapter = new RemoteGdbAdapter(this);
    m_coreAdapter = new CoreGdbAdapter(this);
185
    m_attachAdapter = new AttachGdbAdapter(this);
186 187 188 189 190 191 192 193 194 195 196 197 198 199

    // Output
    connect(&m_outputCollector, SIGNAL(byteDelivery(QByteArray)),
        this, SLOT(readDebugeeOutput(QByteArray)));

    connect(this, SIGNAL(gdbOutputAvailable(int,QString)),
        m_manager, SLOT(showDebuggerOutput(int,QString)),
        Qt::QueuedConnection);
    connect(this, SIGNAL(gdbInputAvailable(int,QString)),
        m_manager, SLOT(showDebuggerInput(int,QString)),
        Qt::QueuedConnection);
    connect(this, SIGNAL(applicationOutputAvailable(QString)),
        m_manager, SLOT(showApplicationOutput(QString)),
        Qt::QueuedConnection);
200
}
201

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

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

GdbEngine::~GdbEngine()
{
hjk's avatar
hjk committed
227
    // prevent sending error messages afterwards
hjk's avatar
hjk committed
228 229
    if (m_gdbAdapter) {
        m_gdbAdapter->disconnect(this);
230
        //delete m_gdbAdapter;
hjk's avatar
hjk committed
231 232
        m_gdbAdapter = 0;
    }
233 234 235
    delete m_plainAdapter;
    delete m_trkAdapter;
    delete m_remoteAdapter;
236
    delete m_coreAdapter;
237
    delete m_attachAdapter;
hjk's avatar
hjk committed
238 239
}

240
void GdbEngine::connectAdapter()
con's avatar
con committed
241 242
{
    // Gdb Process interaction
hjk's avatar
hjk committed
243
    connect(m_gdbAdapter, SIGNAL(error(QProcess::ProcessError)),
244
        this, SLOT(gdbProcError(QProcess::ProcessError)));
hjk's avatar
hjk committed
245
    connect(m_gdbAdapter, SIGNAL(readyReadStandardOutput()),
246
        this, SLOT(readGdbStandardOutput()));
hjk's avatar
hjk committed
247
    connect(m_gdbAdapter, SIGNAL(readyReadStandardError()),
248
        this, SLOT(readGdbStandardError()));
hjk's avatar
hjk committed
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273

    connect(m_gdbAdapter, SIGNAL(adapterStarted()),
        this, SLOT(handleAdapterStarted()));
    connect(m_gdbAdapter, SIGNAL(adapterStartFailed(QString)),
        this, SLOT(handleAdapterStartFailed(QString)));
    connect(m_gdbAdapter, SIGNAL(adapterShutDown()),
        this, SLOT(handleAdapterShutDown()));
    connect(m_gdbAdapter, SIGNAL(adapterShutdownFailed(QString)),
        this, SLOT(handleAdapterShutdownFailed(QString)));

    connect(m_gdbAdapter, SIGNAL(inferiorPrepared()),
        this, SLOT(handleInferiorPrepared()));
    connect(m_gdbAdapter, SIGNAL(inferiorPreparationFailed(QString)),
        this, SLOT(handleInferiorPreparationFailed(QString)));

    connect(m_gdbAdapter, SIGNAL(inferiorStarted()),
        this, SLOT(handleInferiorStarted()));
    connect(m_gdbAdapter, SIGNAL(inferiorStartFailed(QString)),
        this, SLOT(handleInferiorStartFailed(QString)));
    connect(m_gdbAdapter, SIGNAL(inferiorShutDown()),
        this, SLOT(handleInferiorShutDown()));
    connect(m_gdbAdapter, SIGNAL(inferiorShutdownFailed(QString)),
        this, SLOT(handleInferiorShutdownFailed(QString)));

    connect(m_gdbAdapter, SIGNAL(adapterCrashed()),
hjk's avatar
hjk committed
274
        m_manager, SLOT(exitDebugger()));
275
}
276

con's avatar
con committed
277

278 279 280 281 282 283 284 285 286
void GdbEngine::disconnectAdapter()
{
    // Gdb Process interaction
    disconnect(m_gdbAdapter, SIGNAL(error(QProcess::ProcessError)),
        this, SLOT(gdbProcError(QProcess::ProcessError)));
    disconnect(m_gdbAdapter, SIGNAL(readyReadStandardOutput()),
        this, SLOT(readGdbStandardOutput()));
    disconnect(m_gdbAdapter, SIGNAL(readyReadStandardError()),
        this, SLOT(readGdbStandardError()));
287

288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
    disconnect(m_gdbAdapter, SIGNAL(adapterStarted()),
        this, SLOT(handleAdapterStarted()));
    disconnect(m_gdbAdapter, SIGNAL(adapterStartFailed(QString)),
        this, SLOT(handleAdapterStartFailed(QString)));
    disconnect(m_gdbAdapter, SIGNAL(adapterShutDown()),
        this, SLOT(handleAdapterShutDown()));
    disconnect(m_gdbAdapter, SIGNAL(adapterShutdownFailed(QString)),
        this, SLOT(handleAdapterShutdownFailed(QString)));

    disconnect(m_gdbAdapter, SIGNAL(inferiorPrepared()),
        this, SLOT(handleInferiorPrepared()));
    disconnect(m_gdbAdapter, SIGNAL(inferiorPreparationFailed(QString)),
        this, SLOT(handleInferiorPreparationFailed(QString)));

    disconnect(m_gdbAdapter, SIGNAL(inferiorStarted()),
        this, SLOT(handleInferiorStarted()));
    disconnect(m_gdbAdapter, SIGNAL(inferiorStartFailed(QString)),
        this, SLOT(handleInferiorStartFailed(QString)));
    disconnect(m_gdbAdapter, SIGNAL(inferiorShutDown()),
        this, SLOT(handleInferiorShutDown()));
    disconnect(m_gdbAdapter, SIGNAL(inferiorShutdownFailed(QString)),
        this, SLOT(handleInferiorShutdownFailed(QString)));

    disconnect(m_gdbAdapter, SIGNAL(adapterCrashed()),
        m_manager, SLOT(exitDebugger()));
con's avatar
con committed
313 314
}

315 316
void GdbEngine::initializeVariables()
{
317
    m_debuggingHelperState = DebuggingHelperUninitialized;
318
    m_gdbVersion = 100;
hjk's avatar
hjk committed
319
    m_gdbBuildVersion = -1;
320 321 322 323 324 325 326 327 328

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

    m_modulesListOutdated = true;
    m_oldestAcceptableToken = -1;
    m_outputCodec = QTextCodec::codecForLocale();
    m_pendingRequests = 0;
hjk's avatar
hjk committed
329
    m_continuationAfterDone = 0;
hjk's avatar
hjk committed
330
    m_commandsToRunOnTemporaryBreak.clear();
331
    m_cookieForToken.clear();
332 333 334 335 336 337 338 339 340 341
    m_customOutputForToken.clear();

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

    m_inbuffer.clear();

    m_currentFunctionArgs.clear();
    m_currentFrame.clear();
342
    m_dumperHelper.clear();
343 344 345

    // FIXME: unhandled:
    //m_outputCodecState = QTextCodec::ConverterState();
346
    //m_gdbAdapter;
347 348
}

con's avatar
con committed
349 350 351
void GdbEngine::gdbProcError(QProcess::ProcessError error)
{
    QString msg;
352
    bool kill = true;
con's avatar
con committed
353 354
    switch (error) {
        case QProcess::FailedToStart:
355
            //kill = false;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
356
            msg = tr("The Gdb process failed to start. Either the "
con's avatar
con committed
357
                "invoked program '%1' is missing, or you may have insufficient "
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
358
                "permissions to invoke the program.")
hjk's avatar
hjk committed
359
                .arg(theDebuggerStringSetting(GdbLocation));
360 361
            //emit startFailed();
            //shutdown();
con's avatar
con committed
362 363
            break;
        case QProcess::Crashed:
364
            kill = false;
con's avatar
con committed
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
            msg = tr("The Gdb process crashed some time after starting "
                "successfully.");
            break;
        case QProcess::Timedout:
            msg = tr("The last waitFor...() function timed out. "
                "The state of QProcess is unchanged, and you can try calling "
                "waitFor...() again.");
            break;
        case QProcess::WriteError:
            msg = tr("An error occurred when attempting to write "
                "to the Gdb process. For example, the process may not be running, "
                "or it may have closed its input channel.");
            break;
        case QProcess::ReadError:
            msg = tr("An error occurred when attempting to read from "
                "the Gdb process. For example, the process may not be running.");
            break;
        default:
            msg = tr("An unknown error in the Gdb process occurred. "
                "This is the default return value of error().");
    }

hjk's avatar
hjk committed
387
    showStatusMessage(msg);
388
    showMessageBox(QMessageBox::Critical, tr("Error"), msg);
con's avatar
con committed
389
    // act as if it was closed by the core
390
    //if (kill)
hjk's avatar
hjk committed
391
        m_manager->exitDebugger();
con's avatar
con committed
392 393 394 395 396 397
}

#if 0
static void dump(const char *first, const char *middle, const QString & to)
{
    QByteArray ba(first, middle - first);
398
    Q_UNUSED(to)
con's avatar
con committed
399 400 401 402 403 404 405 406 407 408 409
    // 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

410 411 412 413 414 415
void GdbEngine::readDebugeeOutput(const QByteArray &data)
{
    emit applicationOutputAvailable(m_outputCodec->toUnicode(
            data.constData(), data.length(), &m_outputCodecState));
}

hjk's avatar
hjk committed
416 417
void GdbEngine::debugMessage(const QString &msg)
{
418
    emit gdbOutputAvailable(LogDebug, msg);
hjk's avatar
hjk committed
419 420
}

421
void GdbEngine::handleResponse(const QByteArray &buff)
con's avatar
con committed
422 423 424
{
    static QTime lastTime;

425
    if (theDebuggerBoolSetting(LogTimeStamps))
426
        emit gdbOutputAvailable(LogTime, currentTime());
427
    emit gdbOutputAvailable(LogOutput, QString::fromLocal8Bit(buff, buff.length()));
con's avatar
con committed
428 429 430 431 432

#if 0
    qDebug() // << "#### start response handling #### "
        << currentTime()
        << lastTime.msecsTo(QTime::currentTime()) << "ms,"
433 434
        << "buf:" << buff.left(1500) << "..."
        //<< "buf:" << buff
435
        << "size:" << buff.size();
con's avatar
con committed
436
#else
437
    //qDebug() << "buf:" << buff;
con's avatar
con committed
438 439 440 441
#endif

    lastTime = QTime::currentTime();

442 443
    if (buff.isEmpty() || buff == "(gdb) ")
        return;
con's avatar
con committed
444

445 446 447
    const char *from = buff.constData();
    const char *to = from + buff.size();
    const char *inner;
con's avatar
con committed
448

449 450 451 452
    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
453
            break;
454 455 456
    if (from != inner) {
        token = QByteArray(from, inner - from).toInt();
        from = inner;
457
        //qDebug() << "found token" << token;
458
    }
con's avatar
con committed
459

460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
    // next char decides kind of record
    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
475

476 477 478
            GdbMi record;
            while (from != to) {
                GdbMi data;
hjk's avatar
hjk committed
479
                if (*from != ',') {
hjk's avatar
hjk committed
480 481
                    // happens on archer where we get 
                    // 23^running <NL> *running,thread-id="all" <NL> (gdb) 
482
                    record.m_type = GdbMi::Tuple;
hjk's avatar
hjk committed
483 484 485 486 487
                    break;
                }
                ++from; // skip ','
                data.parseResultOrValue(from, to);
                if (data.isValid()) {
488
                    //qDebug() << "parsed response:" << data.toString();
hjk's avatar
hjk committed
489 490
                    record.m_children += data;
                    record.m_type = GdbMi::Tuple;
con's avatar
con committed
491 492
                }
            }
493 494 495 496
            if (asyncClass == "stopped") {
                handleAsyncOutput(record);
            } else if (asyncClass == "running") {
                // Archer has 'thread-id="all"' here
497 498 499 500
            } 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",
501
                // symbols-loaded="0"
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
502
                QByteArray id = record.findChild("id").data();
503
                if (!id.isEmpty())
hjk's avatar
hjk committed
504
                    showStatusMessage(tr("Library %1 loaded.").arg(_(id)));
hjk's avatar
hjk committed
505 506 507 508
            } 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"
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
509
                QByteArray id = record.findChild("id").data();
hjk's avatar
hjk committed
510
                showStatusMessage(tr("Library %1 unloaded.").arg(_(id)));
511 512
            } else if (asyncClass == "thread-group-created") {
                // Archer has "{id="28902"}" 
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
513
                QByteArray id = record.findChild("id").data();
hjk's avatar
hjk committed
514
                showStatusMessage(tr("Thread group %1 created.").arg(_(id)));
515 516
            } else if (asyncClass == "thread-created") {
                //"{id="1",group-id="28902"}" 
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
517
                QByteArray id = record.findChild("id").data();
hjk's avatar
hjk committed
518
                showStatusMessage(tr("Thread %1 created.").arg(_(id)));
519 520
            } else if (asyncClass == "thread-group-exited") {
                // Archer has "{id="28902"}" 
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
521
                QByteArray id = record.findChild("id").data();
hjk's avatar
hjk committed
522
                showStatusMessage(tr("Thread group %1 exited.").arg(_(id)));
523 524
            } else if (asyncClass == "thread-exited") {
                //"{id="1",group-id="28902"}" 
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
525 526
                QByteArray id = record.findChild("id").data();
                QByteArray groupid = record.findChild("group-id").data();
hjk's avatar
hjk committed
527
                showStatusMessage(tr("Thread %1 in group %2 exited.")
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
528
                    .arg(_(id)).arg(_(groupid)));
hjk's avatar
hjk committed
529
            } else if (asyncClass == "thread-selected") {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
530
                QByteArray id = record.findChild("id").data();
hjk's avatar
hjk committed
531
                showStatusMessage(tr("Thread %1 selected.").arg(_(id)));
hjk's avatar
hjk committed
532
                //"{id="2"}" 
533
            #if defined(Q_OS_MAC)
534 535 536 537 538 539 540 541 542 543 544
            } 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 {
545
                qDebug() << "IGNORED ASYNC OUTPUT"
546
                    << asyncClass << record.toString();
547
            }
548 549
            break;
        }
550

551
        case '~': {
552 553
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingConsoleStreamOutput += data;
554 555 556 557 558 559 560 561 562 563 564 565

            // 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]*"));
                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));
566
            }
567 568

            // Show some messages to give the impression something happens.
hjk's avatar
hjk committed
569
            if (data.startsWith("Reading symbols from "))
570 571 572
                showStatusMessage(tr("Reading %1...").arg(_(data.mid(21))), 1000);
            if (data.startsWith("[New "))
                showStatusMessage(_(data), 1000);
573 574
            break;
        }
575

576
        case '@': {
577 578
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingTargetStreamOutput += data;
579
            break;
con's avatar
con committed
580 581
        }

582 583 584 585 586 587
        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:"))
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
588
                qq->showApplicationOutput(_(data.mid(9))); // cut "warning: "
con's avatar
con committed
589 590 591
            break;
        }

592 593
        case '^': {
            GdbResultRecord record;
con's avatar
con committed
594

595
            record.token = token;
con's avatar
con committed
596

597 598 599
            for (inner = from; inner != to; ++inner)
                if (*inner < 'a' || *inner > 'z')
                    break;
con's avatar
con committed
600

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
601
            QByteArray resultClass = QByteArray::fromRawData(from, inner - from);
602 603 604 605 606 607 608 609 610 611 612 613
            if (resultClass == "done")
                record.resultClass = GdbResultDone;
            else if (resultClass == "running")
                record.resultClass = GdbResultRunning;
            else if (resultClass == "connected")
                record.resultClass = GdbResultConnected;
            else if (resultClass == "error")
                record.resultClass = GdbResultError;
            else if (resultClass == "exit")
                record.resultClass = GdbResultExit;
            else
                record.resultClass = GdbResultUnknown;
con's avatar
con committed
614

615 616
            from = inner;
            if (from != to) {
hjk's avatar
hjk committed
617 618 619 620 621 622 623 624 625
                if (*from == ',') {
                    ++from;
                    record.data.parseTuple_helper(from, to);
                    record.data.m_type = GdbMi::Tuple;
                    record.data.m_name = "data";
                } else {
                    // Archer has this
                    record.data.m_type = GdbMi::Tuple;
                    record.data.m_name = "data";
con's avatar
con committed
626 627
                }
            }
628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652

            //qDebug() << "\nLOG STREAM:" + m_pendingLogStreamOutput;
            //qDebug() << "\nTARGET STREAM:" + m_pendingTargetStreamOutput;
            //qDebug() << "\nCONSOLE STREAM:" + m_pendingConsoleStreamOutput;
            record.data.setStreamOutput("logstreamoutput",
                m_pendingLogStreamOutput);
            record.data.setStreamOutput("targetstreamoutput",
                m_pendingTargetStreamOutput);
            record.data.setStreamOutput("consolestreamoutput",
                m_pendingConsoleStreamOutput);
            QByteArray custom = m_customOutputForToken[token];
            if (!custom.isEmpty())
                record.data.setStreamOutput("customvaluecontents",
                    '{' + custom + '}');
            //m_customOutputForToken.remove(token);
            m_pendingLogStreamOutput.clear();
            m_pendingTargetStreamOutput.clear();
            m_pendingConsoleStreamOutput.clear();

            handleResultRecord(record);
            break;
        }
        default: {
            qDebug() << "UNKNOWN RESPONSE TYPE" << c;
            break;
con's avatar
con committed
653 654 655 656 657 658
        }
    }
}

void GdbEngine::readGdbStandardError()
{
hjk's avatar
hjk committed
659
    qWarning() << "Unexpected gdb stderr:" << m_gdbAdapter->readAllStandardError();
con's avatar
con committed
660 661 662 663
}

void GdbEngine::readGdbStandardOutput()
{
664 665
    int newstart = 0;
    int scan = m_inbuffer.size();
con's avatar
con committed
666

hjk's avatar
hjk committed
667
    m_inbuffer.append(m_gdbAdapter->readAllStandardOutput());
con's avatar
con committed
668

669 670
    while (newstart < m_inbuffer.size()) {
        int start = newstart;
671
        int end = m_inbuffer.indexOf('\n', scan);
672 673 674 675 676
        if (end < 0) {
            m_inbuffer.remove(0, start);
            return;
        }
        newstart = end + 1;
677
        scan = newstart;
678 679
        if (end == start)
            continue;
680
        #if defined(Q_OS_WIN)
681 682 683 684 685
        if (m_inbuffer.at(end - 1) == '\r') {
            --end;
            if (end == start)
                continue;
        }
686
        #endif
687
        handleResponse(QByteArray::fromRawData(m_inbuffer.constData() + start, end - start));
con's avatar
con committed
688
    }
689
    m_inbuffer.clear();
con's avatar
con committed
690 691 692 693
}

void GdbEngine::interruptInferior()
{
694
//    debugMessage(_("GDBENGINE INTERRUPT INFERIOR: %1").arg(m_gdbAdapter->state()));
hjk's avatar
hjk committed
695
    qq->notifyInferiorStopRequested();
696

697
    if (m_gdbAdapter->state() == AdapterNotRunning) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
698
        debugMessage(_("TRYING TO INTERRUPT INFERIOR WITHOUT RUNNING GDB"));
hjk's avatar
hjk committed
699
        qq->notifyInferiorExited();
con's avatar
con committed
700
        return;
hjk's avatar
hjk committed
701
    }
con's avatar
con committed
702

703
    debugMessage(_("TRYING TO INTERUPT INFERIOR"));
hjk's avatar
hjk committed
704
    m_gdbAdapter->interruptInferior();
con's avatar
con committed
705 706 707 708
}

void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
{
709
    const qint64 pid = pid0.toLongLong();
con's avatar
con committed
710
    if (pid == 0) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
711
        debugMessage(_("Cannot parse PID from %1").arg(pid0));
con's avatar
con committed
712 713
        return;
    }
hjk's avatar
hjk committed
714
    if (pid == inferiorPid())
con's avatar
con committed
715
        return;
716 717
    debugMessage(_("FOUND PID %1").arg(pid));    

Roberto Raggi's avatar
Roberto Raggi committed
718
    qq->notifyInferiorPidChanged(pid);
719 720
    if (m_dumperInjectionLoad)
        tryLoadDebuggingHelpers();
con's avatar
con committed
721 722
}

hjk's avatar
hjk committed
723 724
void GdbEngine::postCommand(const QString &command, AdapterCallback callback,
                            const char *callbackName, const QVariant &cookie)
725 726 727 728 729 730 731
{
    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
732 733 734
{
    GdbCommand cmd;
    cmd.command = command;
735
    cmd.flags = flags;
hjk's avatar
hjk committed
736 737 738 739 740 741
    cmd.adapterCallback = callback;
    cmd.callbackName = callbackName;
    cmd.cookie = cookie;
    postCommandHelper(cmd);
}

742
void GdbEngine::postCommand(const QString &command, GdbCommandCallback callback,
743 744
                            const char *callbackName, const QVariant &cookie)
{
745
    postCommand(command, NoFlags, callback, callbackName, cookie);
746 747
}

748
void GdbEngine::postCommand(const QString &command, GdbCommandFlags flags,
749 750
                            GdbCommandCallback callback, const char *callbackName,
                            const QVariant &cookie)
hjk's avatar
hjk committed
751 752 753 754 755 756 757 758 759 760 761
{
    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
762
{
hjk's avatar
hjk committed
763 764
    if (!stateAcceptsGdbCommands(m_gdbAdapter->state())) {
        PENDING_DEBUG(_("NO GDB PROCESS RUNNING, CMD IGNORED: ") + cmd.command);
hjk's avatar
hjk committed
765
        debugMessage(_("NO GDB PROCESS RUNNING, CMD IGNORED: ") + cmd.command);
con's avatar
con committed
766 767 768
        return;
    }

hjk's avatar
hjk committed
769
    if (cmd.flags & RebuildModel) {
con's avatar
con committed
770
        ++m_pendingRequests;
hjk's avatar
hjk committed
771
        PENDING_DEBUG("   CALLBACK" << cmd.callbackName
hjk's avatar
hjk committed
772 773
            << "INCREMENTS PENDING TO:" << m_pendingRequests << cmd.command
            << m_gdbAdapter->state());
con's avatar
con committed
774
    } else {
hjk's avatar
hjk committed
775
        PENDING_DEBUG("   UNKNOWN CALLBACK" << cmd.callbackName
hjk's avatar
hjk committed
776 777
            << "LEAVES PENDING AT:" << m_pendingRequests << cmd.command
            << m_gdbAdapter->state());
con's avatar
con committed
778 779
    }

hjk's avatar
hjk committed
780
    if ((cmd.flags & NeedsStop) && status() != DebuggerInferiorStopped
hjk's avatar
hjk committed
781
            && status() != DebuggerProcessStartingUp) {
hjk's avatar
hjk committed
782
        // queue the commands that we cannot send at once
hjk's avatar
hjk committed
783 784 785
        QTC_ASSERT(status() == DebuggerInferiorRunning,
            qDebug() << "STATUS:" << status());
        showStatusMessage(tr("Stopping temporarily."));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
786
        debugMessage(_("QUEUING COMMAND ") + cmd.command);
hjk's avatar
hjk committed
787 788
        m_commandsToRunOnTemporaryBreak.append(cmd);
        interruptInferior();
hjk's avatar
hjk committed
789
    } else if (!cmd.command.isEmpty()) {
790
        flushCommand(cmd);
con's avatar
con committed
791 792 793
    }
}

hjk's avatar
hjk committed
794
void GdbEngine::flushCommand(const GdbCommand &cmd0)
795
{
hjk's avatar
hjk committed
796
    GdbCommand cmd = cmd0;
797
    if (m_gdbAdapter->state() == AdapterNotRunning) {
798 799 800 801 802
        emit gdbInputAvailable(LogInput, cmd.command);
        debugMessage(_("GDB PROCESS NOT RUNNING, PLAIN CMD IGNORED: ") + cmd.command);
        return;
    }

803
    ++currentToken();
804
    cmd.postTime = QTime::currentTime();
805 806
    m_cookieForToken[currentToken()] = cmd;
    cmd.command = QString::number(currentToken()) + cmd.command;
807
    if (cmd.flags & EmbedToken)
808
        cmd.command = cmd.command.arg(currentToken());
809
    emit gdbInputAvailable(LogInput, cmd.command);
810 811

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

con's avatar
con committed
814 815
void GdbEngine::handleResultRecord(const GdbResultRecord &record)
{
816 817
    //qDebug() << "TOKEN:" << record.token
    //    << " ACCEPTABLE:" << m_oldestAcceptableToken;
con's avatar
con committed
818 819 820 821 822 823
    //qDebug() << "\nRESULT" << record.token << record.toString();

    int token = record.token;
    if (token == -1)
        return;

824 825 826 827
    if (!m_cookieForToken.contains(token)) {
        // In theory this should not happen, in practice it does.
        debugMessage(_("COOKIE FOR TOKEN %1 ALREADY EATEN. "
            "TWO RESPONSES FOR ONE COMMAND?").arg(token));
hjk's avatar
hjk committed
828
        // Handle a case known to occur on Linux/gdb 6.8 when debugging moc
829 830 831 832
        // with helpers enabled. In this case we get a second response with
        // msg="Cannot find new threads: generic error"
        if (record.resultClass == GdbResultError) {
            QByteArray msg = record.data.findChild("msg").data();
833 834
            showMessageBox(QMessageBox::Critical,
                tr("Executable failed"), QString::fromLocal8Bit(msg));
hjk's avatar
hjk committed
835
            showStatusMessage(tr("Process failed to start."));
836 837 838 839 840
            exitDebugger();
        }
        return;
    }

841
    GdbCommand cmd = m_cookieForToken.take(token);
842 843 844 845 846
    if (theDebuggerBoolSetting(LogTimeStamps)) {
        emit gdbOutputAvailable(LogTime, _("Response time: %1: %2 s")
            .arg(cmd.command)
            .arg(cmd.postTime.msecsTo(QTime::currentTime()) / 1000.));
    }
con's avatar
con committed
847

848
    if (record.token < m_oldestAcceptableToken && (cmd.flags & Discardable)) {
849
        //qDebug() << "### SKIPPING OLD RESULT" << record.toString();
850
        //showMessageBox(QMessageBox::Information(tr("Skipped"), "xxx"));
con's avatar
con committed
851 852 853 854
        return;
    }

#if 0
855
    qDebug() << "# handleOutput,"
hjk's avatar
hjk committed
856
        << "cmd name:" << cmd.callbackName
857
        << " cmd synchronized:" << cmd.synchronized
con's avatar
con committed
858 859 860
        << "\n record: " << record.toString();
#endif

861
    if (cmd.callback)
hjk's avatar
hjk committed
862 863 864
        (this->*cmd.callback)(record, cmd.cookie);
    if (cmd.adapterCallback)
        (m_gdbAdapter->*cmd.adapterCallback)(record, cmd.cookie);
con's avatar
con committed
865

866
    if (cmd.flags & RebuildModel) {
con's avatar
con committed
867
        --m_pendingRequests;
hjk's avatar
hjk committed
868
        PENDING_DEBUG("   TYPE " << cmd.callbackName << " DECREMENTS PENDING TO: "
con's avatar
con committed
869
            << m_pendingRequests << cmd.command);
870
        if (m_pendingRequests <= 0) {
hjk's avatar
hjk committed
871 872
            PENDING_DEBUG("\n\n ....  AND TRIGGERS MODEL UPDATE\n");
            rebuildModel();
873
        }
con's avatar
con committed
874
    } else {
hjk's avatar
hjk committed
875
        PENDING_DEBUG("   UNKNOWN TYPE " << cmd.callbackName << " LEAVES PENDING AT: "
con's avatar
con committed
876 877
            << m_pendingRequests << cmd.command);
    }
878

879 880
    // Continue only if there are no commands wire anymore, so this will
    // be fully synchroneous.
881 882 883 884
    // 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.
hjk's avatar
hjk committed
885 886 887 888 889 890 891
    if (m_continuationAfterDone && m_cookieForToken.isEmpty()) {
        Continuation cont = m_continuationAfterDone;
        m_continuationAfterDone = 0;
        (this->*cont)();
        //showStatusMessage(tr("Continuing after temporary stop."));
    } else {
        PENDING_DEBUG("MISSING TOKENS: " << m_cookieForToken.keys());
892
    }
con's avatar
con committed
893 894 895 896
}

void GdbEngine::executeDebuggerCommand(const QString &command)
{
897
    if (m_gdbAdapter->state() == AdapterNotRunning) {
898
        debugMessage(_("GDB PROCESS NOT RUNNING, PLAIN CMD IGNORED: ") + command);
con's avatar
con committed
899 900 901
        return;
    }

hjk's avatar
hjk committed
902
    m_gdbAdapter->write(command.toLatin1() + "\r\n");
con's avatar
con committed
903 904
}

hjk's avatar
hjk committed
905
// called from CoreAdapter and AttachAdapter
906
void GdbEngine::updateAll()
907 908
{
    qq->notifyInferiorStopped();
hjk's avatar
hjk committed
909 910
    showStatusMessage(tr("Core file loaded."));
    m_manager->resetLocation();
911
    tryLoadDebuggingHelpers();
912
    qq->stackHandler()->setCurrentIndex(0);
hjk's avatar
hjk committed
913
    updateLocals(); 
914
    reloadStack();
915
    if (supportsThreads())
916
        postCommand(_("-thread-list-ids"), WatchUpdate, CB(handleStackListThreads), 0);
917
    qq->reloadRegisters();
918 919
}

920
void GdbEngine::handleQuerySources(const GdbResultRecord &record, const QVariant &)
con's avatar
con committed
921 922
{
    if (record.resultClass == GdbResultDone) {
923
        QMap<QString, QString> oldShortToFull = m_shortToFullName;
con's avatar
con committed
924 925 926 927 928 929
        m_shortToFullName.clear();
        m_fullToShortName.clear();
        // "^done,files=[{file="../../../../bin/gdbmacros/gdbmacros.cpp",
        // fullname="/data5/dev/ide/main/bin/gdbmacros/gdbmacros.cpp"},
        GdbMi files = record.data.findChild("files");
        foreach (const GdbMi &item, files.children()) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
930
            QString fileName = QString::fromLocal8Bit(item.findChild("file").data());
con's avatar
con committed
931
            GdbMi fullName = item.findChild("fullname");
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
932
            QString full = QString::fromLocal8Bit(fullName.data());
con's avatar
con committed
933 934 935 936 937 938 939 940
            #ifdef Q_OS_WIN
            full = QDir::cleanPath(full);
            #endif
            if (fullName.isValid() && QFileInfo(full).isReadable()) {
                m_shortToFullName[fileName] = full;
                m_fullToShortName[full] = fileName;
            }
        }
941 942
        if (m_shortToFullName != oldShortToFull)
            qq->sourceFileWindow()->setSourceFiles(m_shortToFullName);
con's avatar
con committed
943 944 945
    }
}

946
void GdbEngine::handleInfoThreads(const GdbResultRecord &record, const QVariant &)
947
{
948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964
    if (record.resultClass != GdbResultDone)
        return;