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 202
}

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

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

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

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

con's avatar
con committed
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
#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

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

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

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

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

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

    lastTime = QTime::currentTime();

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

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

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

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

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

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

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

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

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

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

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

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

500 501
            from = inner;
            if (from != to) {
hjk's avatar
hjk committed
502 503 504 505 506 507 508 509 510
                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
511 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

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

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

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

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

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

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

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

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

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

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

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

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

617 618
    if (!interruptProcess(attachedPID))
        debugMessage(_("CANNOT INTERRUPT %1").arg(attachedPID));
con's avatar
con committed
619 620 621 622
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    // 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
749 750 751 752 753
}

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

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

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

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

802
void GdbEngine::handleInfoThreads(const GdbResultRecord &record, const QVariant &)
803
{
804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820
    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;
        }
821
    }
822 823 824 825 826
    // check "* 3 Thread ..."
    QRegExp re(_("^\\*? +\\d+ +[Tt]hread (\\d+)\\.0x.* in"));
    Q_ASSERT(re.isValid());
    if (re.indexIn(data) != -1)
        maybeHandleInferiorPidChanged(re.cap(1));
827 828
}

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

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

853
#if 0
con's avatar
con committed
854 855 856 857 858 859 860 861 862 863
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();
864
    q->showStatusMessage(tr("Jumped. Stopped."));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
865
    QByteArray output = record.data.findChild("logstreamoutput").data();
866
    if (output.isEmpty())
con's avatar
con committed
867
        return;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
868 869 870 871 872 873 874 875 876
    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
877
}
878
#endif
con's avatar
con committed
879

880
void GdbEngine::handleExecRunToFunction(const GdbResultRecord &record, const QVariant &)
con's avatar
con committed
881 882 883 884 885 886 887 888
{
    // 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();
889
    q->showStatusMessage(tr("Run to Function finished. Stopped."));
con's avatar
con committed
890
    GdbMi frame = record.data.findChild("frame");
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
891
    QString file = QString::fromLocal8Bit(frame.findChild("fullname").data());
con's avatar
con committed
892
    int line = frame.findChild("line").data().toInt();
893 894
    qDebug() << "HIT:" << file << line << "IN" << frame.toString()
        << "--" << record.toString();
con's avatar
con committed
895 896 897
    q->gotoLocation(file, line, true);
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
898
static bool isExitedReason(const QByteArray &reason)
con's avatar
con committed
899
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
900 901 902 903
    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
904 905
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
906
static bool isStoppedReason(const QByteArray &reason)
con's avatar
con committed
907
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
908 909 910 911 912 913 914
    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"
915
        #ifdef Q_OS_MAC
con's avatar
con committed
916
        || reason.isEmpty()
917
        #endif
con's avatar
con committed
918 919 920
    ;
}

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

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

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

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

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

con's avatar
con committed
964 965
void GdbEngine::