gdbengine.cpp 141 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
26
** contact the sales department at http://www.qtsoftware.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"
con's avatar
con committed
34

35
#include "watchutils.h"
36
#include "debuggeractions.h"
con's avatar
con committed
37 38
#include "debuggerconstants.h"
#include "debuggermanager.h"
39
#include "debuggertooltip.h"
con's avatar
con committed
40 41 42 43 44 45 46 47 48
#include "gdbmi.h"
#include "procinterrupt.h"

#include "disassemblerhandler.h"
#include "breakhandler.h"
#include "moduleshandler.h"
#include "registerhandler.h"
#include "stackhandler.h"
#include "watchhandler.h"
49
#include "sourcefileswindow.h"
con's avatar
con committed
50

51
#include "debuggerdialogs.h"
con's avatar
con committed
52

hjk's avatar
hjk committed
53
#include <utils/qtcassert.h>
54
#include <texteditor/itexteditor.h>
55
#include <coreplugin/icore.h>
hjk's avatar
hjk committed
56

con's avatar
con committed
57 58 59 60 61
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QTime>
#include <QtCore/QTimer>
62
#include <QtCore/QTextStream>
con's avatar
con committed
63 64

#include <QtGui/QAction>
65
#include <QtGui/QApplication>
con's avatar
con committed
66 67 68
#include <QtGui/QLabel>
#include <QtGui/QMainWindow>
#include <QtGui/QMessageBox>
69 70
#include <QtGui/QDialogButtonBox>
#include <QtGui/QPushButton>
71
#ifdef Q_OS_WIN
72
#    include "shared/sharedlibraryinjector.h"
73
#endif
con's avatar
con committed
74

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
75
#ifdef Q_OS_UNIX
con's avatar
con committed
76 77 78
#include <unistd.h>
#include <dlfcn.h>
#endif
hjk's avatar
hjk committed
79
#include <ctype.h>
con's avatar
con committed
80 81 82 83 84 85 86 87 88 89 90 91 92

using namespace Debugger;
using namespace Debugger::Internal;
using namespace Debugger::Constants;

Q_DECLARE_METATYPE(Debugger::Internal::GdbMi);

//#define DEBUG_PENDING  1
//#define DEBUG_SUBITEM  1

#if DEBUG_PENDING
#   define PENDING_DEBUG(s) qDebug() << s
#else
Roberto Raggi's avatar
Roberto Raggi committed
93
#   define PENDING_DEBUG(s)
con's avatar
con committed
94 95
#endif

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
96 97
#define STRINGIFY_INTERNAL(x) #x
#define STRINGIFY(x) STRINGIFY_INTERNAL(x)
98 99
#define CB(callback) &GdbEngine::callback, STRINGIFY(callback)

con's avatar
con committed
100 101 102 103 104 105 106 107 108 109 110 111
static int &currentToken()
{
    static int token = 0;
    return token;
}

///////////////////////////////////////////////////////////////////////
//
// GdbEngine
//
///////////////////////////////////////////////////////////////////////

112 113 114 115 116 117
GdbEngine::GdbEngine(DebuggerManager *parent) :
#ifdef Q_OS_WIN // Do injection loading with MinGW (call loading does not work with 64bit)
    m_dumperInjectionLoad(true)
#else
    m_dumperInjectionLoad(false)
#endif
con's avatar
con committed
118 119 120
{
    q = parent;
    qq = parent->engineInterface();
121
    m_stubProc.setMode(Core::Utils::ConsoleProcess::Debug);
122 123 124
#ifdef Q_OS_UNIX
    m_stubProc.setSettings(Core::ICore::instance()->settings());
#endif
125 126
    initializeVariables();
    initializeConnections();
con's avatar
con committed
127 128 129 130
}

GdbEngine::~GdbEngine()
{
hjk's avatar
hjk committed
131 132
    // prevent sending error messages afterwards
    m_gdbProc.disconnect(this);
con's avatar
con committed
133 134
}

135
void GdbEngine::initializeConnections()
con's avatar
con committed
136 137
{
    // Gdb Process interaction
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
    connect(&m_gdbProc, SIGNAL(error(QProcess::ProcessError)),
        this, SLOT(gdbProcError(QProcess::ProcessError)));
    connect(&m_gdbProc, SIGNAL(readyReadStandardOutput()),
        this, SLOT(readGdbStandardOutput()));
    connect(&m_gdbProc, SIGNAL(readyReadStandardError()),
        this, SLOT(readGdbStandardError()));
    connect(&m_gdbProc, SIGNAL(finished(int, QProcess::ExitStatus)),
        q, SLOT(exitDebugger()));

    connect(&m_stubProc, SIGNAL(processError(QString)),
        this, SLOT(stubError(QString)));
    connect(&m_stubProc, SIGNAL(processStarted()),
        this, SLOT(stubStarted()));
    connect(&m_stubProc, SIGNAL(wrapperStopped()),
        q, SLOT(exitDebugger()));

    connect(&m_uploadProc, SIGNAL(error(QProcess::ProcessError)),
        this, SLOT(uploadProcError(QProcess::ProcessError)));
156 157 158 159
    connect(&m_uploadProc, SIGNAL(readyReadStandardOutput()),
        this, SLOT(readUploadStandardOutput()));
    connect(&m_uploadProc, SIGNAL(readyReadStandardError()),
        this, SLOT(readUploadStandardError()));
160

con's avatar
con committed
161
    // Output
162
    connect(&m_outputCollector, SIGNAL(byteDelivery(QByteArray)),
163
        this, SLOT(readDebugeeOutput(QByteArray)));
con's avatar
con committed
164

165 166
    connect(this, SIGNAL(gdbOutputAvailable(int,QString)),
        q, SLOT(showDebuggerOutput(int,QString)),
con's avatar
con committed
167
        Qt::QueuedConnection);
168 169
    connect(this, SIGNAL(gdbInputAvailable(int,QString)),
        q, SLOT(showDebuggerInput(int,QString)),
con's avatar
con committed
170
        Qt::QueuedConnection);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
171 172
    connect(this, SIGNAL(applicationOutputAvailable(QString)),
        q, SLOT(showApplicationOutput(QString)),
con's avatar
con committed
173
        Qt::QueuedConnection);
174

175
    // FIXME: These trigger even if the engine is not active
176 177 178 179 180 181
    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()));
con's avatar
con committed
182 183
}

184 185
void GdbEngine::initializeVariables()
{
186
    m_debuggingHelperState = DebuggingHelperUninitialized;
187
    m_gdbVersion = 100;
hjk's avatar
hjk committed
188
    m_gdbBuildVersion = -1;
189 190 191 192 193 194 195 196 197

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

    m_modulesListOutdated = true;
    m_oldestAcceptableToken = -1;
    m_outputCodec = QTextCodec::codecForLocale();
    m_pendingRequests = 0;
198
    m_autoContinue = false;
199
    m_waitingForFirstBreakpointToBeHit = false;
hjk's avatar
hjk committed
200
    m_commandsToRunOnTemporaryBreak.clear();
201
    m_cookieForToken.clear();
202 203
}

con's avatar
con committed
204 205 206
void GdbEngine::gdbProcError(QProcess::ProcessError error)
{
    QString msg;
207
    bool kill = true;
con's avatar
con committed
208 209
    switch (error) {
        case QProcess::FailedToStart:
210
            kill = false;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
211
            msg = tr("The Gdb process failed to start. Either the "
con's avatar
con committed
212
                "invoked program '%1' is missing, or you may have insufficient "
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
213
                "permissions to invoke the program.")
hjk's avatar
hjk committed
214
                .arg(theDebuggerStringSetting(GdbLocation));
con's avatar
con committed
215 216
            break;
        case QProcess::Crashed:
217
            kill = false;
con's avatar
con committed
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
            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().");
    }

240
    q->showStatusMessage(msg);
con's avatar
con committed
241 242
    QMessageBox::critical(q->mainWindow(), tr("Error"), msg);
    // act as if it was closed by the core
243 244
    if (kill)
        q->exitDebugger();
con's avatar
con committed
245 246
}

247 248 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 274 275 276 277 278 279 280 281 282 283
void GdbEngine::uploadProcError(QProcess::ProcessError error)
{
    QString msg;
    switch (error) {
        case QProcess::FailedToStart:
            msg = tr("The upload process failed to start. Either the "
                "invoked script '%1' is missing, or you may have insufficient "
                "permissions to invoke the program.")
                .arg(theDebuggerStringSetting(GdbLocation));
            break;
        case QProcess::Crashed:
            msg = tr("The upload 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 upload 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 upload process. For example, the process may not be running.");
            break;
        default:
            msg = tr("An unknown error in the upload process occurred. "
                "This is the default return value of error().");
    }

    q->showStatusMessage(msg);
    QMessageBox::critical(q->mainWindow(), tr("Error"), msg);
}

284 285 286
void GdbEngine::readUploadStandardOutput()
{
    QByteArray ba = m_uploadProc.readAllStandardOutput();
287
    gdbOutputAvailable(LogOutput, QString::fromLocal8Bit(ba, ba.length()));
288 289 290 291 292
}

void GdbEngine::readUploadStandardError()
{
    QByteArray ba = m_uploadProc.readAllStandardError();
293
    gdbOutputAvailable(LogError, QString::fromLocal8Bit(ba, ba.length()));
294 295
}

con's avatar
con committed
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
#if 0
static void dump(const char *first, const char *middle, const QString & to)
{
    QByteArray ba(first, middle - first);
    Q_UNUSED(to);
    // 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

312 313 314 315 316 317
void GdbEngine::readDebugeeOutput(const QByteArray &data)
{
    emit applicationOutputAvailable(m_outputCodec->toUnicode(
            data.constData(), data.length(), &m_outputCodecState));
}

hjk's avatar
hjk committed
318 319
void GdbEngine::debugMessage(const QString &msg)
{
320
    emit gdbOutputAvailable(LogDebug, msg);
hjk's avatar
hjk committed
321 322
}

323
void GdbEngine::handleResponse(const QByteArray &buff)
con's avatar
con committed
324 325 326
{
    static QTime lastTime;

327 328 329
    if (theDebuggerBoolSetting(LogTimeStamps))
        emit gdbOutputAvailable(LogDebug, currentTime());
    emit gdbOutputAvailable(LogOutput, QString::fromLocal8Bit(buff, buff.length()));
con's avatar
con committed
330 331 332 333 334

#if 0
    qDebug() // << "#### start response handling #### "
        << currentTime()
        << lastTime.msecsTo(QTime::currentTime()) << "ms,"
335 336
        << "buf:" << buff.left(1500) << "..."
        //<< "buf:" << buff
337
        << "size:" << buff.size();
con's avatar
con committed
338
#else
339
    //qDebug() << "buf:" << buff;
con's avatar
con committed
340 341 342 343
#endif

    lastTime = QTime::currentTime();

344 345
    if (buff.isEmpty() || buff == "(gdb) ")
        return;
con's avatar
con committed
346

347 348 349
    const char *from = buff.constData();
    const char *to = from + buff.size();
    const char *inner;
con's avatar
con committed
350

351 352 353 354
    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
355
            break;
356 357 358
    if (from != inner) {
        token = QByteArray(from, inner - from).toInt();
        from = inner;
359
        //qDebug() << "found token" << token;
360
    }
con's avatar
con committed
361

362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
    // 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
377

378 379 380
            GdbMi record;
            while (from != to) {
                GdbMi data;
hjk's avatar
hjk committed
381
                if (*from != ',') {
hjk's avatar
hjk committed
382 383
                    // happens on archer where we get 
                    // 23^running <NL> *running,thread-id="all" <NL> (gdb) 
384
                    record.m_type = GdbMi::Tuple;
hjk's avatar
hjk committed
385 386 387 388 389
                    break;
                }
                ++from; // skip ','
                data.parseResultOrValue(from, to);
                if (data.isValid()) {
390
                    //qDebug() << "parsed response:" << data.toString();
hjk's avatar
hjk committed
391 392
                    record.m_children += data;
                    record.m_type = GdbMi::Tuple;
con's avatar
con committed
393 394
                }
            }
395 396 397 398
            if (asyncClass == "stopped") {
                handleAsyncOutput(record);
            } else if (asyncClass == "running") {
                // Archer has 'thread-id="all"' here
399 400 401 402
            } 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",
403
                // symbols-loaded="0"
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
404
                QByteArray id = record.findChild("id").data();
405
                if (!id.isEmpty())
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
406
                    q->showStatusMessage(tr("Library %1 loaded.").arg(_(id)));
hjk's avatar
hjk committed
407 408 409 410
            } 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
411 412
                QByteArray id = record.findChild("id").data();
                q->showStatusMessage(tr("Library %1 unloaded.").arg(_(id)));
413 414
            } else if (asyncClass == "thread-group-created") {
                // Archer has "{id="28902"}" 
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
415 416
                QByteArray id = record.findChild("id").data();
                q->showStatusMessage(tr("Thread group %1 created.").arg(_(id)));
417 418
            } else if (asyncClass == "thread-created") {
                //"{id="1",group-id="28902"}" 
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
419 420
                QByteArray id = record.findChild("id").data();
                q->showStatusMessage(tr("Thread %1 created.").arg(_(id)));
421 422
            } else if (asyncClass == "thread-group-exited") {
                // Archer has "{id="28902"}" 
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
423 424
                QByteArray id = record.findChild("id").data();
                q->showStatusMessage(tr("Thread group %1 exited.").arg(_(id)));
425 426
            } else if (asyncClass == "thread-exited") {
                //"{id="1",group-id="28902"}" 
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
427 428
                QByteArray id = record.findChild("id").data();
                QByteArray groupid = record.findChild("group-id").data();
429
                q->showStatusMessage(tr("Thread %1 in group %2 exited.")
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
430
                    .arg(_(id)).arg(_(groupid)));
hjk's avatar
hjk committed
431
            } else if (asyncClass == "thread-selected") {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
432 433
                QByteArray id = record.findChild("id").data();
                q->showStatusMessage(tr("Thread %1 selected.").arg(_(id)));
hjk's avatar
hjk committed
434
                //"{id="2"}" 
435
            #if defined(Q_OS_MAC)
436 437 438 439 440 441 442 443 444 445 446
            } 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 {
447
                qDebug() << "IGNORED ASYNC OUTPUT"
448
                    << asyncClass << record.toString();
449
            }
450 451
            break;
        }
452

453
        case '~': {
454 455 456
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingConsoleStreamOutput += data;
            if (data.startsWith("Reading symbols from ")) {
457
                q->showStatusMessage(tr("Reading %1...").arg(_(data.mid(21))));
458
            }
459 460
            break;
        }
461

462
        case '@': {
463 464
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingTargetStreamOutput += data;
465
            break;
con's avatar
con committed
466 467
        }

468 469 470 471 472 473
        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
474
                qq->showApplicationOutput(_(data.mid(9))); // cut "warning: "
con's avatar
con committed
475 476 477
            break;
        }

478 479
        case '^': {
            GdbResultRecord record;
con's avatar
con committed
480

481
            record.token = token;
con's avatar
con committed
482

483 484 485
            for (inner = from; inner != to; ++inner)
                if (*inner < 'a' || *inner > 'z')
                    break;
con's avatar
con committed
486

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
487
            QByteArray resultClass = QByteArray::fromRawData(from, inner - from);
488 489 490 491 492 493 494 495 496 497 498 499
            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
500

501 502
            from = inner;
            if (from != to) {
hjk's avatar
hjk committed
503 504 505 506 507 508 509 510 511
                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
512 513
                }
            }
514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538

            //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
539 540 541 542
        }
    }
}

543
void GdbEngine::handleStubAttached(const GdbResultRecord &, const QVariant &)
544 545 546
{
    qq->notifyInferiorStopped();
    handleAqcuiredInferior();
547
    m_autoContinue = true;
548 549 550 551
}

void GdbEngine::stubStarted()
{
552 553 554
    const qint64 attachedPID = m_stubProc.applicationPID();
    qq->notifyInferiorPidChanged(attachedPID);
    postCommand(_("attach %1").arg(attachedPID), CB(handleStubAttached));
555 556 557 558 559 560 561
}

void GdbEngine::stubError(const QString &msg)
{
    QMessageBox::critical(q->mainWindow(), tr("Debugger Error"), msg);
}

con's avatar
con committed
562 563
void GdbEngine::readGdbStandardError()
{
564
    qWarning() << "Unexpected gdb stderr:" << m_gdbProc.readAllStandardError();
con's avatar
con committed
565 566 567 568
}

void GdbEngine::readGdbStandardOutput()
{
569 570
    int newstart = 0;
    int scan = m_inbuffer.size();
con's avatar
con committed
571

572
    m_inbuffer.append(m_gdbProc.readAllStandardOutput());
con's avatar
con committed
573

574 575
    while (newstart < m_inbuffer.size()) {
        int start = newstart;
576
        int end = m_inbuffer.indexOf('\n', scan);
577 578 579 580 581
        if (end < 0) {
            m_inbuffer.remove(0, start);
            return;
        }
        newstart = end + 1;
582
        scan = newstart;
583 584
        if (end == start)
            continue;
585
        #if defined(Q_OS_WIN)
586 587 588 589 590
        if (m_inbuffer.at(end - 1) == '\r') {
            --end;
            if (end == start)
                continue;
        }
591
        #endif
592
        handleResponse(QByteArray::fromRawData(m_inbuffer.constData() + start, end - start));
con's avatar
con committed
593
    }
594
    m_inbuffer.clear();
con's avatar
con committed
595 596 597 598
}

void GdbEngine::interruptInferior()
{
hjk's avatar
hjk committed
599
    qq->notifyInferiorStopRequested();
600

hjk's avatar
hjk committed
601
    if (m_gdbProc.state() == QProcess::NotRunning) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
602
        debugMessage(_("TRYING TO INTERRUPT INFERIOR WITHOUT RUNNING GDB"));
hjk's avatar
hjk committed
603
        qq->notifyInferiorExited();
con's avatar
con committed
604
        return;
hjk's avatar
hjk committed
605
    }
con's avatar
con committed
606

607
    if (q->startMode() == StartRemote) {
608
        postCommand(_("-exec-interrupt"));
609 610 611
        return;
    }

612 613
    const qint64 attachedPID = q->inferiorPid();
    if (attachedPID <= 0) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
614
        debugMessage(_("TRYING TO INTERRUPT INFERIOR BEFORE PID WAS OBTAINED"));
con's avatar
con committed
615 616 617
        return;
    }

618 619
    if (!interruptProcess(attachedPID))
        debugMessage(_("CANNOT INTERRUPT %1").arg(attachedPID));
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;
    }
629
    if (pid == q->inferiorPid())
con's avatar
con committed
630
        return;
631 632
    debugMessage(_("FOUND PID %1").arg(pid));    

Roberto Raggi's avatar
Roberto Raggi committed
633
    qq->notifyInferiorPidChanged(pid);
634 635
    if (m_dumperInjectionLoad)
        tryLoadDebuggingHelpers();
con's avatar
con committed
636 637
}

638
void GdbEngine::postCommand(const QString &command, GdbCommandCallback callback,
639 640
                            const char *callbackName, const QVariant &cookie)
{
641
    postCommand(command, NoFlags, callback, callbackName, cookie);
642 643
}

644
void GdbEngine::postCommand(const QString &command, GdbCommandFlags flags,
645 646
                            GdbCommandCallback callback, const char *callbackName,
                            const QVariant &cookie)
con's avatar
con committed
647 648
{
    if (m_gdbProc.state() == QProcess::NotRunning) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
649
        debugMessage(_("NO GDB PROCESS RUNNING, CMD IGNORED: ") + command);
con's avatar
con committed
650 651 652
        return;
    }

653
    if (flags & RebuildModel) {
con's avatar
con committed
654
        ++m_pendingRequests;
655
        PENDING_DEBUG("   CALLBACK" << callbackName << "INCREMENTS PENDING TO:"
con's avatar
con committed
656 657
            << m_pendingRequests << command);
    } else {
658
        PENDING_DEBUG("   UNKNOWN CALLBACK" << callbackName << "LEAVES PENDING AT:"
con's avatar
con committed
659 660 661
            << m_pendingRequests << command);
    }

662
    GdbCommand cmd;
con's avatar
con committed
663
    cmd.command = command;
664 665 666
    cmd.flags = flags;
    cmd.callback = callback;
    cmd.callbackName = callbackName;
con's avatar
con committed
667 668
    cmd.cookie = cookie;

669
    if ((flags & NeedsStop) && q->status() != DebuggerInferiorStopped
hjk's avatar
hjk committed
670 671 672
            && q->status() != DebuggerProcessStartingUp) {
        // queue the commands that we cannot send at once
        QTC_ASSERT(q->status() == DebuggerInferiorRunning,
673
            qDebug() << "STATUS:" << q->status());
hjk's avatar
hjk committed
674
        q->showStatusMessage(tr("Stopping temporarily."));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
675
        debugMessage(_("QUEUING COMMAND ") + cmd.command);
hjk's avatar
hjk committed
676 677 678
        m_commandsToRunOnTemporaryBreak.append(cmd);
        interruptInferior();
    } else if (!command.isEmpty()) {
679
        flushCommand(cmd);
con's avatar
con committed
680 681 682
    }
}

683 684 685 686 687
void GdbEngine::flushCommand(GdbCommand &cmd)
{
    ++currentToken();
    m_cookieForToken[currentToken()] = cmd;
    cmd.command = QString::number(currentToken()) + cmd.command;
688
    if (cmd.flags & EmbedToken)
689 690 691 692 693
        cmd.command = cmd.command.arg(currentToken());

    m_gdbProc.write(cmd.command.toLatin1() + "\r\n");
    //emit gdbInputAvailable(QString(), "         " +  currentTime());
    //emit gdbInputAvailable(QString(), "[" + currentTime() + "]    " + cmd.command);
694
    emit gdbInputAvailable(LogInput, cmd.command);
695 696
}

con's avatar
con committed
697 698
void GdbEngine::handleResultRecord(const GdbResultRecord &record)
{
699 700
    //qDebug() << "TOKEN:" << record.token
    //    << " ACCEPTABLE:" << m_oldestAcceptableToken;
con's avatar
con committed
701 702 703 704 705 706 707
    //qDebug() << "";
    //qDebug() << "\nRESULT" << record.token << record.toString();

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

708
    GdbCommand cmd = m_cookieForToken.take(token);
con's avatar
con committed
709

710
    if (record.token < m_oldestAcceptableToken && (cmd.flags & Discardable)) {
711
        //qDebug() << "### SKIPPING OLD RESULT" << record.toString();
con's avatar
con committed
712 713 714 715 716
        //QMessageBox::information(m_mainWindow, tr("Skipped"), "xxx");
        return;
    }

#if 0
717
    qDebug() << "# handleOutput,"
hjk's avatar
hjk committed
718
        << "cmd name:" << cmd.callbackName
719
        << " cmd synchronized:" << cmd.synchronized
con's avatar
con committed
720 721 722 723 724
        << "\n record: " << record.toString();
#endif

    // << "\n data: " << record.data.toString(true);

725 726
    if (cmd.callback)
        (this->*(cmd.callback))(record, cmd.cookie);
con's avatar
con committed
727

728
    if (cmd.flags & RebuildModel) {
con's avatar
con committed
729
        --m_pendingRequests;
hjk's avatar
hjk committed
730
        PENDING_DEBUG("   TYPE " << cmd.callbackName << " DECREMENTS PENDING TO: "
con's avatar
con committed
731
            << m_pendingRequests << cmd.command);
732
        if (m_pendingRequests <= 0) {
hjk's avatar
hjk committed
733 734
            PENDING_DEBUG("\n\n ....  AND TRIGGERS MODEL UPDATE\n");
            rebuildModel();
735
        }
con's avatar
con committed
736
    } else {
hjk's avatar
hjk committed
737
        PENDING_DEBUG("   UNKNOWN TYPE " << cmd.callbackName << " LEAVES PENDING AT: "
con's avatar
con committed
738 739
            << m_pendingRequests << cmd.command);
    }
740 741 742 743 744 745 746 747 748 749

    // 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.
    if (m_cookieForToken.isEmpty() && m_autoContinue) {
        m_autoContinue = false;
        continueInferior();
        q->showStatusMessage(tr("Continuing after temporary stop."));
    }
con's avatar
con committed
750 751 752 753 754
}

void GdbEngine::executeDebuggerCommand(const QString &command)
{
    if (m_gdbProc.state() == QProcess::NotRunning) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
755
        debugMessage(_("NO GDB PROCESS RUNNING, PLAIN CMD IGNORED: ") + command);
con's avatar
con committed
756 757 758
        return;
    }

759
    m_gdbProc.write(command.toLocal8Bit() + "\r\n");
con's avatar
con committed
760 761
}

762
void GdbEngine::handleTargetCore(const GdbResultRecord &, const QVariant &)
763 764
{
    qq->notifyInferiorStopped();
765 766
    q->showStatusMessage(tr("Core file loaded."));
    q->resetLocation();
767
    tryLoadDebuggingHelpers();
768 769
    qq->stackHandler()->setCurrentIndex(0);
    updateLocals(); // Quick shot
770
    reloadStack();
771
    if (supportsThreads())
772
        postCommand(_("-thread-list-ids"), WatchUpdate, CB(handleStackListThreads), 0);
773
    qq->reloadRegisters();
774 775
}

776
void GdbEngine::handleQuerySources(const GdbResultRecord &record, const QVariant &)
con's avatar
con committed
777 778
{
    if (record.resultClass == GdbResultDone) {
779
        QMap<QString, QString> oldShortToFull = m_shortToFullName;
con's avatar
con committed
780 781 782 783 784 785
        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
786
            QString fileName = QString::fromLocal8Bit(item.findChild("file").data());
con's avatar
con committed
787
            GdbMi fullName = item.findChild("fullname");
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
788
            QString full = QString::fromLocal8Bit(fullName.data());
con's avatar
con committed
789 790 791 792
            #ifdef Q_OS_WIN
            full = QDir::cleanPath(full);
            #endif
            if (fullName.isValid() && QFileInfo(full).isReadable()) {
793
                //qDebug() << "STORING 2:" << fileName << full;
con's avatar
con committed
794 795 796 797
                m_shortToFullName[fileName] = full;
                m_fullToShortName[full] = fileName;
            }
        }
798 799
        if (m_shortToFullName != oldShortToFull)
            qq->sourceFileWindow()->setSourceFiles(m_shortToFullName);
con's avatar
con committed
800 801 802
    }
}

803
void GdbEngine::handleInfoThreads(const GdbResultRecord &record, const QVariant &)
804
{
805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821
    if (record.resultClass != GdbResultDone)
        return;
    // FIXME: use something more robust
    // WIN:     [New thread 3380.0x2bc]
    //          * 3 Thread 2312.0x4d0  0x7c91120f in ?? ()
    // LINUX:   * 1 Thread 0x7f466273c6f0 (LWP 21455)  0x0000000000404542 in ...
    const QString data = _(record.data.findChild("consolestreamoutput").data());
    if (data.isEmpty())
        return;
    // check "[New thread 3380.0x2bc]"
    if (data.startsWith(QLatin1Char('['))) {
        QRegExp ren(_("^\\[New thread (\\d+)\\.0x.*"));
        Q_ASSERT(ren.isValid());
        if (ren.indexIn(data) != -1) {
            maybeHandleInferiorPidChanged(ren.cap(1));
            return;
        }
822
    }
823 824 825 826 827
    // check "* 3 Thread ..."
    QRegExp re(_("^\\*? +\\d+ +[Tt]hread (\\d+)\\.0x.* in"));
    Q_ASSERT(re.isValid());
    if (re.indexIn(data) != -1)
        maybeHandleInferiorPidChanged(re.cap(1));
828 829
}

830
void GdbEngine::handleInfoProc(const GdbResultRecord &record, const QVariant &)
con's avatar
con committed
831 832
{
    if (record.resultClass == GdbResultDone) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
833
        #ifdef Q_OS_MAC
con's avatar
con committed
834
        //^done,process-id="85075"
Oswald Buddenhagen's avatar
nicer  
Oswald Buddenhagen committed
835
        maybeHandleInferiorPidChanged(_(record.data.findChild("process-id").data()));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
836
        #else
con's avatar
con committed
837
        // FIXME: use something more robust
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
838 839
        QRegExp re(__("process (\\d+)"));
        QString data = __(record.data.findChild("consolestreamoutput").data());
con's avatar
con committed
840 841 842 843 844 845
        if (re.indexIn(data) != -1)
            maybeHandleInferiorPidChanged(re.cap(1));
        #endif
    }
}

846
void GdbEngine::handleInfoShared(const GdbResultRecord &record, const QVariant &cookie)
con's avatar
con committed
847 848 849
{
    if (record.resultClass == GdbResultDone) {
        // let the modules handler do the parsing
850
        handleModulesList(record, cookie);
con's avatar
con committed
851 852 853
    }
}

854
#if 0
con's avatar
con committed
855 856 857 858 859 860 861 862 863 864
void GdbEngine::handleExecJumpToLine(const GdbResultRecord &record)
{
    // FIXME: remove this special case as soon as 'jump'
    // is supported by MI
    // "&"jump /home/apoenitz/dev/work/test1/test1.cpp:242"
    // ~"Continuing at 0x4058f3."
    // ~"run1 (argc=1, argv=0x7fffb213a478) at test1.cpp:242"
    // ~"242\t x *= 2;"
    //109^done"
    qq->notifyInferiorStopped();
865
    q->showStatusMessage(tr("Jumped. Stopped."));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
866
    QByteArray output = record.data.findChild("logstreamoutput").data();
867
    if (output.isEmpty())
con's avatar
con committed
868
        return;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
869 870 871 872 873 874 875 876 877
    int idx1 = output.indexOf(' ') + 1;
    if (idx1 > 0) {
        int idx2 = output.indexOf(':', idx1);
        if (idx2 > 0) {
            QString file = QString::fromLocal8Bit(output.mid(idx1, idx2 - idx1));
            int line = output.mid(idx2 + 1).toInt();
            q->gotoLocation(file, line, true);
        }
    }
con's avatar
con committed
878
}
879
#endif
con's avatar
con committed
880

881
void GdbEngine::handleExecRunToFunction(const GdbResultRecord &record, const QVariant &)
con's avatar
con committed
882 883 884 885 886 887 888 889
{
    // FIXME: remove this special case as soon as there's a real
    // reason given when the temporary breakpoint is hit.
    // reight now we get:
    // 14*stopped,thread-id="1",frame={addr="0x0000000000403ce4",
    // func="foo",args=[{name="str",value="@0x7fff0f450460"}],
    // file="main.cpp",fullname="/tmp/g/main.cpp",line="37"}
    qq->notifyInferiorStopped();
890
    q->showStatusMessage(tr("Run to Function finished. Stopped."));
con's avatar
con committed
891
    GdbMi frame = record.data.findChild("frame");
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
892
    QString file = QString::fromLocal8Bit(frame.findChild("fullname").data());
con's avatar
con committed
893
    int line = frame.findChild("line").data().toInt();
894 895
    qDebug() << "HIT:" << file << line << "IN" << frame.toString()
        << "--" << record.toString();
con's avatar
con committed
896 897 898
    q->gotoLocation(file, line, true);
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
899
static bool isExitedReason(const QByteArray &reason)
con's avatar
con committed
900
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
901 902 903 904
    return reason == "exited-normally"   // inferior exited normally
        || reason == "exited-signalled"  // inferior exited because of a signal
        //|| reason == "signal-received" // inferior received signal
        || reason == "exited";           // inferior exited
con's avatar
con committed
905 906
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
907
static bool isStoppedReason(const QByteArray &reason)
con's avatar
con committed
908
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
909 910 911 912 913 914 915
    return reason == "function-finished"  // -exec-finish
        || reason == "signal-received"  // handled as "isExitedReason"
        || reason == "breakpoint-hit"     // -exec-continue
        || reason == "end-stepping-range" // -exec-next, -exec-step
        || reason == "location-reached"   // -exec-until
        || reason == "access-watchpoint-trigger"
        || reason == "read-watchpoint-trigger"
916
        #ifdef Q_OS_MAC
con's avatar
con committed
917
        || reason.isEmpty()
918
        #endif
con's avatar
con committed
919 920 921
    ;
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
922 923
void GdbEngine::handleAqcuiredInferior()
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
924
    #ifdef Q_OS_WIN
925
    postCommand(_("info thread"), CB(handleInfoThreads));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
926
    #elif defined(Q_OS_MAC)
927
    postCommand(_("info pid"), NeedsStop, CB(handleInfoProc));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
928 929
    #else
    postCommand(_("info proc"), CB(handleInfoProc));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
930
    #endif
931
    if (theDebuggerBoolSetting(ListSourceFiles))
932
        reloadSourceFiles();
933

934
    // Reverse debugging. FIXME: Should only be used when available.
935 936
    //if (theDebuggerBoolSetting(EnableReverseDebugging))
    //    postCommand(_("target record"));
937

938
    tryLoadDebuggingHelpers();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
939

940
    #ifndef Q_OS_MAC
941
    // intentionally after tryLoadDebuggingHelpers(),
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
942
    // otherwise we'd interupt solib loading.
hjk's avatar
hjk committed
943
    if (theDebuggerBoolSetting(AllPluginBreakpoints)) {
944 945 946
        postCommand(_("set auto-solib-add on"));
        postCommand(_("set stop-on-solib-events 0"));
        postCommand(_("sharedlibrary .*"));
hjk's avatar
hjk committed
947
    } else if (theDebuggerBoolSetting(SelectedPluginBreakpoints)) {
948 949 950
        postCommand(_("set auto-solib-add on"));
        postCommand(_("set stop-on-solib-events 1"));
        postCommand(_("sharedlibrary ")
hjk's avatar
hjk committed
951 952
          + theDebuggerStringSetting(SelectedPluginBreakpointsPattern));
    } else if (theDebuggerBoolSetting(NoPluginBreakpoints)) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
953
        // should be like that already
954 955
        if (!m_dumperInjectionLoad)
            postCommand(_("set auto-solib-add off"));
956
        postCommand(_("set stop-on-solib-events 0"));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
957
    }
958 959
    #endif

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
960 961
    // nicer to see a bit of the world we live in
    reloadModules();
962
    attemptBreakpointSynchronization();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
963