2009-02-25 09:15:00 +01:00
|
|
|
/**************************************************************************
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
|
|
|
|
** This file is part of Qt Creator
|
|
|
|
|
**
|
2011-01-11 16:28:15 +01:00
|
|
|
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2009-06-17 00:01:27 +10:00
|
|
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2010-12-17 16:01:08 +01:00
|
|
|
** No Commercial Usage
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
2010-12-17 16:01:08 +01:00
|
|
|
** This file contains pre-release code and may not be distributed.
|
|
|
|
|
** You may use this file in accordance with the terms and conditions
|
|
|
|
|
** contained in the Technology Preview License Agreement accompanying
|
|
|
|
|
** this package.
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
** GNU Lesser General Public License Usage
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
** 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.
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
2010-12-17 16:01:08 +01:00
|
|
|
** In addition, as a special exception, Nokia gives you certain additional
|
|
|
|
|
** rights. These rights are described in the Nokia Qt LGPL Exception
|
|
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
|
**
|
|
|
|
|
** If you have questions regarding the use of this file, please contact
|
|
|
|
|
** Nokia at qt-info@nokia.com.
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
**************************************************************************/
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-04-30 22:22:37 +02:00
|
|
|
#define QT_NO_CAST_FROM_ASCII
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
#include "gdbengine.h"
|
2010-06-16 11:08:54 +02:00
|
|
|
|
2011-01-10 10:14:23 +01:00
|
|
|
#include "debuggerstartparameters.h"
|
|
|
|
|
#include "disassemblerlines.h"
|
2009-09-23 15:28:50 +02:00
|
|
|
#include "attachgdbadapter.h"
|
|
|
|
|
#include "coregdbadapter.h"
|
2010-05-05 12:49:08 +02:00
|
|
|
#include "localplaingdbadapter.h"
|
2009-10-07 10:25:26 +02:00
|
|
|
#include "termgdbadapter.h"
|
2010-05-05 12:49:08 +02:00
|
|
|
#include "remotegdbserveradapter.h"
|
|
|
|
|
#include "remoteplaingdbadapter.h"
|
2009-09-23 15:28:50 +02:00
|
|
|
#include "trkgdbadapter.h"
|
2011-02-07 14:26:34 +01:00
|
|
|
#include "codagdbadapter.h"
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-03-17 17:00:06 +01:00
|
|
|
#include "debuggeractions.h"
|
2008-12-02 12:01:29 +01:00
|
|
|
#include "debuggerconstants.h"
|
2010-12-08 12:43:11 +01:00
|
|
|
#include "debuggercore.h"
|
|
|
|
|
#include "debuggerplugin.h"
|
|
|
|
|
#include "debuggerrunner.h"
|
2009-09-25 15:02:16 +02:00
|
|
|
#include "debuggerstringutils.h"
|
2011-02-11 15:00:13 +01:00
|
|
|
#include "debuggertooltipmanager.h"
|
2010-12-08 12:43:11 +01:00
|
|
|
#include "disassembleragent.h"
|
2008-12-02 12:01:29 +01:00
|
|
|
#include "gdbmi.h"
|
2010-12-08 12:43:11 +01:00
|
|
|
#include "gdboptionspage.h"
|
|
|
|
|
#include "memoryagent.h"
|
|
|
|
|
#include "watchutils.h"
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
#include "breakhandler.h"
|
|
|
|
|
#include "moduleshandler.h"
|
|
|
|
|
#include "registerhandler.h"
|
2010-02-02 17:25:14 +01:00
|
|
|
#include "snapshothandler.h"
|
2010-06-16 11:08:54 +02:00
|
|
|
#include "sourcefileshandler.h"
|
2008-12-02 12:01:29 +01:00
|
|
|
#include "stackhandler.h"
|
2010-05-18 12:12:22 +02:00
|
|
|
#include "threadshandler.h"
|
2008-12-02 12:01:29 +01:00
|
|
|
#include "watchhandler.h"
|
2010-02-02 17:25:14 +01:00
|
|
|
|
2011-01-21 16:42:57 +01:00
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
|
# include "dbgwinutils.h"
|
|
|
|
|
#endif
|
2010-09-22 16:20:08 +02:00
|
|
|
#include "logwindow.h"
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-04-08 17:25:28 +02:00
|
|
|
#include <coreplugin/icore.h>
|
2011-02-11 15:00:13 +01:00
|
|
|
#include <coreplugin/ifile.h>
|
2011-02-28 16:50:14 +01:00
|
|
|
#include <projectexplorer/abi.h>
|
2011-02-25 13:43:06 +01:00
|
|
|
#include <projectexplorer/projectexplorerconstants.h>
|
2010-12-08 12:43:11 +01:00
|
|
|
#include <texteditor/itexteditor.h>
|
|
|
|
|
#include <utils/qtcassert.h>
|
2008-12-09 16:18:28 +01:00
|
|
|
|
2010-02-02 17:25:14 +01:00
|
|
|
#include <QtCore/QCoreApplication>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <QtCore/QDebug>
|
|
|
|
|
#include <QtCore/QDir>
|
|
|
|
|
#include <QtCore/QFileInfo>
|
2009-08-31 09:14:04 +02:00
|
|
|
#include <QtCore/QMetaObject>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <QtCore/QTime>
|
|
|
|
|
#include <QtCore/QTimer>
|
2010-02-02 17:25:14 +01:00
|
|
|
#include <QtCore/QTemporaryFile>
|
2009-04-29 14:15:09 +02:00
|
|
|
#include <QtCore/QTextStream>
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
#include <QtGui/QAction>
|
2010-02-02 17:25:14 +01:00
|
|
|
#include <QtGui/QDialogButtonBox>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <QtGui/QLabel>
|
|
|
|
|
#include <QtGui/QMainWindow>
|
|
|
|
|
#include <QtGui/QMessageBox>
|
2009-04-08 17:25:28 +02:00
|
|
|
#include <QtGui/QPushButton>
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-06-03 20:45:49 +02:00
|
|
|
#ifdef Q_OS_UNIX
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <dlfcn.h>
|
|
|
|
|
#endif
|
2009-04-02 11:41:36 +02:00
|
|
|
#include <ctype.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2011-02-23 16:47:08 +01:00
|
|
|
using namespace ProjectExplorer;
|
|
|
|
|
|
2009-08-18 08:34:48 +02:00
|
|
|
namespace Debugger {
|
|
|
|
|
namespace Internal {
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2011-02-11 15:00:13 +01:00
|
|
|
class GdbToolTipContext : public DebuggerToolTipContext
|
|
|
|
|
{
|
|
|
|
|
public:
|
2011-02-21 16:45:07 +01:00
|
|
|
GdbToolTipContext(const DebuggerToolTipContext &c) :
|
|
|
|
|
DebuggerToolTipContext(c), editor(0) {}
|
2011-02-11 15:00:13 +01:00
|
|
|
|
|
|
|
|
QPoint mousePosition;
|
|
|
|
|
QString expression;
|
2011-02-21 16:45:07 +01:00
|
|
|
Core::IEditor *editor;
|
2011-02-11 15:00:13 +01:00
|
|
|
};
|
|
|
|
|
|
2010-02-03 16:50:28 +01:00
|
|
|
static const char winPythonVersionC[] = "python2.5";
|
|
|
|
|
|
2011-01-25 10:59:45 +01:00
|
|
|
enum { debugPending = 0 };
|
|
|
|
|
|
|
|
|
|
#define PENDING_DEBUG(s) do { if (debugPending) qDebug() << s; } while (0)
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-05-05 11:16:54 +02:00
|
|
|
#define CB(callback) &GdbEngine::callback, STRINGIFY(callback)
|
|
|
|
|
|
2010-07-07 16:30:42 +02:00
|
|
|
QByteArray GdbEngine::tooltipIName(const QString &exp)
|
2010-01-08 18:16:16 +01:00
|
|
|
{
|
2010-07-07 16:30:42 +02:00
|
|
|
return "tooltip." + exp.toLatin1().toHex();
|
2010-01-08 18:16:16 +01:00
|
|
|
}
|
|
|
|
|
|
2009-09-25 08:35:31 +02:00
|
|
|
static bool stateAcceptsGdbCommands(DebuggerState state)
|
2009-09-22 09:27:19 +02:00
|
|
|
{
|
2009-10-20 11:02:16 +02:00
|
|
|
switch (state) {
|
2010-07-09 17:07:59 +02:00
|
|
|
case EngineSetupRequested:
|
2010-07-09 08:48:33 +02:00
|
|
|
case EngineSetupOk:
|
|
|
|
|
case EngineSetupFailed:
|
2009-10-20 11:02:16 +02:00
|
|
|
case InferiorUnrunnable:
|
2010-07-09 17:07:59 +02:00
|
|
|
case InferiorSetupRequested:
|
2010-07-08 18:10:50 +02:00
|
|
|
case InferiorSetupFailed:
|
2010-07-09 17:07:59 +02:00
|
|
|
case EngineRunRequested:
|
|
|
|
|
case InferiorRunRequested:
|
|
|
|
|
case InferiorRunOk:
|
|
|
|
|
case InferiorStopRequested:
|
|
|
|
|
case InferiorStopOk:
|
|
|
|
|
case InferiorShutdownRequested:
|
|
|
|
|
case EngineShutdownRequested:
|
|
|
|
|
case InferiorShutdownOk:
|
2009-10-20 11:02:16 +02:00
|
|
|
case InferiorShutdownFailed:
|
|
|
|
|
return true;
|
|
|
|
|
case DebuggerNotReady:
|
|
|
|
|
case InferiorStopFailed:
|
2011-01-14 14:25:02 +01:00
|
|
|
case InferiorSetupOk:
|
2010-07-09 17:07:59 +02:00
|
|
|
case EngineRunFailed:
|
2011-01-14 14:40:42 +01:00
|
|
|
case InferiorExitOk:
|
2010-07-09 17:07:59 +02:00
|
|
|
case InferiorRunFailed:
|
|
|
|
|
case EngineShutdownOk:
|
|
|
|
|
case EngineShutdownFailed:
|
|
|
|
|
case DebuggerFinished:
|
2010-07-08 11:54:06 +02:00
|
|
|
return false;
|
2009-10-20 11:02:16 +02:00
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2009-09-22 09:27:19 +02:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
static int ¤tToken()
|
|
|
|
|
{
|
|
|
|
|
static int token = 0;
|
|
|
|
|
return token;
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-24 11:16:00 +02:00
|
|
|
static QByteArray parsePlainConsoleStream(const GdbResponse &response)
|
2009-07-01 12:49:41 +02:00
|
|
|
{
|
2009-09-24 11:16:00 +02:00
|
|
|
GdbMi output = response.data.findChild("consolestreamoutput");
|
2009-07-01 12:49:41 +02:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// GdbEngine
|
|
|
|
|
//
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2011-01-12 12:10:12 +01:00
|
|
|
GdbEngine::GdbEngine(const DebuggerStartParameters &startParameters,
|
|
|
|
|
DebuggerEngine *masterEngine)
|
|
|
|
|
: DebuggerEngine(startParameters, masterEngine)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2011-01-24 13:37:07 +01:00
|
|
|
setObjectName(_("GdbEngine"));
|
2010-07-21 11:05:40 +02:00
|
|
|
|
2009-10-27 12:05:03 +01:00
|
|
|
m_busy = false;
|
2010-11-16 12:52:02 +01:00
|
|
|
m_gdbAdapter = 0;
|
|
|
|
|
m_debuggingHelperState = DebuggingHelperUninitialized;
|
|
|
|
|
m_gdbVersion = 100;
|
|
|
|
|
m_gdbBuildVersion = -1;
|
|
|
|
|
m_isMacGdb = false;
|
|
|
|
|
m_hasPython = false;
|
|
|
|
|
m_registerNamesListed = false;
|
|
|
|
|
m_hasInferiorThreadList = false;
|
|
|
|
|
m_sourcesListUpdating = false;
|
|
|
|
|
m_oldestAcceptableToken = -1;
|
|
|
|
|
m_outputCodec = QTextCodec::codecForLocale();
|
|
|
|
|
m_pendingWatchRequests = 0;
|
|
|
|
|
m_pendingBreakpointRequests = 0;
|
|
|
|
|
m_commandsDoneCallback = 0;
|
2010-12-17 13:07:17 +01:00
|
|
|
m_stackNeeded = false;
|
2011-01-19 10:48:39 +01:00
|
|
|
m_preparedForQmlBreak = false;
|
2011-02-10 13:43:02 +01:00
|
|
|
m_disassembleUsesComma = false;
|
|
|
|
|
|
2010-11-16 12:52:02 +01:00
|
|
|
invalidateSourcesList();
|
|
|
|
|
|
2010-07-15 16:43:56 +02:00
|
|
|
m_gdbAdapter = createAdapter();
|
2009-10-27 12:05:03 +01:00
|
|
|
|
2010-11-16 12:52:02 +01:00
|
|
|
m_commandTimer.setSingleShot(true);
|
|
|
|
|
connect(&m_commandTimer, SIGNAL(timeout()), SLOT(commandTimeout()));
|
|
|
|
|
|
2010-11-10 16:33:11 +01:00
|
|
|
connect(debuggerCore()->action(AutoDerefPointers), SIGNAL(valueChanged(QVariant)),
|
2010-09-23 11:15:56 +02:00
|
|
|
SLOT(reloadLocals()));
|
2010-11-10 16:33:11 +01:00
|
|
|
connect(debuggerCore()->action(SortStructMembers), SIGNAL(valueChanged(QVariant)),
|
2010-09-23 11:15:56 +02:00
|
|
|
SLOT(reloadLocals()));
|
2011-01-17 15:23:22 +01:00
|
|
|
connect(debuggerCore()->action(ShowStdNamespace), SIGNAL(valueChanged(QVariant)),
|
|
|
|
|
SLOT(reloadLocals()));
|
|
|
|
|
connect(debuggerCore()->action(ShowQtNamespace), SIGNAL(valueChanged(QVariant)),
|
|
|
|
|
SLOT(reloadLocals()));
|
2010-11-10 16:33:11 +01:00
|
|
|
connect(debuggerCore()->action(CreateFullBacktrace), SIGNAL(triggered()),
|
2010-09-23 11:15:56 +02:00
|
|
|
SLOT(createFullBacktrace()));
|
2009-09-23 12:04:25 +02:00
|
|
|
}
|
2009-09-23 09:54:21 +02:00
|
|
|
|
2009-09-23 12:04:25 +02:00
|
|
|
DebuggerStartMode GdbEngine::startMode() const
|
|
|
|
|
{
|
2010-06-14 08:57:15 +02:00
|
|
|
return startParameters().startMode;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-05-05 12:49:08 +02:00
|
|
|
AbstractGdbProcess *GdbEngine::gdbProc() const
|
|
|
|
|
{
|
|
|
|
|
return m_gdbAdapter->gdbProc();
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
GdbEngine::~GdbEngine()
|
|
|
|
|
{
|
2010-03-11 11:07:16 +01:00
|
|
|
// Prevent sending error messages afterwards.
|
2010-05-05 12:49:08 +02:00
|
|
|
if (m_gdbAdapter)
|
|
|
|
|
disconnect(gdbProc(), 0, this, 0);
|
2009-10-05 14:59:39 +02:00
|
|
|
delete m_gdbAdapter;
|
2009-10-26 11:55:02 +01:00
|
|
|
m_gdbAdapter = 0;
|
2009-09-21 11:09:38 +02:00
|
|
|
}
|
|
|
|
|
|
2009-09-25 11:06:51 +02:00
|
|
|
QString GdbEngine::errorMessage(QProcess::ProcessError error)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
switch (error) {
|
|
|
|
|
case QProcess::FailedToStart:
|
2009-09-25 11:06:51 +02:00
|
|
|
return tr("The Gdb process failed to start. Either the "
|
2008-12-02 12:01:29 +01:00
|
|
|
"invoked program '%1' is missing, or you may have insufficient "
|
2010-09-29 10:32:41 +02:00
|
|
|
"permissions to invoke the program.\n%2")
|
|
|
|
|
.arg(m_gdb, gdbProc()->errorString());
|
2008-12-02 12:01:29 +01:00
|
|
|
case QProcess::Crashed:
|
2009-09-25 11:06:51 +02:00
|
|
|
return tr("The Gdb process crashed some time after starting "
|
2008-12-02 12:01:29 +01:00
|
|
|
"successfully.");
|
|
|
|
|
case QProcess::Timedout:
|
2009-09-25 11:06:51 +02:00
|
|
|
return tr("The last waitFor...() function timed out. "
|
2008-12-02 12:01:29 +01:00
|
|
|
"The state of QProcess is unchanged, and you can try calling "
|
|
|
|
|
"waitFor...() again.");
|
|
|
|
|
case QProcess::WriteError:
|
2009-09-25 11:06:51 +02:00
|
|
|
return tr("An error occurred when attempting to write "
|
2008-12-02 12:01:29 +01:00
|
|
|
"to the Gdb process. For example, the process may not be running, "
|
|
|
|
|
"or it may have closed its input channel.");
|
|
|
|
|
case QProcess::ReadError:
|
2009-09-25 11:06:51 +02:00
|
|
|
return tr("An error occurred when attempting to read from "
|
2008-12-02 12:01:29 +01:00
|
|
|
"the Gdb process. For example, the process may not be running.");
|
|
|
|
|
default:
|
2009-09-25 11:06:51 +02:00
|
|
|
return tr("An unknown error in the Gdb process occurred. ");
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
static void dump(const char *first, const char *middle, const QString & to)
|
|
|
|
|
{
|
|
|
|
|
QByteArray ba(first, middle - first);
|
2009-07-13 17:35:17 +02:00
|
|
|
Q_UNUSED(to)
|
2008-12-02 12:01:29 +01:00
|
|
|
// 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
|
|
|
|
|
|
2011-01-21 16:42:57 +01:00
|
|
|
// Parse "~:gdb: unknown target exception 0xc0000139 at 0x77bef04e\n"
|
|
|
|
|
// and return an exception message
|
|
|
|
|
static inline QString msgWinException(const QByteArray &data)
|
|
|
|
|
{
|
|
|
|
|
const int exCodePos = data.indexOf("0x");
|
|
|
|
|
const int blankPos = exCodePos != -1 ? data.indexOf(' ', exCodePos + 1) : -1;
|
|
|
|
|
const int addressPos = blankPos != -1 ? data.indexOf("0x", blankPos + 1) : -1;
|
|
|
|
|
if (addressPos < 0)
|
|
|
|
|
return GdbEngine::tr("An exception was triggered.");
|
|
|
|
|
const unsigned exCode = data.mid(exCodePos, blankPos - exCodePos).toUInt(0, 0);
|
|
|
|
|
const quint64 address = data.mid(addressPos).trimmed().toULongLong(0, 0);
|
|
|
|
|
QString rc;
|
|
|
|
|
QTextStream str(&rc);
|
|
|
|
|
str << GdbEngine::tr("An exception was triggered: ");
|
|
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
|
formatWindowsException(exCode, address, 0, 0, 0, str);
|
|
|
|
|
#else
|
|
|
|
|
Q_UNUSED(exCode)
|
|
|
|
|
Q_UNUSED(address)
|
|
|
|
|
#endif
|
|
|
|
|
str << '.';
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-15 14:47:14 +01:00
|
|
|
void GdbEngine::readDebugeeOutput(const QByteArray &data)
|
|
|
|
|
{
|
2010-06-16 11:08:54 +02:00
|
|
|
QString msg = m_outputCodec->toUnicode(data.constData(), data.length(),
|
|
|
|
|
&m_outputCodecState);
|
2011-02-21 17:01:40 +01:00
|
|
|
showMessage(msg, AppOutput);
|
2009-01-15 14:47:14 +01:00
|
|
|
}
|
|
|
|
|
|
2009-02-24 20:34:30 +01:00
|
|
|
void GdbEngine::handleResponse(const QByteArray &buff)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(QString::fromLocal8Bit(buff, buff.length()), LogOutput);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
#if 0
|
2010-08-23 14:27:24 +02:00
|
|
|
static QTime lastTime;
|
2008-12-02 12:01:29 +01:00
|
|
|
qDebug() // << "#### start response handling #### "
|
|
|
|
|
<< lastTime.msecsTo(QTime::currentTime()) << "ms,"
|
2009-05-06 20:55:21 +02:00
|
|
|
<< "buf:" << buff.left(1500) << "..."
|
|
|
|
|
//<< "buf:" << buff
|
2009-02-24 20:34:30 +01:00
|
|
|
<< "size:" << buff.size();
|
2010-08-23 14:27:24 +02:00
|
|
|
lastTime = QTime::currentTime();
|
2008-12-02 12:01:29 +01:00
|
|
|
#else
|
2009-05-06 20:55:21 +02:00
|
|
|
//qDebug() << "buf:" << buff;
|
2008-12-02 12:01:29 +01:00
|
|
|
#endif
|
2009-02-24 20:34:30 +01:00
|
|
|
if (buff.isEmpty() || buff == "(gdb) ")
|
|
|
|
|
return;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-02-24 20:34:30 +01:00
|
|
|
const char *from = buff.constData();
|
|
|
|
|
const char *to = from + buff.size();
|
|
|
|
|
const char *inner;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-02-24 20:34:30 +01:00
|
|
|
int token = -1;
|
|
|
|
|
// token is a sequence of numbers
|
|
|
|
|
for (inner = from; inner != to; ++inner)
|
|
|
|
|
if (*inner < '0' || *inner > '9')
|
2008-12-02 12:01:29 +01:00
|
|
|
break;
|
2009-02-24 20:34:30 +01:00
|
|
|
if (from != inner) {
|
|
|
|
|
token = QByteArray(from, inner - from).toInt();
|
|
|
|
|
from = inner;
|
2009-05-06 20:55:21 +02:00
|
|
|
//qDebug() << "found token" << token;
|
2009-02-24 20:34:30 +01:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-09-24 11:16:00 +02:00
|
|
|
// next char decides kind of response
|
2009-02-24 20:34:30 +01:00
|
|
|
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;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-09-24 11:16:00 +02:00
|
|
|
GdbMi result;
|
2009-02-24 20:34:30 +01:00
|
|
|
while (from != to) {
|
|
|
|
|
GdbMi data;
|
2009-04-07 14:14:43 +02:00
|
|
|
if (*from != ',') {
|
2010-01-29 21:33:57 +01:00
|
|
|
// happens on archer where we get
|
|
|
|
|
// 23^running <NL> *running,thread-id="all" <NL> (gdb)
|
2009-09-24 11:16:00 +02:00
|
|
|
result.m_type = GdbMi::Tuple;
|
2009-04-07 14:14:43 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
++from; // skip ','
|
|
|
|
|
data.parseResultOrValue(from, to);
|
|
|
|
|
if (data.isValid()) {
|
2009-09-24 11:16:00 +02:00
|
|
|
//qDebug() << "parsed result:" << data.toString();
|
|
|
|
|
result.m_children += data;
|
|
|
|
|
result.m_type = GdbMi::Tuple;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
2009-02-24 20:34:30 +01:00
|
|
|
if (asyncClass == "stopped") {
|
2009-10-09 10:20:42 +02:00
|
|
|
handleStopResponse(result);
|
2009-11-09 23:21:18 +01:00
|
|
|
m_pendingLogStreamOutput.clear();
|
|
|
|
|
m_pendingConsoleStreamOutput.clear();
|
2009-02-24 20:34:30 +01:00
|
|
|
} else if (asyncClass == "running") {
|
|
|
|
|
// Archer has 'thread-id="all"' here
|
2009-04-01 15:44:34 +02:00
|
|
|
} 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",
|
2009-04-01 16:06:49 +02:00
|
|
|
// symbols-loaded="0"
|
2009-09-24 11:16:00 +02:00
|
|
|
QByteArray id = result.findChild("id").data();
|
2009-04-29 10:33:37 +02:00
|
|
|
if (!id.isEmpty())
|
2010-03-23 14:49:02 +01:00
|
|
|
showStatusMessage(tr("Library %1 loaded").arg(_(id)), 1000);
|
2010-07-21 11:02:51 +02:00
|
|
|
progressPing();
|
2009-12-09 11:13:20 +01:00
|
|
|
invalidateSourcesList();
|
2009-04-07 09:40:56 +02:00
|
|
|
} 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"
|
2009-09-24 11:16:00 +02:00
|
|
|
QByteArray id = result.findChild("id").data();
|
2010-07-21 11:02:51 +02:00
|
|
|
progressPing();
|
2010-03-23 14:49:02 +01:00
|
|
|
showStatusMessage(tr("Library %1 unloaded").arg(_(id)), 1000);
|
2009-12-09 11:13:20 +01:00
|
|
|
invalidateSourcesList();
|
2010-03-24 13:47:28 +01:00
|
|
|
} else if (asyncClass == "thread-group-added") {
|
|
|
|
|
// 7.1-symbianelf has "{id="i1"}"
|
2010-04-07 09:19:36 +02:00
|
|
|
} else if (asyncClass == "thread-group-created"
|
|
|
|
|
|| asyncClass == "thread-group-started") {
|
|
|
|
|
// Archer had only "{id="28902"}" at some point of 6.8.x.
|
2010-04-07 15:18:11 +02:00
|
|
|
// *-started seems to be standard in 7.1, but in early
|
|
|
|
|
// 7.0.x, there was a *-created instead.
|
2010-07-21 11:02:51 +02:00
|
|
|
progressPing();
|
2010-07-15 14:54:17 +02:00
|
|
|
// 7.1.50 has thread-group-started,id="i1",pid="3529"
|
2009-09-24 11:16:00 +02:00
|
|
|
QByteArray id = result.findChild("id").data();
|
2010-04-29 18:36:18 +02:00
|
|
|
showStatusMessage(tr("Thread group %1 created").arg(_(id)), 1000);
|
2010-07-15 14:54:17 +02:00
|
|
|
int pid = id.toInt();
|
|
|
|
|
if (!pid) {
|
|
|
|
|
id = result.findChild("pid").data();
|
|
|
|
|
pid = id.toInt();
|
|
|
|
|
}
|
|
|
|
|
if (pid)
|
|
|
|
|
notifyInferiorPid(pid);
|
2009-04-01 16:06:49 +02:00
|
|
|
} else if (asyncClass == "thread-created") {
|
2010-01-29 21:33:57 +01:00
|
|
|
//"{id="1",group-id="28902"}"
|
2009-09-24 11:16:00 +02:00
|
|
|
QByteArray id = result.findChild("id").data();
|
2010-03-23 14:49:02 +01:00
|
|
|
showStatusMessage(tr("Thread %1 created").arg(_(id)), 1000);
|
2009-04-01 16:49:45 +02:00
|
|
|
} else if (asyncClass == "thread-group-exited") {
|
2010-01-29 21:33:57 +01:00
|
|
|
// Archer has "{id="28902"}"
|
2009-09-24 11:16:00 +02:00
|
|
|
QByteArray id = result.findChild("id").data();
|
2010-03-23 14:49:02 +01:00
|
|
|
showStatusMessage(tr("Thread group %1 exited").arg(_(id)), 1000);
|
2009-04-01 16:49:45 +02:00
|
|
|
} else if (asyncClass == "thread-exited") {
|
2010-01-29 21:33:57 +01:00
|
|
|
//"{id="1",group-id="28902"}"
|
2009-09-24 11:16:00 +02:00
|
|
|
QByteArray id = result.findChild("id").data();
|
|
|
|
|
QByteArray groupid = result.findChild("group-id").data();
|
2010-03-23 14:49:02 +01:00
|
|
|
showStatusMessage(tr("Thread %1 in group %2 exited")
|
2010-03-10 10:32:58 +01:00
|
|
|
.arg(_(id)).arg(_(groupid)), 1000);
|
2009-04-07 09:51:21 +02:00
|
|
|
} else if (asyncClass == "thread-selected") {
|
2009-09-24 11:16:00 +02:00
|
|
|
QByteArray id = result.findChild("id").data();
|
2010-03-23 14:49:02 +01:00
|
|
|
showStatusMessage(tr("Thread %1 selected").arg(_(id)), 1000);
|
2010-01-29 21:33:57 +01:00
|
|
|
//"{id="2"}"
|
2010-03-26 08:56:36 +01:00
|
|
|
} else if (m_isMacGdb && asyncClass == "shlibs-updated") {
|
|
|
|
|
// Apple's gdb announces updated libs.
|
2009-12-09 11:13:20 +01:00
|
|
|
invalidateSourcesList();
|
2010-03-26 08:56:36 +01:00
|
|
|
} else if (m_isMacGdb && asyncClass == "shlibs-added") {
|
|
|
|
|
// Apple's gdb announces added libs.
|
2009-02-24 20:34:30 +01:00
|
|
|
// {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=""}}
|
2009-12-09 11:13:20 +01:00
|
|
|
invalidateSourcesList();
|
2010-03-26 16:31:47 +01:00
|
|
|
} else if (m_isMacGdb && asyncClass == "resolve-pending-breakpoint") {
|
|
|
|
|
// Apple's gdb announces resolved breakpoints.
|
|
|
|
|
// new_bp="1",pended_bp="1",new_expr="\"gdbengine.cpp\":1584",
|
|
|
|
|
// bkpt={number="1",type="breakpoint",disp="keep",enabled="y",
|
|
|
|
|
// addr="0x0000000115cc3ddf",func="foo()",file="../foo.cpp",
|
2010-06-15 11:14:44 +02:00
|
|
|
// line="1584",shlib="/../libFoo_debug.dylib",times="0"}
|
2010-03-26 16:31:47 +01:00
|
|
|
const GdbMi bkpt = result.findChild("bkpt");
|
|
|
|
|
const int number = bkpt.findChild("number").data().toInt();
|
2011-01-24 13:37:07 +01:00
|
|
|
if (!isQmlStepBreakpoint1(number) && isQmlStepBreakpoint2(number)) {
|
|
|
|
|
BreakpointId id = breakHandler()->findBreakpointByNumber(number);
|
|
|
|
|
updateBreakpointDataFromOutput(id, bkpt);
|
2011-03-02 13:37:29 +01:00
|
|
|
BreakpointResponse response = breakHandler()->response(id);
|
|
|
|
|
if (response.correctedLineNumber == 0)
|
|
|
|
|
attemptAdjustBreakpointLocation(id);
|
2011-01-24 13:37:07 +01:00
|
|
|
}
|
2009-02-24 20:34:30 +01:00
|
|
|
} else {
|
2009-05-06 20:55:21 +02:00
|
|
|
qDebug() << "IGNORED ASYNC OUTPUT"
|
2009-09-24 11:16:00 +02:00
|
|
|
<< asyncClass << result.toString();
|
2009-02-12 13:31:19 +01:00
|
|
|
}
|
2009-02-24 20:34:30 +01:00
|
|
|
break;
|
|
|
|
|
}
|
2009-02-12 13:31:19 +01:00
|
|
|
|
2009-02-24 20:34:30 +01:00
|
|
|
case '~': {
|
2009-03-03 09:31:13 +01:00
|
|
|
QByteArray data = GdbMi::parseCString(from, to);
|
|
|
|
|
m_pendingConsoleStreamOutput += data;
|
2009-09-23 12:12:36 +02:00
|
|
|
|
|
|
|
|
// Parse pid from noise.
|
2010-06-16 11:08:54 +02:00
|
|
|
if (!inferiorPid()) {
|
2009-09-23 12:12:36 +02:00
|
|
|
// 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]*"));
|
2009-10-08 16:14:25 +02:00
|
|
|
// Mac: [Switching to process 9294 local thread 0x2e03] or
|
|
|
|
|
// [Switching to process 31773]
|
|
|
|
|
static QRegExp re3(_("Switching to process ([0-9]+)"));
|
2009-09-23 12:12:36 +02:00
|
|
|
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));
|
2009-09-29 14:40:23 +02:00
|
|
|
else if (re3.indexIn(_(data)) != -1)
|
|
|
|
|
maybeHandleInferiorPidChanged(re3.cap(1));
|
2009-09-23 12:04:25 +02:00
|
|
|
}
|
2009-09-23 12:12:36 +02:00
|
|
|
|
|
|
|
|
// Show some messages to give the impression something happens.
|
2009-10-22 20:04:59 +02:00
|
|
|
if (data.startsWith("Reading symbols from ")) {
|
2009-09-23 12:12:36 +02:00
|
|
|
showStatusMessage(tr("Reading %1...").arg(_(data.mid(21))), 1000);
|
2010-07-22 12:02:09 +02:00
|
|
|
progressPing();
|
2009-12-09 11:13:20 +01:00
|
|
|
invalidateSourcesList();
|
2009-10-22 20:04:59 +02:00
|
|
|
} else if (data.startsWith("[New ") || data.startsWith("[Thread ")) {
|
|
|
|
|
if (data.endsWith('\n'))
|
|
|
|
|
data.chop(1);
|
2010-07-22 12:02:09 +02:00
|
|
|
progressPing();
|
2009-09-23 12:12:36 +02:00
|
|
|
showStatusMessage(_(data), 1000);
|
2011-01-21 16:42:57 +01:00
|
|
|
} else if (data.startsWith("gdb: unknown target exception 0x")) {
|
|
|
|
|
// [Windows, most likely some DLL/Entry point not found]:
|
|
|
|
|
// "gdb: unknown target exception 0xc0000139 at 0x77bef04e"
|
|
|
|
|
// This may be fatal and cause the target to exit later
|
|
|
|
|
m_lastWinException = msgWinException(data);
|
|
|
|
|
showMessage(m_lastWinException, LogMisc);
|
2009-10-22 20:04:59 +02:00
|
|
|
}
|
2011-01-24 13:37:07 +01:00
|
|
|
|
|
|
|
|
if (data.startsWith("QMLBP:")) {
|
|
|
|
|
int pos1 = 6;
|
|
|
|
|
int pos2 = data.indexOf(' ', pos1);
|
|
|
|
|
m_qmlBreakpointNumbers[2] = data.mid(pos1, pos2 - pos1).toInt();
|
|
|
|
|
//qDebug() << "FOUND QMLBP: " << m_qmlBreakpointNumbers[2];
|
|
|
|
|
//qDebug() << "CURRENT: " << m_qmlBreakpointNumbers;
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-24 20:34:30 +01:00
|
|
|
break;
|
|
|
|
|
}
|
2009-02-12 13:31:19 +01:00
|
|
|
|
2009-02-24 20:34:30 +01:00
|
|
|
case '@': {
|
2009-10-05 17:58:25 +02:00
|
|
|
readDebugeeOutput(GdbMi::parseCString(from, to));
|
2009-02-24 20:34:30 +01:00
|
|
|
break;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-02-24 20:34:30 +01:00
|
|
|
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:"))
|
2010-11-30 12:47:53 +01:00
|
|
|
showMessage(_(data.mid(9)), AppStuff); // Cut "warning: "
|
2008-12-02 12:01:29 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-24 20:34:30 +01:00
|
|
|
case '^': {
|
2009-09-24 11:16:00 +02:00
|
|
|
GdbResponse response;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-09-24 11:16:00 +02:00
|
|
|
response.token = token;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-02-24 20:34:30 +01:00
|
|
|
for (inner = from; inner != to; ++inner)
|
|
|
|
|
if (*inner < 'a' || *inner > 'z')
|
|
|
|
|
break;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-05-04 18:22:13 +02:00
|
|
|
QByteArray resultClass = QByteArray::fromRawData(from, inner - from);
|
2009-09-28 15:06:17 +02:00
|
|
|
if (resultClass == "done") {
|
2009-09-24 11:16:00 +02:00
|
|
|
response.resultClass = GdbResultDone;
|
2009-09-28 15:06:17 +02:00
|
|
|
} else if (resultClass == "running") {
|
2009-09-24 11:16:00 +02:00
|
|
|
response.resultClass = GdbResultRunning;
|
2009-09-28 15:06:17 +02:00
|
|
|
} else if (resultClass == "connected") {
|
2009-09-24 11:16:00 +02:00
|
|
|
response.resultClass = GdbResultConnected;
|
2009-09-28 15:06:17 +02:00
|
|
|
} else if (resultClass == "error") {
|
2009-09-24 11:16:00 +02:00
|
|
|
response.resultClass = GdbResultError;
|
2009-09-28 15:06:17 +02:00
|
|
|
} else if (resultClass == "exit") {
|
2009-09-24 11:16:00 +02:00
|
|
|
response.resultClass = GdbResultExit;
|
2009-09-28 15:06:17 +02:00
|
|
|
} else {
|
2009-09-24 11:16:00 +02:00
|
|
|
response.resultClass = GdbResultUnknown;
|
2009-09-28 15:06:17 +02:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-02-24 20:34:30 +01:00
|
|
|
from = inner;
|
|
|
|
|
if (from != to) {
|
2009-04-07 09:51:21 +02:00
|
|
|
if (*from == ',') {
|
|
|
|
|
++from;
|
2009-09-24 11:16:00 +02:00
|
|
|
response.data.parseTuple_helper(from, to);
|
|
|
|
|
response.data.m_type = GdbMi::Tuple;
|
|
|
|
|
response.data.m_name = "data";
|
2009-04-07 09:51:21 +02:00
|
|
|
} else {
|
2010-11-30 12:47:53 +01:00
|
|
|
// Archer has this.
|
2009-09-24 11:16:00 +02:00
|
|
|
response.data.m_type = GdbMi::Tuple;
|
|
|
|
|
response.data.m_name = "data";
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
2009-02-24 20:34:30 +01:00
|
|
|
|
|
|
|
|
//qDebug() << "\nLOG STREAM:" + m_pendingLogStreamOutput;
|
|
|
|
|
//qDebug() << "\nCONSOLE STREAM:" + m_pendingConsoleStreamOutput;
|
2009-09-24 11:16:00 +02:00
|
|
|
response.data.setStreamOutput("logstreamoutput",
|
2009-02-24 20:34:30 +01:00
|
|
|
m_pendingLogStreamOutput);
|
2009-09-24 11:16:00 +02:00
|
|
|
response.data.setStreamOutput("consolestreamoutput",
|
2009-02-24 20:34:30 +01:00
|
|
|
m_pendingConsoleStreamOutput);
|
|
|
|
|
m_pendingLogStreamOutput.clear();
|
|
|
|
|
m_pendingConsoleStreamOutput.clear();
|
|
|
|
|
|
2009-10-23 21:32:08 +02:00
|
|
|
handleResultRecord(&response);
|
2009-02-24 20:34:30 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: {
|
|
|
|
|
qDebug() << "UNKNOWN RESPONSE TYPE" << c;
|
|
|
|
|
break;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::readGdbStandardError()
|
|
|
|
|
{
|
2010-05-05 12:49:08 +02:00
|
|
|
QByteArray err = gdbProc()->readAllStandardError();
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("UNEXPECTED GDB STDERR: " + err));
|
2009-10-30 18:18:21 +01:00
|
|
|
if (err == "Undefined command: \"bb\". Try \"help\".\n")
|
|
|
|
|
return;
|
2010-03-29 16:45:34 +02:00
|
|
|
if (err.startsWith("BFD: reopening"))
|
|
|
|
|
return;
|
2009-10-30 18:18:21 +01:00
|
|
|
qWarning() << "Unexpected gdb stderr:" << err;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::readGdbStandardOutput()
|
|
|
|
|
{
|
2010-11-16 12:42:57 +01:00
|
|
|
m_commandTimer.start(); // Restart timer.
|
2009-11-03 14:56:27 +01:00
|
|
|
|
2009-02-24 22:36:36 +01:00
|
|
|
int newstart = 0;
|
|
|
|
|
int scan = m_inbuffer.size();
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2010-05-05 12:49:08 +02:00
|
|
|
m_inbuffer.append(gdbProc()->readAllStandardOutput());
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2010-11-16 12:42:57 +01:00
|
|
|
// This can trigger when a dialog starts a nested event loop.
|
2009-10-27 12:05:03 +01:00
|
|
|
if (m_busy)
|
|
|
|
|
return;
|
|
|
|
|
|
2009-02-24 20:34:30 +01:00
|
|
|
while (newstart < m_inbuffer.size()) {
|
|
|
|
|
int start = newstart;
|
2009-02-24 22:36:36 +01:00
|
|
|
int end = m_inbuffer.indexOf('\n', scan);
|
2009-02-24 20:34:30 +01:00
|
|
|
if (end < 0) {
|
|
|
|
|
m_inbuffer.remove(0, start);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
newstart = end + 1;
|
2009-02-24 22:36:36 +01:00
|
|
|
scan = newstart;
|
2009-02-24 20:34:30 +01:00
|
|
|
if (end == start)
|
|
|
|
|
continue;
|
2010-06-10 13:12:12 +02:00
|
|
|
# if defined(Q_OS_WIN)
|
2009-02-24 20:34:30 +01:00
|
|
|
if (m_inbuffer.at(end - 1) == '\r') {
|
|
|
|
|
--end;
|
|
|
|
|
if (end == start)
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2010-06-10 13:12:12 +02:00
|
|
|
# endif
|
2009-10-27 12:05:03 +01:00
|
|
|
m_busy = true;
|
2009-02-24 20:34:30 +01:00
|
|
|
handleResponse(QByteArray::fromRawData(m_inbuffer.constData() + start, end - start));
|
2009-10-27 12:05:03 +01:00
|
|
|
m_busy = false;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2009-02-24 21:50:20 +01:00
|
|
|
m_inbuffer.clear();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::interruptInferior()
|
|
|
|
|
{
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == InferiorStopRequested, qDebug() << state(); return);
|
2011-02-03 16:49:28 +01:00
|
|
|
|
|
|
|
|
if (debuggerCore()->boolSetting(TargetAsync)) {
|
|
|
|
|
postCommand("-exec-interrupt");
|
|
|
|
|
} else {
|
|
|
|
|
showStatusMessage(tr("Stop requested..."), 5000);
|
|
|
|
|
showMessage(_("TRYING TO INTERRUPT INFERIOR"));
|
|
|
|
|
m_gdbAdapter->interruptInferior();
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-11-02 16:59:57 +01:00
|
|
|
void GdbEngine::interruptInferiorTemporarily()
|
|
|
|
|
{
|
2010-06-16 11:08:54 +02:00
|
|
|
foreach (const GdbCommand &cmd, m_commandsToRunOnTemporaryBreak) {
|
2009-11-02 16:59:57 +01:00
|
|
|
if (cmd.flags & LosesChild) {
|
2010-07-09 17:07:59 +02:00
|
|
|
notifyInferiorIll();
|
2010-07-20 12:14:59 +02:00
|
|
|
return;
|
2009-11-02 16:59:57 +01:00
|
|
|
}
|
2010-06-16 11:08:54 +02:00
|
|
|
}
|
2010-07-20 12:14:59 +02:00
|
|
|
requestInterruptInferior();
|
2009-11-02 16:59:57 +01:00
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
|
|
|
|
|
{
|
2009-05-25 16:22:11 +02:00
|
|
|
const qint64 pid = pid0.toLongLong();
|
2008-12-02 12:01:29 +01:00
|
|
|
if (pid == 0) {
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("Cannot parse PID from %1").arg(pid0));
|
2008-12-02 12:01:29 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2010-06-16 11:08:54 +02:00
|
|
|
if (pid == inferiorPid())
|
2008-12-02 12:01:29 +01:00
|
|
|
return;
|
2009-05-25 16:22:11 +02:00
|
|
|
|
2010-06-16 11:08:54 +02:00
|
|
|
showMessage(_("FOUND PID %1").arg(pid));
|
|
|
|
|
notifyInferiorPid(pid);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-01-05 16:51:55 +01:00
|
|
|
void GdbEngine::postCommand(const QByteArray &command, AdapterCallback callback,
|
2009-09-21 11:09:38 +02:00
|
|
|
const char *callbackName, const QVariant &cookie)
|
2009-09-21 17:35:19 +02:00
|
|
|
{
|
|
|
|
|
postCommand(command, NoFlags, callback, callbackName, cookie);
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-05 16:51:55 +01:00
|
|
|
void GdbEngine::postCommand(const QByteArray &command, GdbCommandFlags flags,
|
2009-09-21 17:35:19 +02:00
|
|
|
AdapterCallback callback,
|
|
|
|
|
const char *callbackName, const QVariant &cookie)
|
2009-09-21 11:09:38 +02:00
|
|
|
{
|
|
|
|
|
GdbCommand cmd;
|
|
|
|
|
cmd.command = command;
|
2009-09-22 11:31:34 +02:00
|
|
|
cmd.flags = flags;
|
2009-09-21 11:09:38 +02:00
|
|
|
cmd.adapterCallback = callback;
|
|
|
|
|
cmd.callbackName = callbackName;
|
|
|
|
|
cmd.cookie = cookie;
|
|
|
|
|
postCommandHelper(cmd);
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-05 16:51:55 +01:00
|
|
|
void GdbEngine::postCommand(const QByteArray &command, GdbCommandCallback callback,
|
2009-05-05 11:16:54 +02:00
|
|
|
const char *callbackName, const QVariant &cookie)
|
|
|
|
|
{
|
2009-05-07 09:51:21 +02:00
|
|
|
postCommand(command, NoFlags, callback, callbackName, cookie);
|
2009-05-05 11:16:54 +02:00
|
|
|
}
|
|
|
|
|
|
2010-01-05 16:51:55 +01:00
|
|
|
void GdbEngine::postCommand(const QByteArray &command, GdbCommandFlags flags,
|
2009-05-05 11:16:54 +02:00
|
|
|
GdbCommandCallback callback, const char *callbackName,
|
|
|
|
|
const QVariant &cookie)
|
2009-09-21 11:09:38 +02:00
|
|
|
{
|
|
|
|
|
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)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-09-24 11:45:08 +02:00
|
|
|
if (!stateAcceptsGdbCommands(state())) {
|
2010-01-14 15:25:53 +01:00
|
|
|
PENDING_DEBUG(_("NO GDB PROCESS RUNNING, CMD IGNORED: " + cmd.command));
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("NO GDB PROCESS RUNNING, CMD IGNORED: %1 %2")
|
2010-01-05 16:51:55 +01:00
|
|
|
.arg(_(cmd.command)).arg(state()));
|
2008-12-02 12:01:29 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-02 18:02:30 +01:00
|
|
|
if (cmd.flags & RebuildWatchModel) {
|
|
|
|
|
++m_pendingWatchRequests;
|
2010-03-03 18:08:18 +01:00
|
|
|
PENDING_DEBUG(" WATCH MODEL:" << cmd.command << "=>" << cmd.callbackName
|
2010-03-02 18:02:30 +01:00
|
|
|
<< "INCREMENTS PENDING TO" << m_pendingWatchRequests);
|
2010-03-03 18:08:18 +01:00
|
|
|
} else if (cmd.flags & RebuildBreakpointModel) {
|
|
|
|
|
++m_pendingBreakpointRequests;
|
|
|
|
|
PENDING_DEBUG(" BRWAKPOINT MODEL:" << cmd.command << "=>" << cmd.callbackName
|
|
|
|
|
<< "INCREMENTS PENDING TO" << m_pendingBreakpointRequests);
|
2008-12-02 12:01:29 +01:00
|
|
|
} else {
|
2009-10-27 14:52:29 +01:00
|
|
|
PENDING_DEBUG(" OTHER (IN):" << cmd.command << "=>" << cmd.callbackName
|
2010-03-03 18:08:18 +01:00
|
|
|
<< "LEAVES PENDING WATCH AT" << m_pendingWatchRequests
|
|
|
|
|
<< "LEAVES PENDING BREAKPOINT AT" << m_pendingBreakpointRequests);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-04-09 14:53:45 +02:00
|
|
|
// FIXME: clean up logic below
|
|
|
|
|
if (cmd.flags & Immediate) {
|
|
|
|
|
// This should always be sent.
|
|
|
|
|
flushCommand(cmd);
|
|
|
|
|
} else if ((cmd.flags & NeedsStop)
|
|
|
|
|
|| !m_commandsToRunOnTemporaryBreak.isEmpty()) {
|
2010-07-09 17:07:59 +02:00
|
|
|
if (state() == InferiorStopOk || state() == InferiorUnrunnable
|
|
|
|
|
|| state() == InferiorSetupRequested || state() == EngineSetupOk
|
|
|
|
|
|| state() == InferiorShutdownRequested) {
|
2009-09-25 08:35:31 +02:00
|
|
|
// Can be safely sent now.
|
|
|
|
|
flushCommand(cmd);
|
|
|
|
|
} else {
|
|
|
|
|
// Queue the commands that we cannot send at once.
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("QUEUING COMMAND " + cmd.command));
|
2009-09-25 08:35:31 +02:00
|
|
|
m_commandsToRunOnTemporaryBreak.append(cmd);
|
2010-07-09 17:07:59 +02:00
|
|
|
if (state() == InferiorStopRequested) {
|
|
|
|
|
if (cmd.flags & LosesChild) {
|
|
|
|
|
notifyInferiorIll();
|
|
|
|
|
}
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("CHILD ALREADY BEING INTERRUPTED. STILL HOPING."));
|
2010-04-12 14:56:19 +02:00
|
|
|
// Calling shutdown() here breaks all situations where two
|
|
|
|
|
// NeedsStop commands are issued in quick succession.
|
2010-07-09 17:07:59 +02:00
|
|
|
} else if (state() == InferiorRunOk) {
|
2010-04-29 18:36:18 +02:00
|
|
|
showStatusMessage(tr("Stopping temporarily"), 1000);
|
2009-11-02 16:59:57 +01:00
|
|
|
interruptInferiorTemporarily();
|
2009-11-02 14:40:54 +01:00
|
|
|
} else {
|
2010-07-09 17:07:59 +02:00
|
|
|
qDebug() << "ATTEMPTING TO QUEUE COMMAND "
|
|
|
|
|
<< cmd.command << "IN INAPPROPRIATE STATE" << state();
|
2009-10-23 21:54:27 +02:00
|
|
|
}
|
2009-09-25 08:35:31 +02:00
|
|
|
}
|
2009-09-21 11:09:38 +02:00
|
|
|
} else if (!cmd.command.isEmpty()) {
|
2009-05-04 18:30:22 +02:00
|
|
|
flushCommand(cmd);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-02 16:40:16 +01:00
|
|
|
void GdbEngine::flushQueuedCommands()
|
|
|
|
|
{
|
2010-04-29 18:36:18 +02:00
|
|
|
showStatusMessage(tr("Processing queued commands"), 1000);
|
2009-11-02 16:40:16 +01:00
|
|
|
while (!m_commandsToRunOnTemporaryBreak.isEmpty()) {
|
|
|
|
|
GdbCommand cmd = m_commandsToRunOnTemporaryBreak.takeFirst();
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("RUNNING QUEUED COMMAND " + cmd.command + ' '
|
2010-01-05 16:51:55 +01:00
|
|
|
+ cmd.callbackName));
|
2009-11-02 16:40:16 +01:00
|
|
|
flushCommand(cmd);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-21 11:09:38 +02:00
|
|
|
void GdbEngine::flushCommand(const GdbCommand &cmd0)
|
2009-05-04 18:30:22 +02:00
|
|
|
{
|
2010-07-15 14:03:45 +02:00
|
|
|
if (!stateAcceptsGdbCommands(state())) {
|
|
|
|
|
showMessage(_(cmd0.command), LogInput);
|
|
|
|
|
showMessage(_("GDB PROCESS ACCEPTS NO CMD IN STATE %1 ").arg(state()));
|
2009-09-15 12:16:21 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-29 10:32:41 +02:00
|
|
|
QTC_ASSERT(gdbProc()->state() == QProcess::Running, return;)
|
2010-09-07 10:59:40 +02:00
|
|
|
|
2009-05-04 18:30:22 +02:00
|
|
|
++currentToken();
|
2010-07-15 14:03:45 +02:00
|
|
|
GdbCommand cmd = cmd0;
|
2009-07-06 11:37:21 +02:00
|
|
|
cmd.postTime = QTime::currentTime();
|
2009-05-04 18:30:22 +02:00
|
|
|
m_cookieForToken[currentToken()] = cmd;
|
2010-06-28 14:53:31 +02:00
|
|
|
if (cmd.flags & ConsoleCommand)
|
|
|
|
|
cmd.command = "-interpreter-exec console \"" + cmd.command + '"';
|
2010-01-05 16:51:55 +01:00
|
|
|
cmd.command = QByteArray::number(currentToken()) + cmd.command;
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_(cmd.command), LogInput);
|
2009-09-15 12:16:21 +02:00
|
|
|
|
2010-01-05 16:51:55 +01:00
|
|
|
m_gdbAdapter->write(cmd.command + "\r\n");
|
2009-11-02 16:59:57 +01:00
|
|
|
|
2010-09-06 11:29:52 +02:00
|
|
|
// Start Watchdog.
|
2010-11-16 12:42:57 +01:00
|
|
|
if (m_commandTimer.interval() <= 20000)
|
|
|
|
|
m_commandTimer.setInterval(commandTimeoutTime());
|
2010-09-06 15:29:26 +02:00
|
|
|
// The process can die for external reason between the "-gdb-exit" was
|
|
|
|
|
// sent and a response could be retrieved. We don't want the watchdog
|
|
|
|
|
// to bark in that case since the only possible outcome is a dead
|
|
|
|
|
// process anyway.
|
2010-09-07 10:59:40 +02:00
|
|
|
if (!cmd.command.endsWith("-gdb-exit"))
|
2010-11-16 12:42:57 +01:00
|
|
|
m_commandTimer.start();
|
2009-11-03 14:56:27 +01:00
|
|
|
|
2010-07-09 17:07:59 +02:00
|
|
|
//if (cmd.flags & LosesChild)
|
2010-10-27 15:39:25 +02:00
|
|
|
// notifyInferiorIll();
|
2009-05-04 18:30:22 +02:00
|
|
|
}
|
|
|
|
|
|
2009-12-11 16:59:34 +01:00
|
|
|
int GdbEngine::commandTimeoutTime() const
|
|
|
|
|
{
|
2010-11-10 16:33:11 +01:00
|
|
|
int time = debuggerCore()->action(GdbWatchdogTimeout)->value().toInt();
|
2011-01-05 09:55:01 +01:00
|
|
|
return 1000 * qMax(40, time);
|
2009-12-11 16:59:34 +01:00
|
|
|
}
|
|
|
|
|
|
2009-11-03 14:56:27 +01:00
|
|
|
void GdbEngine::commandTimeout()
|
|
|
|
|
{
|
|
|
|
|
QList<int> keys = m_cookieForToken.keys();
|
|
|
|
|
qSort(keys);
|
2009-12-11 16:59:34 +01:00
|
|
|
bool killIt = false;
|
2009-11-03 14:56:27 +01:00
|
|
|
foreach (int key, keys) {
|
2009-12-11 16:59:34 +01:00
|
|
|
const GdbCommand &cmd = m_cookieForToken.value(key);
|
|
|
|
|
if (!(cmd.flags & NonCriticalResponse))
|
|
|
|
|
killIt = true;
|
2010-01-12 13:49:56 +01:00
|
|
|
QByteArray msg = QByteArray::number(key);
|
|
|
|
|
msg += ": " + cmd.command + " => ";
|
2010-09-08 10:32:40 +02:00
|
|
|
msg += cmd.callbackName ? cmd.callbackName : "<unnamed callback>";
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_(msg));
|
2009-11-03 14:56:27 +01:00
|
|
|
}
|
2009-12-11 16:59:34 +01:00
|
|
|
if (killIt) {
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("TIMED OUT WAITING FOR GDB REPLY. COMMANDS STILL IN PROGRESS:"));
|
2010-11-16 12:42:57 +01:00
|
|
|
int timeOut = m_commandTimer.interval();
|
|
|
|
|
//m_commandTimer.stop();
|
2010-01-08 11:32:53 +01:00
|
|
|
const QString msg = tr("The gdb process has not responded "
|
2010-01-12 15:13:36 +01:00
|
|
|
"to a command within %1 seconds. This could mean it is stuck "
|
2009-12-11 16:59:34 +01:00
|
|
|
"in an endless loop or taking longer than expected to perform "
|
2010-01-08 11:32:53 +01:00
|
|
|
"the operation.\nYou can choose between waiting "
|
2010-01-12 13:51:46 +01:00
|
|
|
"longer or abort debugging.").arg(timeOut / 1000);
|
2009-12-11 16:59:34 +01:00
|
|
|
QMessageBox *mb = showMessageBox(QMessageBox::Critical,
|
2010-01-29 21:33:57 +01:00
|
|
|
tr("Gdb not responding"), msg,
|
2009-12-11 16:59:34 +01:00
|
|
|
QMessageBox::Ok | QMessageBox::Cancel);
|
|
|
|
|
mb->button(QMessageBox::Cancel)->setText(tr("Give gdb more time"));
|
|
|
|
|
mb->button(QMessageBox::Ok)->setText(tr("Stop debugging"));
|
|
|
|
|
if (mb->exec() == QMessageBox::Ok) {
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("KILLING DEBUGGER AS REQUESTED BY USER"));
|
2009-12-11 16:59:34 +01:00
|
|
|
// This is an undefined state, so we just pull the emergency brake.
|
2010-06-15 11:42:49 +02:00
|
|
|
watchHandler()->endCycle();
|
2010-05-05 12:49:08 +02:00
|
|
|
gdbProc()->kill();
|
2009-12-11 16:59:34 +01:00
|
|
|
} else {
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("CONTINUE DEBUGGER AS REQUESTED BY USER"));
|
2009-12-11 16:59:34 +01:00
|
|
|
}
|
2010-01-12 13:49:56 +01:00
|
|
|
} else {
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("\nNON-CRITICAL TIMEOUT\n"));
|
2009-12-11 16:59:34 +01:00
|
|
|
}
|
2009-11-03 14:56:27 +01:00
|
|
|
}
|
|
|
|
|
|
2009-10-23 21:32:08 +02:00
|
|
|
void GdbEngine::handleResultRecord(GdbResponse *response)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-09-24 11:16:00 +02:00
|
|
|
//qDebug() << "TOKEN:" << response.token
|
2009-05-06 20:55:21 +02:00
|
|
|
// << " ACCEPTABLE:" << m_oldestAcceptableToken;
|
2009-09-24 11:16:00 +02:00
|
|
|
//qDebug() << "\nRESULT" << response.token << response.toString();
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-10-23 21:32:08 +02:00
|
|
|
int token = response->token;
|
2008-12-02 12:01:29 +01:00
|
|
|
if (token == -1)
|
|
|
|
|
return;
|
|
|
|
|
|
2009-08-13 09:42:34 +02:00
|
|
|
if (!m_cookieForToken.contains(token)) {
|
2009-10-05 08:56:02 +02:00
|
|
|
// In theory this should not happen (rather the error should be
|
|
|
|
|
// reported in the "first" response to the command) in practice it
|
|
|
|
|
// does. We try to handle a few situations we are aware of gracefully.
|
|
|
|
|
// Ideally, this code should not be present at all.
|
2011-01-21 16:42:57 +01:00
|
|
|
showMessage(_("COOKIE FOR TOKEN %1 ALREADY EATEN (%2). "
|
|
|
|
|
"TWO RESPONSES FOR ONE COMMAND?").arg(token).
|
|
|
|
|
arg(QString::fromAscii(stateName(state()))));
|
2009-10-23 21:32:08 +02:00
|
|
|
if (response->resultClass == GdbResultError) {
|
|
|
|
|
QByteArray msg = response->data.findChild("msg").data();
|
2009-10-05 08:56:02 +02:00
|
|
|
if (msg == "Cannot find new threads: generic error") {
|
|
|
|
|
// Handle a case known to occur on Linux/gdb 6.8 when debugging moc
|
|
|
|
|
// with helpers enabled. In this case we get a second response with
|
|
|
|
|
// msg="Cannot find new threads: generic error"
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("APPLYING WORKAROUND #1"));
|
2009-10-05 08:56:02 +02:00
|
|
|
showMessageBox(QMessageBox::Critical,
|
|
|
|
|
tr("Executable failed"), QString::fromLocal8Bit(msg));
|
2010-04-29 18:36:18 +02:00
|
|
|
showStatusMessage(tr("Process failed to start"));
|
2010-07-09 17:07:59 +02:00
|
|
|
//shutdown();
|
|
|
|
|
notifyInferiorIll();
|
2010-01-29 21:33:57 +01:00
|
|
|
} else if (msg == "\"finish\" not meaningful in the outermost frame.") {
|
2009-10-05 08:56:02 +02:00
|
|
|
// Handle a case known to appear on gdb 6.4 symbianelf when
|
|
|
|
|
// the stack is cut due to access to protected memory.
|
2010-07-09 17:07:59 +02:00
|
|
|
//showMessage(_("APPLYING WORKAROUND #2"));
|
|
|
|
|
notifyInferiorStopOk();
|
2009-10-05 08:56:02 +02:00
|
|
|
} else if (msg.startsWith("Cannot find bounds of current function")) {
|
|
|
|
|
// Happens when running "-exec-next" in a function for which
|
|
|
|
|
// there is no debug information. Divert to "-exec-next-step"
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("APPLYING WORKAROUND #3"));
|
2010-07-09 17:07:59 +02:00
|
|
|
notifyInferiorStopOk();
|
2010-03-10 16:17:40 +01:00
|
|
|
executeNextI();
|
2009-10-29 18:31:45 +01:00
|
|
|
} else if (msg.startsWith("Couldn't get registers: No such process.")) {
|
|
|
|
|
// Happens on archer-tromey-python 6.8.50.20090910-cvs
|
|
|
|
|
// There might to be a race between a process shutting down
|
|
|
|
|
// and library load messages.
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("APPLYING WORKAROUND #4"));
|
2010-07-09 17:07:59 +02:00
|
|
|
notifyInferiorStopOk();
|
2010-10-27 15:39:25 +02:00
|
|
|
//notifyInferiorIll();
|
2010-07-09 17:07:59 +02:00
|
|
|
//showStatusMessage(tr("Executable failed: %1")
|
|
|
|
|
// .arg(QString::fromLocal8Bit(msg)));
|
|
|
|
|
//shutdown();
|
|
|
|
|
//showMessageBox(QMessageBox::Critical,
|
|
|
|
|
// tr("Executable failed"), QString::fromLocal8Bit(msg));
|
2010-11-24 15:55:09 +01:00
|
|
|
} else if (msg.contains("Cannot insert breakpoint")) {
|
|
|
|
|
// For breakpoints set by address to non-existent addresses we
|
|
|
|
|
// might get something like "6^error,msg="Warning:\nCannot insert
|
|
|
|
|
// breakpoint 3.\nError accessing memory address 0x34592327:
|
|
|
|
|
// Input/output error.\nCannot insert breakpoint 4.\nError
|
|
|
|
|
// accessing memory address 0x34592335: Input/output error.\n".
|
|
|
|
|
// This should not stop us from proceeding.
|
|
|
|
|
// Most notably, that happens after a "6^running" and "*running"
|
|
|
|
|
// We are probably sitting at _start and can't proceed as
|
|
|
|
|
// long as the breakpoints are enabled.
|
|
|
|
|
// FIXME: Should we silently disable the offending breakpoints?
|
|
|
|
|
showMessage(_("APPLYING WORKAROUND #5"));
|
|
|
|
|
showMessageBox(QMessageBox::Critical,
|
|
|
|
|
tr("Setting breakpoints failed"), QString::fromLocal8Bit(msg));
|
|
|
|
|
QTC_ASSERT(state() == InferiorRunOk, /**/);
|
|
|
|
|
notifyInferiorSpontaneousStop();
|
|
|
|
|
notifyEngineIll();
|
2009-10-05 08:56:02 +02:00
|
|
|
} else {
|
2011-01-21 16:42:57 +01:00
|
|
|
// Windows: Some DLL or some function not found. Report
|
|
|
|
|
// the exception now in a box.
|
|
|
|
|
if (msg.startsWith("During startup program exited with"))
|
|
|
|
|
notifyInferiorExited();
|
|
|
|
|
QString logMsg;
|
|
|
|
|
if (!m_lastWinException.isEmpty())
|
|
|
|
|
logMsg = m_lastWinException + QLatin1Char('\n');
|
|
|
|
|
logMsg += QString::fromLocal8Bit(msg);
|
|
|
|
|
showMessageBox(QMessageBox::Critical, tr("Executable Failed"), logMsg);
|
|
|
|
|
showStatusMessage(tr("Executable failed: %1").arg(logMsg));
|
2009-09-28 09:18:24 +02:00
|
|
|
}
|
2009-08-13 09:42:34 +02:00
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2009-05-04 18:30:22 +02:00
|
|
|
GdbCommand cmd = m_cookieForToken.take(token);
|
2010-11-10 16:33:11 +01:00
|
|
|
if (debuggerCore()->boolSetting(LogTimeStamps)) {
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("Response time: %1: %2 s")
|
2010-01-05 16:51:55 +01:00
|
|
|
.arg(_(cmd.command))
|
2010-06-14 17:23:25 +02:00
|
|
|
.arg(cmd.postTime.msecsTo(QTime::currentTime()) / 1000.),
|
|
|
|
|
LogTime);
|
2009-07-06 11:37:21 +02:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-10-23 21:32:08 +02:00
|
|
|
if (response->token < m_oldestAcceptableToken && (cmd.flags & Discardable)) {
|
2010-06-14 18:19:02 +02:00
|
|
|
//showMessage(_("### SKIPPING OLD RESULT") + response.toString());
|
2008-12-02 12:01:29 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-23 21:32:08 +02:00
|
|
|
response->cookie = cmd.cookie;
|
2009-09-24 11:16:00 +02:00
|
|
|
|
2010-06-30 14:18:47 +02:00
|
|
|
bool isExpectedResult =
|
2010-10-12 11:52:11 +02:00
|
|
|
(response->resultClass == GdbResultError) // Can always happen.
|
|
|
|
|
|| (response->resultClass == GdbResultRunning && (cmd.flags & RunRequest))
|
|
|
|
|
|| (response->resultClass == GdbResultExit && (cmd.flags & ExitRequest))
|
|
|
|
|
|| (response->resultClass == GdbResultDone);
|
|
|
|
|
// GdbResultDone can almost "always" happen. Known examples are:
|
|
|
|
|
// (response->resultClass == GdbResultDone && cmd.command == "continue")
|
2010-06-30 14:39:41 +02:00
|
|
|
// Happens with some incarnations of gdb 6.8 for "jump to line"
|
2010-10-12 11:52:11 +02:00
|
|
|
// (response->resultClass == GdbResultDone && cmd.command.startsWith("jump"))
|
|
|
|
|
// (response->resultClass == GdbResultDone && cmd.command.startsWith("detach"))
|
|
|
|
|
// Happens when stepping finishes very quickly and issues *stopped/^done
|
|
|
|
|
// instead of ^running/*stopped
|
|
|
|
|
// (response->resultClass == GdbResultDone && (cmd.flags & RunRequest));
|
2010-06-30 14:18:47 +02:00
|
|
|
|
2010-06-30 14:39:41 +02:00
|
|
|
if (!isExpectedResult) {
|
2010-04-09 12:49:21 +02:00
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
|
// Ignore spurious 'running' responses to 'attach'
|
2010-09-02 16:46:39 +02:00
|
|
|
const bool warning = !((startParameters().startMode == AttachExternal
|
|
|
|
|
|| startParameters().useTerminal)
|
2010-04-09 12:49:21 +02:00
|
|
|
&& cmd.command.startsWith("attach"));
|
|
|
|
|
#else
|
|
|
|
|
const bool warning = true;
|
|
|
|
|
#endif
|
|
|
|
|
if (warning) {
|
|
|
|
|
QByteArray rsp = GdbResponse::stringFromResultClass(response->resultClass);
|
|
|
|
|
rsp = "UNEXPECTED RESPONSE '" + rsp + "' TO COMMAND '" + cmd.command + "'";
|
|
|
|
|
qWarning() << rsp << " AT " __FILE__ ":" STRINGIFY(__LINE__);
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_(rsp));
|
2010-04-09 12:49:21 +02:00
|
|
|
}
|
2009-10-12 12:00:07 +02:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2010-06-30 14:39:41 +02:00
|
|
|
if (cmd.callback)
|
|
|
|
|
(this->*cmd.callback)(*response);
|
|
|
|
|
else if (cmd.adapterCallback)
|
|
|
|
|
(m_gdbAdapter->*cmd.adapterCallback)(*response);
|
|
|
|
|
|
2010-03-02 18:02:30 +01:00
|
|
|
if (cmd.flags & RebuildWatchModel) {
|
|
|
|
|
--m_pendingWatchRequests;
|
2009-10-27 14:52:29 +01:00
|
|
|
PENDING_DEBUG(" WATCH" << cmd.command << "=>" << cmd.callbackName
|
2010-03-03 18:08:18 +01:00
|
|
|
<< "DECREMENTS PENDING WATCH TO" << m_pendingWatchRequests);
|
2010-03-02 18:02:30 +01:00
|
|
|
if (m_pendingWatchRequests <= 0) {
|
2010-03-03 18:08:18 +01:00
|
|
|
PENDING_DEBUG("\n\n ... AND TRIGGERS WATCH MODEL UPDATE\n");
|
2010-03-02 18:02:30 +01:00
|
|
|
rebuildWatchModel();
|
2009-02-13 12:34:10 +01:00
|
|
|
}
|
2010-03-03 18:08:18 +01:00
|
|
|
} else if (cmd.flags & RebuildBreakpointModel) {
|
|
|
|
|
--m_pendingBreakpointRequests;
|
|
|
|
|
PENDING_DEBUG(" BREAKPOINT" << cmd.command << "=>" << cmd.callbackName
|
|
|
|
|
<< "DECREMENTS PENDING TO" << m_pendingWatchRequests);
|
|
|
|
|
if (m_pendingBreakpointRequests <= 0) {
|
|
|
|
|
PENDING_DEBUG("\n\n ... AND TRIGGERS BREAKPOINT MODEL UPDATE\n");
|
|
|
|
|
attemptBreakpointSynchronization();
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
} else {
|
2009-10-27 14:52:29 +01:00
|
|
|
PENDING_DEBUG(" OTHER (OUT):" << cmd.command << "=>" << cmd.callbackName
|
2010-03-03 18:08:18 +01:00
|
|
|
<< "LEAVES PENDING WATCH AT" << m_pendingWatchRequests
|
|
|
|
|
<< "LEAVES PENDING BREAKPOINT AT" << m_pendingBreakpointRequests);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2009-05-07 12:18:45 +02:00
|
|
|
|
2009-11-02 14:40:54 +01:00
|
|
|
// Commands were queued, but we were in RunningRequested state, so the interrupt
|
|
|
|
|
// was postponed.
|
|
|
|
|
// This is done after the command callbacks so the running-requesting commands
|
|
|
|
|
// can assert on the right state.
|
2010-07-09 17:07:59 +02:00
|
|
|
if (state() == InferiorRunOk && !m_commandsToRunOnTemporaryBreak.isEmpty())
|
2009-11-02 16:59:57 +01:00
|
|
|
interruptInferiorTemporarily();
|
2009-11-02 14:40:54 +01:00
|
|
|
|
2009-09-15 12:16:21 +02:00
|
|
|
// Continue only if there are no commands wire anymore, so this will
|
2010-08-30 07:52:41 +02:00
|
|
|
// be fully synchronous.
|
2009-05-07 12:18:45 +02:00
|
|
|
// 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.
|
2009-10-08 12:38:01 +02:00
|
|
|
if (m_commandsDoneCallback && m_cookieForToken.isEmpty()) {
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("ALL COMMANDS DONE; INVOKING CALLBACK"));
|
2009-10-08 12:38:01 +02:00
|
|
|
CommandsDoneCallback cont = m_commandsDoneCallback;
|
|
|
|
|
m_commandsDoneCallback = 0;
|
2009-09-21 11:09:38 +02:00
|
|
|
(this->*cont)();
|
|
|
|
|
} else {
|
|
|
|
|
PENDING_DEBUG("MISSING TOKENS: " << m_cookieForToken.keys());
|
2009-05-07 12:18:45 +02:00
|
|
|
}
|
2009-11-03 14:56:27 +01:00
|
|
|
|
|
|
|
|
if (m_cookieForToken.isEmpty())
|
2010-11-16 12:42:57 +01:00
|
|
|
m_commandTimer.stop();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-16 12:41:06 +01:00
|
|
|
bool GdbEngine::acceptsDebuggerCommands() const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2011-02-16 12:41:06 +01:00
|
|
|
return state() == InferiorStopOk
|
|
|
|
|
|| state() == InferiorUnrunnable;
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2011-02-16 12:41:06 +01:00
|
|
|
void GdbEngine::executeDebuggerCommand(const QString &command)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(acceptsDebuggerCommands(), /**/);
|
2009-09-11 15:53:08 +02:00
|
|
|
m_gdbAdapter->write(command.toLatin1() + "\r\n");
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-16 12:41:06 +01:00
|
|
|
// This is called from CoreAdapter and AttachAdapter.
|
2009-09-23 15:28:50 +02:00
|
|
|
void GdbEngine::updateAll()
|
2009-03-02 15:14:12 +01:00
|
|
|
{
|
2010-01-20 15:38:01 +01:00
|
|
|
if (hasPython())
|
|
|
|
|
updateAllPython();
|
|
|
|
|
else
|
|
|
|
|
updateAllClassic();
|
2009-09-30 12:27:03 +02:00
|
|
|
}
|
|
|
|
|
|
2009-09-24 11:16:00 +02:00
|
|
|
void GdbEngine::handleQuerySources(const GdbResponse &response)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-12-09 11:13:20 +01:00
|
|
|
m_sourcesListUpdating = false;
|
|
|
|
|
m_sourcesListOutdated = false;
|
2009-09-24 11:16:00 +02:00
|
|
|
if (response.resultClass == GdbResultDone) {
|
2009-02-17 14:08:49 +01:00
|
|
|
QMap<QString, QString> oldShortToFull = m_shortToFullName;
|
2008-12-02 12:01:29 +01:00
|
|
|
m_shortToFullName.clear();
|
|
|
|
|
m_fullToShortName.clear();
|
|
|
|
|
// "^done,files=[{file="../../../../bin/gdbmacros/gdbmacros.cpp",
|
|
|
|
|
// fullname="/data5/dev/ide/main/bin/gdbmacros/gdbmacros.cpp"},
|
2009-09-24 11:16:00 +02:00
|
|
|
GdbMi files = response.data.findChild("files");
|
2008-12-02 12:01:29 +01:00
|
|
|
foreach (const GdbMi &item, files.children()) {
|
|
|
|
|
GdbMi fullName = item.findChild("fullname");
|
2010-03-05 18:24:32 +01:00
|
|
|
GdbMi fileName = item.findChild("file");
|
|
|
|
|
QString file = QString::fromLocal8Bit(fileName.data());
|
2009-10-22 20:04:59 +02:00
|
|
|
if (fullName.isValid()) {
|
2009-10-28 11:59:57 +01:00
|
|
|
QString full = cleanupFullName(QString::fromLocal8Bit(fullName.data()));
|
2010-03-05 18:24:32 +01:00
|
|
|
m_shortToFullName[file] = full;
|
|
|
|
|
m_fullToShortName[full] = file;
|
|
|
|
|
} else if (fileName.isValid()) {
|
|
|
|
|
m_shortToFullName[file] = tr("<unknown>");
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
2009-02-17 14:08:49 +01:00
|
|
|
if (m_shortToFullName != oldShortToFull)
|
2010-06-16 11:08:54 +02:00
|
|
|
sourceFilesHandler()->setSourceFiles(m_shortToFullName);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-30 14:18:47 +02:00
|
|
|
void GdbEngine::handleExecuteJumpToLine(const GdbResponse &response)
|
|
|
|
|
{
|
|
|
|
|
if (response.resultClass == GdbResultRunning) {
|
2011-02-11 15:00:13 +01:00
|
|
|
doNotifyInferiorRunOk();
|
2010-06-30 14:18:47 +02:00
|
|
|
// All is fine. Waiting for the temporary breakpoint to be hit.
|
|
|
|
|
} else if (response.resultClass == GdbResultDone) {
|
|
|
|
|
// This happens on old gdb. Trigger the effect of a '*stopped'.
|
|
|
|
|
showStatusMessage(tr("Jumped. Stopped"));
|
2010-07-12 15:39:22 +02:00
|
|
|
notifyInferiorSpontaneousStop();
|
2010-06-30 14:18:47 +02:00
|
|
|
handleStop1(response);
|
2009-04-30 15:21:37 +02:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-06-30 14:39:41 +02:00
|
|
|
void GdbEngine::handleExecuteRunToLine(const GdbResponse &response)
|
|
|
|
|
{
|
|
|
|
|
if (response.resultClass == GdbResultRunning) {
|
2011-02-11 15:00:13 +01:00
|
|
|
doNotifyInferiorRunOk();
|
2010-06-30 14:39:41 +02:00
|
|
|
// All is fine. Waiting for the temporary breakpoint to be hit.
|
|
|
|
|
} else if (response.resultClass == GdbResultDone) {
|
|
|
|
|
// This happens on old gdb. Trigger the effect of a '*stopped'.
|
|
|
|
|
// >&"continue\n"
|
|
|
|
|
// >~"Continuing.\n"
|
|
|
|
|
//>~"testArray () at ../simple/app.cpp:241\n"
|
|
|
|
|
//>~"241\t s[1] = \"b\";\n"
|
|
|
|
|
//>122^done
|
2010-12-16 19:06:33 +01:00
|
|
|
gotoLocation(m_targetFrame);
|
2010-06-30 14:39:41 +02:00
|
|
|
showStatusMessage(tr("Target line hit. Stopped"));
|
2010-07-12 15:39:22 +02:00
|
|
|
notifyInferiorSpontaneousStop();
|
2010-06-30 14:39:41 +02:00
|
|
|
handleStop1(response);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-04-30 15:21:37 +02:00
|
|
|
static bool isExitedReason(const QByteArray &reason)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-04-30 15:21:37 +02:00
|
|
|
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
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-09-25 11:06:51 +02:00
|
|
|
#if 0
|
2009-02-18 15:27:56 +01:00
|
|
|
void GdbEngine::handleAqcuiredInferior()
|
|
|
|
|
{
|
2009-05-25 17:19:42 +02:00
|
|
|
// Reverse debugging. FIXME: Should only be used when available.
|
2010-11-10 16:33:11 +01:00
|
|
|
//if (debuggerCore()->boolSetting(EnableReverseDebugging))
|
2010-01-05 16:51:55 +01:00
|
|
|
// postCommand("target response");
|
2009-05-25 17:19:42 +02:00
|
|
|
|
2009-04-07 16:39:17 +02:00
|
|
|
tryLoadDebuggingHelpers();
|
2009-02-18 15:27:56 +01:00
|
|
|
|
2010-06-10 13:12:12 +02:00
|
|
|
# ifndef Q_OS_MAC
|
2009-04-07 16:39:17 +02:00
|
|
|
// intentionally after tryLoadDebuggingHelpers(),
|
2010-01-11 10:22:55 +01:00
|
|
|
// otherwise we'd interrupt solib loading.
|
2010-11-10 16:33:11 +01:00
|
|
|
if (debuggerCore()->boolSetting(AllPluginBreakpoints)) {
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("set auto-solib-add on");
|
|
|
|
|
postCommand("set stop-on-solib-events 0");
|
|
|
|
|
postCommand("sharedlibrary .*");
|
2010-11-10 16:33:11 +01:00
|
|
|
} else if (debuggerCore()->boolSetting(SelectedPluginBreakpoints)) {
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("set auto-solib-add on");
|
|
|
|
|
postCommand("set stop-on-solib-events 1");
|
|
|
|
|
postCommand("sharedlibrary "
|
2009-03-19 10:54:27 +01:00
|
|
|
+ theDebuggerStringSetting(SelectedPluginBreakpointsPattern));
|
2010-11-10 16:33:11 +01:00
|
|
|
} else if (debuggerCore()->boolSetting(NoPluginBreakpoints)) {
|
2009-02-18 15:27:56 +01:00
|
|
|
// should be like that already
|
2010-01-20 17:35:21 +01:00
|
|
|
postCommand("set auto-solib-add off");
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("set stop-on-solib-events 0");
|
2009-02-18 15:27:56 +01:00
|
|
|
}
|
2010-06-10 13:12:12 +02:00
|
|
|
# endif
|
2009-03-03 09:31:13 +01:00
|
|
|
|
2009-09-24 10:08:17 +02:00
|
|
|
// It's nicer to see a bit of the world we live in.
|
2009-10-28 22:08:58 +01:00
|
|
|
reloadModulesInternal();
|
2009-02-18 15:27:56 +01:00
|
|
|
}
|
2009-09-25 11:06:51 +02:00
|
|
|
#endif
|
2009-02-18 15:27:56 +01:00
|
|
|
|
2009-10-16 15:47:54 +02:00
|
|
|
#ifdef Q_OS_UNIX
|
|
|
|
|
# define STOP_SIGNAL "SIGINT"
|
|
|
|
|
# define CROSS_STOP_SIGNAL "SIGTRAP"
|
|
|
|
|
#else
|
|
|
|
|
# define STOP_SIGNAL "SIGTRAP"
|
|
|
|
|
# define CROSS_STOP_SIGNAL "SIGINT"
|
|
|
|
|
#endif
|
|
|
|
|
|
2009-10-09 10:20:42 +02:00
|
|
|
void GdbEngine::handleStopResponse(const GdbMi &data)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-10-30 17:40:29 +01:00
|
|
|
// This is gdb 7+'s initial *stopped in response to attach.
|
|
|
|
|
// For consistency, we just discard it.
|
2010-07-09 17:07:59 +02:00
|
|
|
if (state() == InferiorSetupRequested)
|
2009-10-30 17:40:29 +01:00
|
|
|
return;
|
|
|
|
|
|
2010-07-13 08:41:27 +02:00
|
|
|
if (isDying()) {
|
|
|
|
|
notifyInferiorStopOk();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-21 11:09:38 +02:00
|
|
|
const QByteArray reason = data.findChild("reason").data();
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-02-17 16:25:48 +01:00
|
|
|
if (isExitedReason(reason)) {
|
2010-07-09 17:07:59 +02:00
|
|
|
// // The user triggered a stop, but meanwhile the app simply exited ...
|
|
|
|
|
// QTC_ASSERT(state() == InferiorStopRequested
|
|
|
|
|
// /*|| state() == InferiorStopRequested_Kill*/,
|
|
|
|
|
// qDebug() << state());
|
2009-04-30 15:21:37 +02:00
|
|
|
QString msg;
|
2009-02-17 16:25:48 +01:00
|
|
|
if (reason == "exited") {
|
2010-03-23 14:49:02 +01:00
|
|
|
msg = tr("Application exited with exit code %1")
|
2009-04-30 22:22:37 +02:00
|
|
|
.arg(_(data.findChild("exit-code").toString()));
|
|
|
|
|
} else if (reason == "exited-signalled" || reason == "signal-received") {
|
2010-03-23 14:49:02 +01:00
|
|
|
msg = tr("Application exited after receiving signal %1")
|
2009-04-30 22:22:37 +02:00
|
|
|
.arg(_(data.findChild("signal-name").toString()));
|
2009-04-30 15:21:37 +02:00
|
|
|
} else {
|
2010-03-23 14:49:02 +01:00
|
|
|
msg = tr("Application exited normally");
|
2009-02-17 16:25:48 +01:00
|
|
|
}
|
2009-09-14 11:02:36 +02:00
|
|
|
showStatusMessage(msg);
|
2010-07-09 17:07:59 +02:00
|
|
|
notifyInferiorExited();
|
2009-02-17 16:25:48 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-18 12:30:56 +01:00
|
|
|
const int bkptno = data.findChild("bkptno").data().toInt();
|
|
|
|
|
const GdbMi frame = data.findChild("frame");
|
|
|
|
|
|
2010-11-23 15:54:57 +01:00
|
|
|
const int lineNumber = frame.findChild("line").data().toInt();
|
|
|
|
|
QString fullName = QString::fromUtf8(frame.findChild("fullname").data());
|
2011-01-19 10:48:39 +01:00
|
|
|
|
2010-11-23 15:54:57 +01:00
|
|
|
if (fullName.isEmpty())
|
|
|
|
|
fullName = QString::fromUtf8(frame.findChild("file").data());
|
|
|
|
|
|
2011-01-24 13:37:07 +01:00
|
|
|
if (bkptno && frame.isValid()
|
|
|
|
|
&& !isQmlStepBreakpoint1(bkptno)
|
|
|
|
|
&& !isQmlStepBreakpoint2(bkptno)) {
|
2010-11-23 15:54:57 +01:00
|
|
|
// Use opportunity to update the breakpoint marker position.
|
2010-11-18 15:47:50 +01:00
|
|
|
BreakHandler *handler = breakHandler();
|
2011-01-24 13:37:07 +01:00
|
|
|
//qDebug() << " PROBLEM: " << m_qmlBreakpointNumbers << bkptno
|
|
|
|
|
// << isQmlStepBreakpoint1(bkptno)
|
|
|
|
|
// << isQmlStepBreakpoint2(bkptno)
|
2010-11-18 15:47:50 +01:00
|
|
|
BreakpointId id = handler->findBreakpointByNumber(bkptno);
|
|
|
|
|
const BreakpointResponse &response = handler->response(id);
|
|
|
|
|
QString fileName = response.fileName;
|
|
|
|
|
if (fileName.isEmpty())
|
|
|
|
|
fileName = handler->fileName(id);
|
|
|
|
|
if (fileName.isEmpty())
|
2010-11-23 15:54:57 +01:00
|
|
|
fileName = fullName;
|
|
|
|
|
if (!fileName.isEmpty())
|
2010-11-18 12:30:56 +01:00
|
|
|
handler->setMarkerFileAndLine(id, fileName, lineNumber);
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-24 13:37:07 +01:00
|
|
|
//qDebug() << "BP " << bkptno << data.toString();
|
2010-11-23 15:54:57 +01:00
|
|
|
// Quickly set the location marker.
|
2010-11-24 11:44:43 +01:00
|
|
|
if (lineNumber && !debuggerCore()->boolSetting(OperateByInstruction)
|
2011-01-24 13:37:07 +01:00
|
|
|
&& QFileInfo(fullName).exists()
|
|
|
|
|
&& !isQmlStepBreakpoint1(bkptno)
|
|
|
|
|
&& !isQmlStepBreakpoint2(bkptno))
|
2010-12-16 19:06:33 +01:00
|
|
|
gotoLocation(Location(fullName, lineNumber));
|
2010-11-23 15:54:57 +01:00
|
|
|
|
2009-02-16 18:28:15 +01:00
|
|
|
if (!m_commandsToRunOnTemporaryBreak.isEmpty()) {
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == InferiorStopRequested, qDebug() << state())
|
2010-07-13 08:41:27 +02:00
|
|
|
notifyInferiorStopOk();
|
2009-11-02 16:40:16 +01:00
|
|
|
flushQueuedCommands();
|
2010-07-09 17:07:59 +02:00
|
|
|
if (state() == InferiorStopOk) {
|
2009-11-02 16:59:57 +01:00
|
|
|
QTC_ASSERT(m_commandsDoneCallback == 0, /**/);
|
|
|
|
|
m_commandsDoneCallback = &GdbEngine::autoContinueInferior;
|
|
|
|
|
} else {
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == InferiorShutdownRequested, qDebug() << state())
|
2009-02-16 18:28:15 +01:00
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-09 17:07:59 +02:00
|
|
|
if (state() == InferiorRunOk) {
|
2009-10-09 10:54:03 +02:00
|
|
|
// Stop triggered by a breakpoint or otherwise not directly
|
|
|
|
|
// initiated by the user.
|
2010-07-09 17:07:59 +02:00
|
|
|
notifyInferiorSpontaneousStop();
|
|
|
|
|
} else if (state() == InferiorRunRequested) {
|
2010-10-12 11:52:11 +02:00
|
|
|
// Stop triggered by something like "-exec-step\n"
|
2010-07-09 17:07:59 +02:00
|
|
|
// "&"Cannot access memory at address 0xbfffedd4\n"
|
|
|
|
|
// In this case a proper response 94^error,msg="" will follow and
|
|
|
|
|
// be handled in the result handler.
|
2010-10-12 11:52:11 +02:00
|
|
|
// -- or --
|
|
|
|
|
// *stopped arriving earlier than ^done response to an -exec-step
|
2011-02-11 15:00:13 +01:00
|
|
|
doNotifyInferiorRunOk();
|
2010-10-12 11:52:11 +02:00
|
|
|
notifyInferiorSpontaneousStop();
|
2011-01-24 13:37:07 +01:00
|
|
|
} else if (state() == InferiorStopOk && isQmlStepBreakpoint2(bkptno)) {
|
|
|
|
|
// That's expected.
|
2009-10-09 10:54:03 +02:00
|
|
|
} else {
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == InferiorStopRequested, qDebug() << state());
|
|
|
|
|
notifyInferiorStopOk();
|
2009-10-09 10:54:03 +02:00
|
|
|
}
|
|
|
|
|
|
2010-11-23 12:31:47 +01:00
|
|
|
// FIXME: Replace the #ifdef by the "target" architecture.
|
|
|
|
|
#ifdef Q_OS_LINUX
|
|
|
|
|
if (!m_entryPoint.isEmpty()) {
|
2010-12-17 12:02:23 +01:00
|
|
|
// This is needed as long as we support stock gdb 6.8.
|
2010-11-23 12:31:47 +01:00
|
|
|
if (frame.findChild("addr").data() == m_entryPoint) {
|
|
|
|
|
// There are two expected reasons for getting here:
|
2010-12-17 12:02:23 +01:00
|
|
|
// 1) For some reason, attaching to a stopped process causes *two*
|
|
|
|
|
// SIGSTOPs
|
2010-11-23 12:31:47 +01:00
|
|
|
// when trying to continue (kernel i386 2.6.24-23-ubuntu, gdb 6.8).
|
|
|
|
|
// Interestingly enough, on MacOSX no signal is delivered at all.
|
|
|
|
|
// 2) The explicit tbreak at the entry point we set to query the PID.
|
2010-12-17 12:02:23 +01:00
|
|
|
// Gdb <= 6.8 reports a frame but no reason, 6.8.50+ reports
|
|
|
|
|
// everything.
|
2010-11-23 12:31:47 +01:00
|
|
|
// The case of the user really setting a breakpoint at _start is simply
|
|
|
|
|
// unsupported.
|
|
|
|
|
if (!inferiorPid()) // For programs without -pthread under gdb <= 6.8.
|
|
|
|
|
postCommand("info proc", CB(handleInfoProc));
|
|
|
|
|
continueInferiorInternal();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// We are past the initial stop(s). No need to waste time on further checks.
|
|
|
|
|
m_entryPoint.clear();
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2011-01-24 13:37:07 +01:00
|
|
|
//qDebug() << "STOP: " << m_qmlBreakpointNumbers;
|
|
|
|
|
|
|
|
|
|
if (isQmlStepBreakpoint1(bkptno))
|
2011-01-19 14:34:25 +01:00
|
|
|
return;
|
|
|
|
|
|
2010-11-23 12:31:47 +01:00
|
|
|
handleStop0(data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::handleStop0(const GdbMi &data)
|
|
|
|
|
{
|
2009-11-12 14:49:54 +01:00
|
|
|
#if 0 // See http://vladimir_prus.blogspot.com/2007/12/debugger-stories-pending-breakpoints.html
|
2009-11-10 12:40:23 +01:00
|
|
|
// Due to LD_PRELOADing the dumpers, these events can occur even before
|
|
|
|
|
// reaching the entry point. So handle it before the entry point hacks below.
|
|
|
|
|
if (reason.isEmpty() && m_gdbVersion < 70000 && !m_isMacGdb) {
|
|
|
|
|
// On Linux it reports "Stopped due to shared library event\n", but
|
|
|
|
|
// on Windows it simply forgets about it. Thus, we identify the response
|
|
|
|
|
// based on it having no frame information.
|
|
|
|
|
if (!data.findChild("frame").isValid()) {
|
2009-12-09 11:13:20 +01:00
|
|
|
invalidateSourcesList();
|
2009-11-10 12:40:23 +01:00
|
|
|
// Each stop causes a roundtrip and button flicker, so prevent
|
|
|
|
|
// a flood of useless stops. Will be automatically re-enabled.
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("set stop-on-solib-events 0");
|
2009-11-10 12:40:23 +01:00
|
|
|
#if 0
|
|
|
|
|
// The related code (handleAqcuiredInferior()) is disabled as well.
|
2010-11-10 16:33:11 +01:00
|
|
|
if (debuggerCore()->boolSetting(SelectedPluginBreakpoints)) {
|
2009-11-10 12:40:23 +01:00
|
|
|
QString dataStr = _(data.toString());
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("SHARED LIBRARY EVENT: ") + dataStr);
|
2009-11-10 12:40:23 +01:00
|
|
|
QString pat = theDebuggerStringSetting(SelectedPluginBreakpointsPattern);
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("PATTERN: ") + pat);
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("sharedlibrary " + pat.toLocal8Bit());
|
2009-11-10 12:40:23 +01:00
|
|
|
showStatusMessage(tr("Loading %1...").arg(dataStr));
|
2009-10-30 17:16:56 +01:00
|
|
|
}
|
2009-11-10 12:40:23 +01:00
|
|
|
#endif
|
|
|
|
|
continueInferiorInternal();
|
|
|
|
|
return;
|
2009-10-23 22:17:15 +02:00
|
|
|
}
|
|
|
|
|
}
|
2009-11-12 14:49:54 +01:00
|
|
|
#endif
|
2009-10-23 22:17:15 +02:00
|
|
|
|
2010-11-23 12:31:47 +01:00
|
|
|
const GdbMi frame = data.findChild("frame");
|
|
|
|
|
const QByteArray reason = data.findChild("reason").data();
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2010-05-19 12:00:01 +02:00
|
|
|
// This was seen on XP after removing a breakpoint while running
|
2009-06-19 12:04:21 +02:00
|
|
|
// >945*stopped,reason="signal-received",signal-name="SIGTRAP",
|
2009-02-13 16:55:24 +01:00
|
|
|
// signal-meaning="Trace/breakpoint trap",thread-id="2",
|
|
|
|
|
// frame={addr="0x7c91120f",func="ntdll!DbgUiConnectToDbg",
|
|
|
|
|
// args=[],from="C:\\WINDOWS\\system32\\ntdll.dll"}
|
2009-12-18 16:25:59 +01:00
|
|
|
// also seen on gdb 6.8-symbianelf without qXfer:libraries:read+;
|
|
|
|
|
// FIXME: remote.c parses "loaded" reply. It should be turning
|
|
|
|
|
// that into a TARGET_WAITKIND_LOADED. Does it?
|
|
|
|
|
// The bandaid here has the problem that it breaks for 'next' over a
|
|
|
|
|
// statement that indirectly loads shared libraries
|
2010-01-06 14:16:35 +01:00
|
|
|
// 6.1.2010: Breaks interrupting inferiors, disabled:
|
|
|
|
|
// if (reason == "signal-received"
|
|
|
|
|
// && data.findChild("signal-name").data() == "SIGTRAP") {
|
|
|
|
|
// continueInferiorInternal();
|
|
|
|
|
// return;
|
|
|
|
|
// }
|
2009-02-13 16:55:24 +01:00
|
|
|
|
2010-03-11 11:07:16 +01:00
|
|
|
// Jump over well-known frames.
|
2008-12-02 12:01:29 +01:00
|
|
|
static int stepCounter = 0;
|
2010-11-10 16:33:11 +01:00
|
|
|
if (debuggerCore()->boolSetting(SkipKnownFrames)) {
|
2008-12-02 12:01:29 +01:00
|
|
|
if (reason == "end-stepping-range" || reason == "function-finished") {
|
2010-06-14 18:19:02 +02:00
|
|
|
//showMessage(frame.toString());
|
2009-04-30 15:21:37 +02:00
|
|
|
QString funcName = _(frame.findChild("func").data());
|
|
|
|
|
QString fileName = QString::fromLocal8Bit(frame.findChild("file").data());
|
2008-12-02 12:01:29 +01:00
|
|
|
if (isLeavableFunction(funcName, fileName)) {
|
2010-06-14 18:19:02 +02:00
|
|
|
//showMessage(_("LEAVING ") + funcName);
|
2008-12-02 12:01:29 +01:00
|
|
|
++stepCounter;
|
2010-11-08 15:41:44 +01:00
|
|
|
executeStepOut();
|
2008-12-02 12:01:29 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (isSkippableFunction(funcName, fileName)) {
|
2010-06-14 18:19:02 +02:00
|
|
|
//showMessage(_("SKIPPING ") + funcName);
|
2008-12-02 12:01:29 +01:00
|
|
|
++stepCounter;
|
2010-11-08 15:41:44 +01:00
|
|
|
executeStep();
|
2008-12-02 12:01:29 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
//if (stepCounter)
|
|
|
|
|
// qDebug() << "STEPCOUNTER:" << stepCounter;
|
|
|
|
|
stepCounter = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-19 12:00:01 +02:00
|
|
|
// Show return value if possible, usually with reason "function-finished".
|
|
|
|
|
// *stopped,reason="function-finished",frame={addr="0x080556da",
|
|
|
|
|
// func="testReturnValue",args=[],file="/../app.cpp",
|
|
|
|
|
// fullname="/../app.cpp",line="1611"},gdb-result-var="$1",
|
|
|
|
|
// return-value="{d = 0x808d998}",thread-id="1",stopped-threads="all",
|
|
|
|
|
// core="1"
|
|
|
|
|
GdbMi resultVar = data.findChild("gdb-result-var");
|
|
|
|
|
if (resultVar.isValid())
|
|
|
|
|
m_resultVarName = resultVar.data();
|
|
|
|
|
else
|
|
|
|
|
m_resultVarName.clear();
|
|
|
|
|
|
2009-11-03 14:06:21 +01:00
|
|
|
bool initHelpers = m_debuggingHelperState == DebuggingHelperUninitialized
|
|
|
|
|
|| m_debuggingHelperState == DebuggingHelperLoadTried;
|
2009-10-23 22:52:16 +02:00
|
|
|
// Don't load helpers on stops triggered by signals unless it's
|
|
|
|
|
// an intentional trap.
|
2009-11-13 14:15:08 +01:00
|
|
|
if (initHelpers
|
|
|
|
|
&& m_gdbAdapter->dumperHandling() != AbstractGdbAdapter::DumperLoadedByGdbPreload
|
2009-11-16 11:35:47 +01:00
|
|
|
&& reason == "signal-received") {
|
2009-10-26 13:51:56 +01:00
|
|
|
QByteArray name = data.findChild("signal-name").data();
|
|
|
|
|
if (name != STOP_SIGNAL
|
2010-06-16 11:08:54 +02:00
|
|
|
&& (startParameters().startMode != AttachToRemote
|
2009-10-26 13:51:56 +01:00
|
|
|
|| name != CROSS_STOP_SIGNAL))
|
2009-10-13 14:54:23 +02:00
|
|
|
initHelpers = false;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2010-08-30 07:52:41 +02:00
|
|
|
if (isSynchronous())
|
2009-10-27 16:36:23 +01:00
|
|
|
initHelpers = false;
|
2009-10-23 22:52:16 +02:00
|
|
|
if (initHelpers) {
|
2010-01-20 15:38:01 +01:00
|
|
|
tryLoadDebuggingHelpersClassic();
|
2009-10-23 22:52:16 +02:00
|
|
|
QVariant var = QVariant::fromValue<GdbMi>(data);
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("p 4", CB(handleStop1), var); // dummy
|
2009-10-23 22:52:16 +02:00
|
|
|
} else {
|
|
|
|
|
handleStop1(data);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2009-10-27 15:31:22 +01:00
|
|
|
// Dumper loading is sequenced, as otherwise the display functions
|
|
|
|
|
// will start requesting data without knowing that dumpers are available.
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-09-24 11:16:00 +02:00
|
|
|
void GdbEngine::handleStop1(const GdbResponse &response)
|
2009-09-21 11:09:38 +02:00
|
|
|
{
|
2009-10-09 10:59:56 +02:00
|
|
|
handleStop1(response.cookie.value<GdbMi>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::handleStop1(const GdbMi &data)
|
|
|
|
|
{
|
2010-07-13 08:41:27 +02:00
|
|
|
if (isDying()) {
|
|
|
|
|
qDebug() << "HANDLING STOP WHILE DYING";
|
|
|
|
|
notifyInferiorStopOk();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-13 10:31:03 +01:00
|
|
|
QByteArray reason = data.findChild("reason").data();
|
|
|
|
|
|
2010-09-02 16:46:39 +02:00
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
|
if (startParameters().useTerminal && reason == "signal-received"
|
|
|
|
|
&& data.findChild("signal-name").data() == "SIGTRAP") {
|
|
|
|
|
// Command line start up trap
|
|
|
|
|
showMessage(_("INTERNAL CONTINUE"), LogMisc);
|
2010-01-13 10:31:03 +01:00
|
|
|
continueInferiorInternal();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-09-02 16:46:39 +02:00
|
|
|
#endif
|
2010-01-13 10:31:03 +01:00
|
|
|
|
2010-03-03 18:08:18 +01:00
|
|
|
// This is for display only.
|
2009-10-22 15:46:49 +02:00
|
|
|
if (m_modulesListOutdated)
|
2010-03-03 18:08:18 +01:00
|
|
|
reloadModulesInternal();
|
|
|
|
|
|
|
|
|
|
if (m_breakListOutdated) {
|
|
|
|
|
reloadBreakListInternal();
|
|
|
|
|
} else {
|
|
|
|
|
// Older gdb versions do not produce "library loaded" messages
|
|
|
|
|
// so the breakpoint update is not triggered.
|
2010-06-15 11:42:49 +02:00
|
|
|
if (m_gdbVersion < 70000 && !m_isMacGdb && breakHandler()->size() > 0)
|
2009-12-09 11:13:20 +01:00
|
|
|
reloadBreakListInternal();
|
2010-02-05 15:23:09 +01:00
|
|
|
}
|
2009-11-12 14:49:54 +01:00
|
|
|
|
2010-05-07 15:16:31 +02:00
|
|
|
if (reason == "watchpoint-trigger") {
|
|
|
|
|
// *stopped,reason="watchpoint-trigger",wpt={number="2",exp="*0xbfffed40"},
|
|
|
|
|
// value={old="1",new="0"},frame={addr="0x00451e1b",
|
|
|
|
|
// func="QScopedPointer",args=[{name="this",value="0xbfffed40"},
|
|
|
|
|
// {name="p",value="0x0"}],file="x.h",fullname="/home/.../x.h",line="95"},
|
2010-06-15 11:14:44 +02:00
|
|
|
// thread-id="1",stopped-threads="all",core="2"
|
2010-05-07 15:16:31 +02:00
|
|
|
GdbMi wpt = data.findChild("wpt");
|
2010-11-17 17:16:34 +01:00
|
|
|
const int bpNumber = wpt.findChild("number").data().toInt();
|
|
|
|
|
const BreakpointId id = breakHandler()->findBreakpointByNumber(bpNumber);
|
2010-11-26 12:30:05 +01:00
|
|
|
const quint64 bpAddress = wpt.findChild("exp").data().mid(1).toULongLong(0, 0);
|
2010-11-17 17:16:34 +01:00
|
|
|
showStatusMessage(msgWatchpointTriggered(id, bpNumber, bpAddress));
|
2010-11-23 16:14:19 +01:00
|
|
|
} else if (reason == "breakpoint-hit") {
|
2010-11-17 17:16:34 +01:00
|
|
|
GdbMi gNumber = data.findChild("bkptno"); // 'number' or 'bkptno'?
|
|
|
|
|
if (!gNumber.isValid())
|
|
|
|
|
gNumber = data.findChild("number");
|
|
|
|
|
const int bpNumber = gNumber.data().toInt();
|
|
|
|
|
const QByteArray threadId = data.findChild("thread-id").data();
|
|
|
|
|
const BreakpointId id = breakHandler()->findBreakpointByNumber(bpNumber);
|
2010-12-01 11:27:31 +01:00
|
|
|
showStatusMessage(msgBreakpointTriggered(id, bpNumber, _(threadId)));
|
2010-12-20 16:28:26 +01:00
|
|
|
m_currentThread = threadId;
|
2009-09-21 11:09:38 +02:00
|
|
|
} else {
|
2010-11-17 17:16:34 +01:00
|
|
|
QString reasontr = msgStopped(_(reason));
|
2011-02-23 11:28:20 +01:00
|
|
|
if (reason == "signal-received") {
|
2009-09-21 11:09:38 +02:00
|
|
|
QByteArray name = data.findChild("signal-name").data();
|
2010-03-10 10:32:58 +01:00
|
|
|
QByteArray meaning = data.findChild("signal-meaning").data();
|
2009-10-16 15:47:54 +02:00
|
|
|
// Ignore these as they are showing up regularly when
|
2009-09-21 11:09:38 +02:00
|
|
|
// stopping debugging.
|
2010-11-26 14:35:36 +01:00
|
|
|
if (name == STOP_SIGNAL) {
|
|
|
|
|
showMessage(_(STOP_SIGNAL " CONSIDERED HARMLESS. CONTINUING."));
|
|
|
|
|
} else if (startParameters().startMode == AttachToRemote
|
|
|
|
|
&& name == CROSS_STOP_SIGNAL) {
|
|
|
|
|
showMessage(_(CROSS_STOP_SIGNAL " CONSIDERED HARMLESS. CONTINUING."));
|
|
|
|
|
} else {
|
|
|
|
|
showMessage(_("HANDLING SIGNAL" + name));
|
2011-02-23 11:28:20 +01:00
|
|
|
if (debuggerCore()->boolSetting(UseMessageBoxForSignals))
|
|
|
|
|
showStoppedBySignalMessageBox(_(meaning), _(name));
|
2010-03-10 10:32:58 +01:00
|
|
|
if (!name.isEmpty() && !meaning.isEmpty())
|
2010-11-17 17:16:34 +01:00
|
|
|
reasontr = msgStoppedBySignal(_(meaning), _(name));
|
2009-09-21 11:09:38 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (reason.isEmpty())
|
2010-11-17 17:16:34 +01:00
|
|
|
showStatusMessage(msgStopped());
|
2009-09-21 11:09:38 +02:00
|
|
|
else
|
2010-03-10 10:32:58 +01:00
|
|
|
showStatusMessage(reasontr);
|
2009-09-21 11:09:38 +02:00
|
|
|
}
|
|
|
|
|
|
2010-12-17 13:07:17 +01:00
|
|
|
// Let the event loop run before deciding whether to update the stack.
|
|
|
|
|
m_stackNeeded = true; // setTokenBarrier() might reset this.
|
|
|
|
|
m_currentThreadId = data.findChild("thread-id").data().toInt();
|
|
|
|
|
QTimer::singleShot(0, this, SLOT(handleStop2()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::handleStop2()
|
|
|
|
|
{
|
|
|
|
|
// We are already continuing.
|
|
|
|
|
if (!m_stackNeeded)
|
|
|
|
|
return;
|
|
|
|
|
|
2010-11-24 11:44:43 +01:00
|
|
|
reloadStack(false); // Will trigger register reload.
|
2009-09-23 13:59:55 +02:00
|
|
|
|
|
|
|
|
if (supportsThreads()) {
|
2010-05-17 17:38:31 +02:00
|
|
|
if (m_gdbAdapter->isTrkAdapter()) {
|
2010-02-08 12:39:25 +01:00
|
|
|
m_gdbAdapter->trkReloadThreads();
|
2010-05-19 14:24:00 +02:00
|
|
|
} else if (m_isMacGdb) {
|
2010-09-15 14:16:03 +02:00
|
|
|
postCommand("-thread-list-ids", Discardable,
|
2010-12-17 13:07:17 +01:00
|
|
|
CB(handleThreadListIds), m_currentThreadId);
|
2010-05-17 17:38:31 +02:00
|
|
|
} else {
|
|
|
|
|
// This is only available in gdb 7.1+.
|
2010-09-15 14:16:03 +02:00
|
|
|
postCommand("-thread-info", Discardable,
|
2010-12-17 13:07:17 +01:00
|
|
|
CB(handleThreadInfo), m_currentThreadId);
|
2010-05-17 17:38:31 +02:00
|
|
|
}
|
2009-09-23 13:59:55 +02:00
|
|
|
}
|
2010-12-17 13:07:17 +01:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-11-09 22:11:56 +01:00
|
|
|
void GdbEngine::handleInfoProc(const GdbResponse &response)
|
|
|
|
|
{
|
|
|
|
|
if (response.resultClass == GdbResultDone) {
|
|
|
|
|
static QRegExp re(_("\\bprocess ([0-9]+)\n"));
|
|
|
|
|
QTC_ASSERT(re.isValid(), return);
|
|
|
|
|
if (re.indexIn(_(response.data.findChild("consolestreamoutput").data())) != -1)
|
|
|
|
|
maybeHandleInferiorPidChanged(re.cap(1));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-24 11:16:00 +02:00
|
|
|
void GdbEngine::handleShowVersion(const GdbResponse &response)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("PARSING VERSION: " + response.toString()));
|
2008-12-02 12:01:29 +01:00
|
|
|
if (response.resultClass == GdbResultDone) {
|
|
|
|
|
m_gdbVersion = 100;
|
2009-02-23 16:06:23 +01:00
|
|
|
m_gdbBuildVersion = -1;
|
2009-10-22 11:08:06 +02:00
|
|
|
m_isMacGdb = false;
|
2010-01-21 16:34:59 +01:00
|
|
|
GdbMi version = response.data.findChild("consolestreamoutput");
|
|
|
|
|
QString msg = QString::fromLocal8Bit(version.data());
|
2010-05-27 15:41:52 +02:00
|
|
|
extractGdbVersion(msg,
|
|
|
|
|
&m_gdbVersion, &m_gdbBuildVersion, &m_isMacGdb);
|
|
|
|
|
if (m_gdbVersion > 60500 && m_gdbVersion < 200000)
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("SUPPORTED GDB VERSION ") + msg);
|
2010-05-27 15:41:52 +02:00
|
|
|
else
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("UNSUPPORTED GDB VERSION ") + msg);
|
2010-01-21 16:34:59 +01:00
|
|
|
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("USING GDB VERSION: %1, BUILD: %2%3").arg(m_gdbVersion)
|
2010-01-21 16:34:59 +01:00
|
|
|
.arg(m_gdbBuildVersion).arg(_(m_isMacGdb ? " (APPLE)" : "")));
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-20 15:38:01 +01:00
|
|
|
void GdbEngine::handleHasPython(const GdbResponse &response)
|
2009-10-14 09:41:14 +02:00
|
|
|
{
|
|
|
|
|
if (response.resultClass == GdbResultDone) {
|
2010-01-20 15:38:01 +01:00
|
|
|
m_hasPython = true;
|
2010-06-25 09:07:14 +02:00
|
|
|
GdbMi contents = response.data.findChild("consolestreamoutput");
|
|
|
|
|
GdbMi data;
|
|
|
|
|
data.fromStringMultiple(contents.data());
|
|
|
|
|
const GdbMi dumpers = data.findChild("dumpers");
|
|
|
|
|
foreach (const GdbMi &dumper, dumpers.children()) {
|
2010-09-01 19:02:56 +02:00
|
|
|
QByteArray type = dumper.findChild("type").data();
|
2010-06-25 09:07:14 +02:00
|
|
|
QStringList formats(tr("Raw structure"));
|
2011-02-24 14:26:16 +01:00
|
|
|
foreach (const QByteArray &format,
|
|
|
|
|
dumper.findChild("formats").data().split(',')) {
|
|
|
|
|
if (format == "Normal")
|
|
|
|
|
formats.append(tr("Normal"));
|
|
|
|
|
else if (format == "Displayed")
|
|
|
|
|
formats.append(tr("Displayed"));
|
|
|
|
|
else if (!format.isEmpty())
|
|
|
|
|
formats.append(_(format));
|
|
|
|
|
}
|
2010-06-25 09:07:14 +02:00
|
|
|
watchHandler()->addTypeFormats(type, formats);
|
|
|
|
|
}
|
2010-09-14 12:57:24 +02:00
|
|
|
const GdbMi hasInferiorThreadList = data.findChild("hasInferiorThreadList");
|
|
|
|
|
m_hasInferiorThreadList = (hasInferiorThreadList.data().toInt() != 0);
|
2009-10-14 09:41:14 +02:00
|
|
|
} else {
|
2010-09-23 14:58:45 +02:00
|
|
|
pythonDumpersFailed();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::pythonDumpersFailed()
|
|
|
|
|
{
|
|
|
|
|
m_hasPython = false;
|
|
|
|
|
if (m_gdbAdapter->dumperHandling()
|
|
|
|
|
== AbstractGdbAdapter::DumperLoadedByGdbPreload
|
|
|
|
|
&& checkDebuggingHelpersClassic()) {
|
2010-11-04 18:19:33 +01:00
|
|
|
#ifdef Q_OS_MAC
|
|
|
|
|
const char * const LD_PRELOAD_ENV_VAR = "DYLD_INSERT_LIBRARIES";
|
|
|
|
|
#else
|
|
|
|
|
const char * const LD_PRELOAD_ENV_VAR = "LD_PRELOAD";
|
|
|
|
|
#endif
|
2010-09-23 14:58:45 +02:00
|
|
|
QByteArray cmd = "set environment ";
|
2010-11-04 18:19:33 +01:00
|
|
|
cmd += LD_PRELOAD_ENV_VAR;
|
2010-09-23 14:58:45 +02:00
|
|
|
cmd += ' ';
|
|
|
|
|
cmd += startParameters().startMode == StartRemoteGdb
|
|
|
|
|
? startParameters().remoteDumperLib
|
|
|
|
|
: qtDumperLibraryName().toLocal8Bit();
|
|
|
|
|
postCommand(cmd);
|
|
|
|
|
m_debuggingHelperState = DebuggingHelperLoadTried;
|
2009-10-14 09:41:14 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-09 17:07:59 +02:00
|
|
|
void GdbEngine::showExecutionError(const QString &message)
|
|
|
|
|
{
|
|
|
|
|
showMessageBox(QMessageBox::Critical, tr("Execution Error"),
|
|
|
|
|
tr("Cannot continue debugged process:\n") + message);
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-10 16:17:40 +01:00
|
|
|
void GdbEngine::handleExecuteContinue(const GdbResponse &response)
|
2009-07-21 14:37:31 +02:00
|
|
|
{
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == InferiorRunRequested, qDebug() << state());
|
2009-07-21 14:37:31 +02:00
|
|
|
if (response.resultClass == GdbResultRunning) {
|
2011-02-11 15:00:13 +01:00
|
|
|
doNotifyInferiorRunOk();
|
2010-07-09 17:07:59 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
QByteArray msg = response.data.findChild("msg").data();
|
|
|
|
|
if (msg.startsWith("Cannot find bounds of current function")
|
|
|
|
|
|| msg.startsWith("\"finish\" not meaningful in the outermost frame")) {
|
|
|
|
|
notifyInferiorRunFailed();
|
|
|
|
|
if (isDying())
|
2009-11-02 16:59:57 +01:00
|
|
|
return;
|
2010-07-09 17:07:59 +02:00
|
|
|
if (!m_commandsToRunOnTemporaryBreak.isEmpty())
|
|
|
|
|
flushQueuedCommands();
|
|
|
|
|
QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
|
|
|
|
|
showStatusMessage(tr("Stopped."), 5000);
|
|
|
|
|
reloadStack(true);
|
|
|
|
|
//showStatusMessage(tr("No debug information available. "
|
|
|
|
|
// "Leaving function..."));
|
|
|
|
|
//executeStepOut();
|
|
|
|
|
} else {
|
|
|
|
|
showExecutionError(QString::fromLocal8Bit(msg));
|
|
|
|
|
notifyInferiorIll();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString GdbEngine::fullName(const QString &fileName)
|
|
|
|
|
{
|
|
|
|
|
if (fileName.isEmpty())
|
|
|
|
|
return QString();
|
2010-01-29 15:47:51 +01:00
|
|
|
//QTC_ASSERT(!m_sourcesListOutdated, /* */)
|
2009-10-22 22:54:30 +02:00
|
|
|
QTC_ASSERT(!m_sourcesListUpdating, /* */)
|
2009-10-28 11:59:57 +01:00
|
|
|
return m_shortToFullName.value(fileName, QString());
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-10-28 11:59:57 +01:00
|
|
|
QString GdbEngine::cleanupFullName(const QString &fileName)
|
|
|
|
|
{
|
2010-08-09 15:27:21 +02:00
|
|
|
QString cleanFilePath = fileName;
|
|
|
|
|
#ifdef Q_OS_WIN
|
2009-10-28 11:59:57 +01:00
|
|
|
QTC_ASSERT(!fileName.isEmpty(), return QString())
|
2009-10-29 17:49:37 +01:00
|
|
|
// Gdb on windows often delivers "fullnames" which
|
2010-11-26 12:30:05 +01:00
|
|
|
// (a) have no drive letter and (b) are not normalized.
|
2009-10-29 17:49:37 +01:00
|
|
|
QFileInfo fi(fileName);
|
2010-08-09 15:27:21 +02:00
|
|
|
if (fi.isReadable())
|
|
|
|
|
cleanFilePath = QDir::cleanPath(fi.absoluteFilePath());
|
2009-10-28 11:59:57 +01:00
|
|
|
#endif
|
2010-08-09 15:27:21 +02:00
|
|
|
if (startMode() == StartRemoteGdb) {
|
|
|
|
|
cleanFilePath.replace(0, startParameters().remoteMountPoint.length(),
|
|
|
|
|
startParameters().localMountDir);
|
|
|
|
|
}
|
|
|
|
|
return cleanFilePath;
|
|
|
|
|
}
|
2009-10-28 11:59:57 +01:00
|
|
|
|
2010-07-09 17:07:59 +02:00
|
|
|
void GdbEngine::shutdownInferior()
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(state() == InferiorShutdownRequested, qDebug() << state());
|
|
|
|
|
m_commandsToRunOnTemporaryBreak.clear();
|
|
|
|
|
m_gdbAdapter->shutdownInferior();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::defaultInferiorShutdown(const char *cmd)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == InferiorShutdownRequested, qDebug() << state());
|
|
|
|
|
postCommand(cmd, NeedsStop | LosesChild, CB(handleInferiorShutdown));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::handleInferiorShutdown(const GdbResponse &response)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(state() == InferiorShutdownRequested, qDebug() << state());
|
|
|
|
|
if (response.resultClass == GdbResultDone) {
|
|
|
|
|
notifyInferiorShutdownOk();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
QByteArray ba = response.data.findChild("msg").data();
|
|
|
|
|
if (ba.contains(": No such file or directory.")) {
|
|
|
|
|
// This happens when someone removed the binary behind our back.
|
|
|
|
|
// It is not really an error from a user's point of view.
|
|
|
|
|
showMessage(_("NOTE: " + ba));
|
|
|
|
|
notifyInferiorShutdownOk();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
showMessageBox(QMessageBox::Critical,
|
|
|
|
|
tr("Failed to shut down application"),
|
|
|
|
|
AbstractGdbAdapter::msgInferiorStopFailed(QString::fromLocal8Bit(ba)));
|
|
|
|
|
notifyInferiorShutdownFailed();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::shutdownEngine()
|
|
|
|
|
{
|
|
|
|
|
m_gdbAdapter->shutdownAdapter();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::notifyAdapterShutdownFailed()
|
|
|
|
|
{
|
|
|
|
|
showMessage(_("ADAPTER SHUTDOWN FAILED"));
|
|
|
|
|
QTC_ASSERT(state() == EngineShutdownRequested, qDebug() << state());
|
|
|
|
|
notifyEngineShutdownFailed();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::notifyAdapterShutdownOk()
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(state() == EngineShutdownRequested, qDebug() << state());
|
2010-07-02 12:20:26 +02:00
|
|
|
showMessage(_("INITIATE GDBENGINE SHUTDOWN IN STATE %1, PROC: %2")
|
2010-07-09 17:07:59 +02:00
|
|
|
.arg(lastGoodState()).arg(gdbProc()->state()));
|
|
|
|
|
m_commandsDoneCallback = 0;
|
2010-09-29 10:32:41 +02:00
|
|
|
switch (gdbProc()->state()) {
|
|
|
|
|
case QProcess::Running:
|
2010-09-07 10:59:40 +02:00
|
|
|
postCommand("-gdb-exit", GdbEngine::ExitRequest, CB(handleGdbExit));
|
2010-09-29 10:32:41 +02:00
|
|
|
break;
|
2010-11-26 12:30:05 +01:00
|
|
|
case QProcess::NotRunning:
|
|
|
|
|
// Cannot find executable.
|
2010-09-29 10:32:41 +02:00
|
|
|
notifyEngineShutdownOk();
|
|
|
|
|
break;
|
|
|
|
|
case QProcess::Starting:
|
2010-09-07 10:59:40 +02:00
|
|
|
showMessage(_("GDB NOT REALLY RUNNING; KILLING IT"));
|
|
|
|
|
gdbProc()->kill();
|
|
|
|
|
notifyEngineShutdownFailed();
|
2010-09-29 10:32:41 +02:00
|
|
|
break;
|
2010-09-07 10:59:40 +02:00
|
|
|
}
|
2010-07-09 17:07:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::handleGdbExit(const GdbResponse &response)
|
|
|
|
|
{
|
|
|
|
|
if (response.resultClass == GdbResultExit) {
|
|
|
|
|
showMessage(_("GDB CLAIMS EXIT; WAITING"));
|
|
|
|
|
// Don't set state here, this will be handled in handleGdbFinished()
|
|
|
|
|
//notifyEngineShutdownOk();
|
|
|
|
|
} else {
|
|
|
|
|
QString msg = m_gdbAdapter->msgGdbStopFailed(
|
|
|
|
|
QString::fromLocal8Bit(response.data.findChild("msg").data()));
|
|
|
|
|
qDebug() << (_("GDB WON'T EXIT (%1); KILLING IT").arg(msg));
|
|
|
|
|
showMessage(_("GDB WON'T EXIT (%1); KILLING IT").arg(msg));
|
|
|
|
|
gdbProc()->kill();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-05-07 15:20:44 +02:00
|
|
|
void GdbEngine::detachDebugger()
|
|
|
|
|
{
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
|
|
|
|
|
QTC_ASSERT(startMode() != AttachCore, qDebug() << startMode());
|
2010-07-12 15:39:22 +02:00
|
|
|
postCommand("detach", GdbEngine::ExitRequest, CB(handleDetach));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::handleDetach(const GdbResponse &response)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(response);
|
|
|
|
|
QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
|
2010-07-09 17:07:59 +02:00
|
|
|
notifyInferiorExited();
|
2010-07-02 12:20:26 +02:00
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
int GdbEngine::currentFrame() const
|
|
|
|
|
{
|
2010-06-15 11:42:49 +02:00
|
|
|
return stackHandler()->currentIndex();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-24 16:50:15 +01:00
|
|
|
QString msgNoGdbBinaryForToolChain(const ProjectExplorer::Abi &tc)
|
2010-04-09 08:43:26 +02:00
|
|
|
{
|
2011-02-01 18:36:00 +01:00
|
|
|
return GdbEngine::tr("There is no gdb binary available for binaries in format '%1'")
|
|
|
|
|
.arg(tc.toString());
|
2009-10-08 17:23:27 +02:00
|
|
|
}
|
|
|
|
|
|
2010-06-14 13:17:54 +02:00
|
|
|
AbstractGdbAdapter *GdbEngine::createAdapter()
|
2009-09-29 13:49:35 +02:00
|
|
|
{
|
2010-06-16 11:08:54 +02:00
|
|
|
const DebuggerStartParameters &sp = startParameters();
|
2011-02-23 16:47:08 +01:00
|
|
|
if (sp.toolChainAbi.os() == ProjectExplorer::Abi::SymbianOS) {
|
2011-02-01 18:36:00 +01:00
|
|
|
// FIXME: 1 of 3 testing hacks.
|
2011-01-25 09:18:45 +01:00
|
|
|
if (sp.debugClient == DebuggerStartParameters::DebugClientCoda)
|
2011-02-07 14:26:34 +01:00
|
|
|
return new CodaGdbAdapter(this);
|
2011-01-07 15:47:22 +01:00
|
|
|
else
|
2010-06-16 11:08:54 +02:00
|
|
|
return new TrkGdbAdapter(this);
|
2009-09-29 13:49:35 +02:00
|
|
|
}
|
2010-06-16 11:08:54 +02:00
|
|
|
|
2010-06-14 13:17:54 +02:00
|
|
|
switch (sp.startMode) {
|
2011-01-07 15:47:22 +01:00
|
|
|
case AttachCore:
|
|
|
|
|
return new CoreGdbAdapter(this);
|
|
|
|
|
case AttachToRemote:
|
2011-02-01 18:36:00 +01:00
|
|
|
return new RemoteGdbServerAdapter(this, sp.toolChainAbi);
|
2011-01-07 15:47:22 +01:00
|
|
|
case StartRemoteGdb:
|
|
|
|
|
return new RemotePlainGdbAdapter(this);
|
|
|
|
|
case AttachExternal:
|
|
|
|
|
return new AttachGdbAdapter(this);
|
|
|
|
|
default:
|
|
|
|
|
if (sp.useTerminal)
|
|
|
|
|
return new TermGdbAdapter(this);
|
|
|
|
|
return new LocalPlainGdbAdapter(this);
|
2009-09-29 13:49:35 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-08 18:10:50 +02:00
|
|
|
void GdbEngine::setupEngine()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state());
|
2010-06-16 11:08:54 +02:00
|
|
|
QTC_ASSERT(m_debuggingHelperState == DebuggingHelperUninitialized, /**/);
|
2009-10-20 11:02:16 +02:00
|
|
|
|
2010-07-08 12:41:26 +02:00
|
|
|
if (m_gdbAdapter->dumperHandling() != AbstractGdbAdapter::DumperNotAvailable) {
|
2010-11-10 16:33:11 +01:00
|
|
|
connect(debuggerCore()->action(UseDebuggingHelpers),
|
|
|
|
|
SIGNAL(valueChanged(QVariant)),
|
|
|
|
|
SLOT(setUseDebuggingHelpers(QVariant)));
|
2010-07-08 12:41:26 +02:00
|
|
|
}
|
2009-09-24 10:08:17 +02:00
|
|
|
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == EngineSetupRequested, /**/);
|
2009-09-23 10:14:53 +02:00
|
|
|
m_gdbAdapter->startAdapter();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-02-05 10:23:55 +01:00
|
|
|
unsigned GdbEngine::debuggerCapabilities() const
|
|
|
|
|
{
|
2010-07-13 15:57:34 +02:00
|
|
|
unsigned caps = ReverseSteppingCapability
|
2010-11-26 09:58:34 +01:00
|
|
|
| AutoDerefPointersCapability
|
|
|
|
|
| DisassemblerCapability
|
|
|
|
|
| RegisterCapability
|
|
|
|
|
| ShowMemoryCapability
|
|
|
|
|
| JumpToLineCapability
|
|
|
|
|
| ReloadModuleCapability
|
|
|
|
|
| ReloadModuleSymbolsCapability
|
|
|
|
|
| BreakOnThrowAndCatchCapability
|
2011-02-07 11:04:31 +01:00
|
|
|
| BreakConditionCapability
|
2011-02-21 13:19:42 +01:00
|
|
|
| TracePointCapability
|
2010-06-15 11:14:44 +02:00
|
|
|
| ReturnFromFunctionCapability
|
2010-04-16 16:36:00 +02:00
|
|
|
| CreateFullBacktraceCapability
|
2010-05-07 15:16:31 +02:00
|
|
|
| WatchpointCapability
|
2010-11-26 09:58:34 +01:00
|
|
|
| AddWatcherCapability
|
|
|
|
|
| ShowModuleSymbolsCapability;
|
2010-07-13 15:57:34 +02:00
|
|
|
|
|
|
|
|
if (startParameters().startMode == AttachCore)
|
|
|
|
|
return caps;
|
|
|
|
|
|
|
|
|
|
return caps | SnapshotCapability;
|
2010-02-05 10:23:55 +01:00
|
|
|
}
|
|
|
|
|
|
2009-10-08 13:47:56 +02:00
|
|
|
void GdbEngine::continueInferiorInternal()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-07-20 12:14:59 +02:00
|
|
|
QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
|
|
|
|
|
notifyInferiorRunRequested();
|
|
|
|
|
showStatusMessage(tr("Running requested..."), 5000);
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == InferiorRunRequested, qDebug() << state());
|
2010-03-10 16:17:40 +01:00
|
|
|
postCommand("-exec-continue", RunRequest, CB(handleExecuteContinue));
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-11 15:00:13 +01:00
|
|
|
void GdbEngine::doNotifyInferiorRunOk()
|
|
|
|
|
{
|
|
|
|
|
clearToolTip();
|
|
|
|
|
notifyInferiorRunOk();
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-08 13:47:56 +02:00
|
|
|
void GdbEngine::autoContinueInferior()
|
|
|
|
|
{
|
2010-11-30 08:59:22 +01:00
|
|
|
resetLocation();
|
2009-10-08 13:47:56 +02:00
|
|
|
continueInferiorInternal();
|
|
|
|
|
showStatusMessage(tr("Continuing after temporary stop..."), 1000);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::continueInferior()
|
|
|
|
|
{
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
|
2009-10-20 14:08:59 +02:00
|
|
|
setTokenBarrier();
|
2009-10-08 13:47:56 +02:00
|
|
|
continueInferiorInternal();
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-10 16:17:40 +01:00
|
|
|
void GdbEngine::executeStep()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
|
2008-12-02 12:01:29 +01:00
|
|
|
setTokenBarrier();
|
2010-07-09 17:07:59 +02:00
|
|
|
notifyInferiorRunRequested();
|
2009-09-25 08:35:31 +02:00
|
|
|
showStatusMessage(tr("Step requested..."), 5000);
|
2010-06-15 11:42:49 +02:00
|
|
|
if (m_gdbAdapter->isTrkAdapter() && stackHandler()->stackSize() > 0)
|
2010-09-21 15:12:33 +02:00
|
|
|
postCommand("sal step,0x" + QByteArray::number(stackHandler()->topAddress(), 16));
|
2010-06-16 11:08:54 +02:00
|
|
|
if (isReverseDebugging())
|
2010-03-11 11:07:16 +01:00
|
|
|
postCommand("reverse-step", RunRequest, CB(handleExecuteStep));
|
2009-05-25 17:19:42 +02:00
|
|
|
else
|
2010-03-10 16:17:40 +01:00
|
|
|
postCommand("-exec-step", RunRequest, CB(handleExecuteStep));
|
2010-02-01 16:14:57 +01:00
|
|
|
}
|
|
|
|
|
|
2010-03-10 16:17:40 +01:00
|
|
|
void GdbEngine::handleExecuteStep(const GdbResponse &response)
|
2010-02-01 16:14:57 +01:00
|
|
|
{
|
2010-10-12 11:52:11 +02:00
|
|
|
if (response.resultClass == GdbResultDone) {
|
|
|
|
|
// Step was finishing too quick, and a '*stopped' messages should
|
2011-02-23 15:15:04 +01:00
|
|
|
// have preceded it, so just ignore this result.
|
2010-10-12 11:52:11 +02:00
|
|
|
QTC_ASSERT(state() == InferiorStopOk, /**/);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == InferiorRunRequested, qDebug() << state());
|
2010-02-01 16:14:57 +01:00
|
|
|
if (response.resultClass == GdbResultRunning) {
|
2011-02-11 15:00:13 +01:00
|
|
|
doNotifyInferiorRunOk();
|
2010-07-09 17:07:59 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
QByteArray msg = response.data.findChild("msg").data();
|
|
|
|
|
if (msg.startsWith("Cannot find bounds of current function")) {
|
|
|
|
|
notifyInferiorRunFailed();
|
|
|
|
|
if (isDying())
|
2010-02-01 16:14:57 +01:00
|
|
|
return;
|
2010-07-09 17:07:59 +02:00
|
|
|
if (!m_commandsToRunOnTemporaryBreak.isEmpty())
|
|
|
|
|
flushQueuedCommands();
|
|
|
|
|
executeStepI(); // Fall back to instruction-wise stepping.
|
|
|
|
|
} else {
|
|
|
|
|
showExecutionError(QString::fromLocal8Bit(msg));
|
|
|
|
|
notifyInferiorIll();
|
2010-02-01 16:14:57 +01:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-03-10 16:17:40 +01:00
|
|
|
void GdbEngine::executeStepI()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
|
2008-12-02 12:01:29 +01:00
|
|
|
setTokenBarrier();
|
2010-07-09 17:07:59 +02:00
|
|
|
notifyInferiorRunRequested();
|
2009-09-25 08:35:31 +02:00
|
|
|
showStatusMessage(tr("Step by instruction requested..."), 5000);
|
2010-06-16 11:08:54 +02:00
|
|
|
if (isReverseDebugging())
|
2010-03-11 11:07:16 +01:00
|
|
|
postCommand("reverse-stepi", RunRequest, CB(handleExecuteContinue));
|
2009-05-25 17:19:42 +02:00
|
|
|
else
|
2010-03-10 16:17:40 +01:00
|
|
|
postCommand("-exec-step-instruction", RunRequest, CB(handleExecuteContinue));
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-03-10 16:17:40 +01:00
|
|
|
void GdbEngine::executeStepOut()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
|
2010-06-01 14:35:51 +02:00
|
|
|
postCommand("-stack-select-frame 0");
|
2008-12-02 12:01:29 +01:00
|
|
|
setTokenBarrier();
|
2010-07-09 17:07:59 +02:00
|
|
|
notifyInferiorRunRequested();
|
2009-09-25 08:35:31 +02:00
|
|
|
showStatusMessage(tr("Finish function requested..."), 5000);
|
2010-03-10 16:17:40 +01:00
|
|
|
postCommand("-exec-finish", RunRequest, CB(handleExecuteContinue));
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-03-10 16:17:40 +01:00
|
|
|
void GdbEngine::executeNext()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
|
2008-12-02 12:01:29 +01:00
|
|
|
setTokenBarrier();
|
2010-07-09 17:07:59 +02:00
|
|
|
notifyInferiorRunRequested();
|
2009-09-25 08:35:31 +02:00
|
|
|
showStatusMessage(tr("Step next requested..."), 5000);
|
2010-06-15 11:42:49 +02:00
|
|
|
if (m_gdbAdapter->isTrkAdapter() && stackHandler()->stackSize() > 0)
|
2010-09-21 15:12:33 +02:00
|
|
|
postCommand("sal next,0x" + QByteArray::number(stackHandler()->topAddress(), 16));
|
2010-06-16 11:08:54 +02:00
|
|
|
if (isReverseDebugging())
|
2010-03-11 11:07:16 +01:00
|
|
|
postCommand("reverse-next", RunRequest, CB(handleExecuteNext));
|
2010-01-05 16:51:55 +01:00
|
|
|
else
|
2010-03-10 16:17:40 +01:00
|
|
|
postCommand("-exec-next", RunRequest, CB(handleExecuteNext));
|
2010-02-01 16:14:57 +01:00
|
|
|
}
|
|
|
|
|
|
2010-03-10 16:17:40 +01:00
|
|
|
void GdbEngine::handleExecuteNext(const GdbResponse &response)
|
2010-02-01 16:14:57 +01:00
|
|
|
{
|
2010-10-12 11:52:11 +02:00
|
|
|
if (response.resultClass == GdbResultDone) {
|
|
|
|
|
// Step was finishing too quick, and a '*stopped' messages should
|
2011-02-23 15:15:04 +01:00
|
|
|
// have preceded it, so just ignore this result.
|
2010-10-12 11:52:11 +02:00
|
|
|
QTC_ASSERT(state() == InferiorStopOk, /**/);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == InferiorRunRequested, qDebug() << state());
|
2010-02-01 16:14:57 +01:00
|
|
|
if (response.resultClass == GdbResultRunning) {
|
2011-02-11 15:00:13 +01:00
|
|
|
doNotifyInferiorRunOk();
|
2010-07-09 17:07:59 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
|
|
|
|
|
QByteArray msg = response.data.findChild("msg").data();
|
|
|
|
|
if (msg.startsWith("Cannot find bounds of current function")) {
|
|
|
|
|
if (!m_commandsToRunOnTemporaryBreak.isEmpty())
|
|
|
|
|
flushQueuedCommands();
|
|
|
|
|
notifyInferiorRunFailed();
|
|
|
|
|
if (!isDying())
|
2010-03-10 16:17:40 +01:00
|
|
|
executeNextI(); // Fall back to instruction-wise stepping.
|
2010-07-09 17:07:59 +02:00
|
|
|
} else {
|
|
|
|
|
showMessageBox(QMessageBox::Critical, tr("Execution Error"),
|
|
|
|
|
tr("Cannot continue debugged process:\n") + QString::fromLocal8Bit(msg));
|
|
|
|
|
notifyInferiorIll();
|
2010-02-01 16:14:57 +01:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-03-10 16:17:40 +01:00
|
|
|
void GdbEngine::executeNextI()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
|
2008-12-02 12:01:29 +01:00
|
|
|
setTokenBarrier();
|
2010-07-09 17:07:59 +02:00
|
|
|
notifyInferiorRunRequested();
|
2009-09-25 08:35:31 +02:00
|
|
|
showStatusMessage(tr("Step next instruction requested..."), 5000);
|
2010-06-16 11:08:54 +02:00
|
|
|
if (isReverseDebugging())
|
2010-03-11 11:07:16 +01:00
|
|
|
postCommand("reverse-nexti", RunRequest, CB(handleExecuteContinue));
|
2009-05-25 17:19:42 +02:00
|
|
|
else
|
2010-03-10 16:17:40 +01:00
|
|
|
postCommand("-exec-next-instruction", RunRequest, CB(handleExecuteContinue));
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-23 10:16:11 +01:00
|
|
|
static QByteArray addressSpec(quint64 address)
|
|
|
|
|
{
|
|
|
|
|
return "*0x" + QByteArray::number(address, 16);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::executeRunToLine(const ContextData &data)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
|
2008-12-02 12:01:29 +01:00
|
|
|
setTokenBarrier();
|
2010-07-09 17:07:59 +02:00
|
|
|
notifyInferiorRunRequested();
|
2011-02-23 10:16:11 +01:00
|
|
|
showStatusMessage(tr("Run to line %1 requested...").arg(data.lineNumber), 5000);
|
2010-03-29 15:58:52 +02:00
|
|
|
#if 1
|
2011-02-23 10:16:11 +01:00
|
|
|
m_targetFrame.file = data.fileName;
|
|
|
|
|
m_targetFrame.line = data.lineNumber;
|
|
|
|
|
QByteArray loc;
|
|
|
|
|
if (data.address)
|
|
|
|
|
loc = addressSpec(data.address);
|
|
|
|
|
else
|
|
|
|
|
loc = '"' + breakLocation(data.fileName).toLocal8Bit() + '"' + ':'
|
|
|
|
|
+ QByteArray::number(data.lineNumber);
|
2010-03-29 15:58:52 +02:00
|
|
|
postCommand("tbreak " + loc);
|
2010-06-30 14:39:41 +02:00
|
|
|
postCommand("continue", RunRequest, CB(handleExecuteRunToLine));
|
2010-03-29 15:58:52 +02:00
|
|
|
#else
|
|
|
|
|
// Seems to jump to unpredicatable places. Observed in the manual
|
|
|
|
|
// tests in the Foo::Foo() constructor with both gdb 6.8 and 7.1.
|
2010-01-05 16:51:55 +01:00
|
|
|
QByteArray args = '"' + breakLocation(fileName).toLocal8Bit() + '"' + ':'
|
|
|
|
|
+ QByteArray::number(lineNumber);
|
2010-03-10 16:17:40 +01:00
|
|
|
postCommand("-exec-until " + args, RunRequest, CB(handleExecuteContinue));
|
2010-03-29 15:58:52 +02:00
|
|
|
#endif
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-03-10 16:17:40 +01:00
|
|
|
void GdbEngine::executeRunToFunction(const QString &functionName)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
|
2008-12-02 12:01:29 +01:00
|
|
|
setTokenBarrier();
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("-break-insert -t " + functionName.toLatin1());
|
2010-07-09 17:07:59 +02:00
|
|
|
showStatusMessage(tr("Run to function %1 requested...").arg(functionName), 5000);
|
2009-10-20 14:08:59 +02:00
|
|
|
continueInferiorInternal();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-23 10:16:11 +01:00
|
|
|
void GdbEngine::executeJumpToLine(const ContextData &data)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
|
2011-02-23 10:16:11 +01:00
|
|
|
QByteArray loc;
|
|
|
|
|
if (data.address)
|
|
|
|
|
loc = addressSpec(data.address);
|
|
|
|
|
else
|
|
|
|
|
loc = '"' + breakLocation(data.fileName).toLocal8Bit() + '"' + ':'
|
|
|
|
|
+ QByteArray::number(data.lineNumber);
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("tbreak " + loc);
|
2010-07-09 17:07:59 +02:00
|
|
|
notifyInferiorRunRequested();
|
2010-06-30 14:18:47 +02:00
|
|
|
postCommand("jump " + loc, RunRequest, CB(handleExecuteJumpToLine));
|
2008-12-02 12:01:29 +01:00
|
|
|
// will produce something like
|
2009-10-22 22:54:30 +02:00
|
|
|
// &"jump \"/home/apoenitz/dev/work/test1/test1.cpp\":242"
|
2008-12-02 12:01:29 +01:00
|
|
|
// ~"Continuing at 0x4058f3."
|
|
|
|
|
// ~"run1 (argc=1, argv=0x7fffbf1f5538) at test1.cpp:242"
|
|
|
|
|
// ~"242\t x *= 2;"
|
|
|
|
|
// 23^done"
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-10 16:17:40 +01:00
|
|
|
void GdbEngine::executeReturn()
|
2010-02-15 16:02:41 +01:00
|
|
|
{
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
|
2010-02-15 16:02:41 +01:00
|
|
|
setTokenBarrier();
|
2010-07-09 17:07:59 +02:00
|
|
|
notifyInferiorRunRequested();
|
2010-02-15 16:02:41 +01:00
|
|
|
showStatusMessage(tr("Immediate return from function requested..."), 5000);
|
2010-03-10 16:17:40 +01:00
|
|
|
postCommand("-exec-finish", RunRequest, CB(handleExecuteReturn));
|
2010-02-15 16:02:41 +01:00
|
|
|
}
|
|
|
|
|
|
2010-03-10 16:17:40 +01:00
|
|
|
void GdbEngine::handleExecuteReturn(const GdbResponse &response)
|
2010-02-15 16:02:41 +01:00
|
|
|
{
|
|
|
|
|
if (response.resultClass == GdbResultDone) {
|
2010-07-09 17:07:59 +02:00
|
|
|
notifyInferiorStopOk();
|
2010-02-15 16:02:41 +01:00
|
|
|
updateAll();
|
2010-07-09 17:07:59 +02:00
|
|
|
return;
|
2010-02-15 16:02:41 +01:00
|
|
|
}
|
2010-07-09 17:07:59 +02:00
|
|
|
notifyInferiorRunFailed();
|
2010-02-15 16:02:41 +01:00
|
|
|
}
|
2010-07-09 17:07:59 +02:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
/*!
|
2011-01-26 17:22:25 +01:00
|
|
|
\fn void Debugger::Internal::GdbEngine::setTokenBarrier()
|
2009-02-18 13:50:55 +01:00
|
|
|
\brief Discard the results of all pending watch-updating commands.
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-02-18 10:28:31 +01:00
|
|
|
This method is called at the beginning of all step/next/finish etc.
|
2008-12-02 12:01:29 +01:00
|
|
|
debugger functions.
|
2009-02-18 13:50:55 +01:00
|
|
|
If non-watch-updating commands with call-backs are still in the pipe,
|
|
|
|
|
it will complain.
|
2008-12-02 12:01:29 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void GdbEngine::setTokenBarrier()
|
|
|
|
|
{
|
2009-05-04 18:30:22 +02:00
|
|
|
foreach (const GdbCommand &cookie, m_cookieForToken) {
|
|
|
|
|
QTC_ASSERT(!cookie.callback || (cookie.flags & Discardable),
|
2010-03-10 17:39:08 +01:00
|
|
|
qDebug() << "CMD:" << cookie.command
|
|
|
|
|
<< " FLAGS:" << cookie.flags
|
|
|
|
|
<< " CALLBACK:" << cookie.callbackName;
|
2009-04-14 12:14:01 +02:00
|
|
|
return
|
|
|
|
|
);
|
|
|
|
|
}
|
2009-03-04 10:11:42 +01:00
|
|
|
PENDING_DEBUG("\n--- token barrier ---\n");
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("--- token barrier ---"), LogMiscInput);
|
2010-11-10 16:33:11 +01:00
|
|
|
if (debuggerCore()->boolSetting(LogTimeStamps))
|
2010-09-22 16:20:08 +02:00
|
|
|
showMessage(LogWindow::logTimeStamp(), LogMiscInput);
|
2008-12-02 12:01:29 +01:00
|
|
|
m_oldestAcceptableToken = currentToken();
|
2010-12-17 13:07:17 +01:00
|
|
|
m_stackNeeded = false;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// Breakpoint specific stuff
|
|
|
|
|
//
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2010-11-16 13:52:21 +01:00
|
|
|
void GdbEngine::updateBreakpointDataFromOutput(BreakpointId id, const GdbMi &bkpt)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-11-16 13:52:21 +01:00
|
|
|
QTC_ASSERT(bkpt.isValid(), return);
|
2011-01-24 13:37:07 +01:00
|
|
|
|
2010-11-16 13:52:21 +01:00
|
|
|
BreakpointResponse response = breakHandler()->response(id);
|
2010-11-10 16:33:11 +01:00
|
|
|
|
2010-11-15 17:04:29 +01:00
|
|
|
response.multiple = false;
|
|
|
|
|
response.enabled = true;
|
2010-11-18 14:57:00 +01:00
|
|
|
response.pending = false;
|
2010-11-15 17:04:29 +01:00
|
|
|
response.condition.clear();
|
2009-10-23 18:45:01 +02:00
|
|
|
QByteArray file, fullName;
|
2008-12-02 12:01:29 +01:00
|
|
|
foreach (const GdbMi &child, bkpt.children()) {
|
|
|
|
|
if (child.hasName("number")) {
|
2010-11-15 17:04:29 +01:00
|
|
|
response.number = child.data().toInt();
|
2008-12-02 12:01:29 +01:00
|
|
|
} else if (child.hasName("func")) {
|
2010-11-15 17:04:29 +01:00
|
|
|
response.functionName = _(child.data());
|
2008-12-02 12:01:29 +01:00
|
|
|
} else if (child.hasName("addr")) {
|
2011-03-02 13:37:29 +01:00
|
|
|
// <MULTIPLE> happens in constructors, inline functions, and
|
|
|
|
|
// at other places like 'foreach' lines. In this case there are
|
|
|
|
|
// fields named "addr" in the response and/or the address
|
|
|
|
|
// is called <MULTIPLE>.
|
|
|
|
|
//qDebug() << "ADDR: " << child.data() << (child.data() == "<MULTIPLE>");
|
2010-09-21 14:26:45 +02:00
|
|
|
if (child.data().startsWith("0x")) {
|
2010-11-15 17:04:29 +01:00
|
|
|
response.address = child.data().mid(2).toULongLong(0, 16);
|
2010-09-21 14:26:45 +02:00
|
|
|
} else {
|
2010-11-16 11:55:48 +01:00
|
|
|
response.extra = child.data();
|
2010-09-21 14:26:45 +02:00
|
|
|
if (child.data() == "<MULTIPLE>")
|
2010-11-15 17:04:29 +01:00
|
|
|
response.multiple = true;
|
2010-09-21 14:26:45 +02:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
} else if (child.hasName("file")) {
|
2009-10-23 18:45:01 +02:00
|
|
|
file = child.data();
|
2008-12-02 12:01:29 +01:00
|
|
|
} else if (child.hasName("fullname")) {
|
2009-10-23 18:45:01 +02:00
|
|
|
fullName = child.data();
|
2008-12-02 12:01:29 +01:00
|
|
|
} else if (child.hasName("line")) {
|
2011-01-05 19:30:24 +01:00
|
|
|
// The line numbers here are the uncorrected ones. So don't
|
|
|
|
|
// change it if we know better already.
|
|
|
|
|
if (response.correctedLineNumber == 0)
|
|
|
|
|
response.lineNumber = child.data().toInt();
|
2008-12-02 12:01:29 +01:00
|
|
|
} else if (child.hasName("cond")) {
|
|
|
|
|
// gdb 6.3 likes to "rewrite" conditions. Just accept that fact.
|
2010-11-26 12:30:05 +01:00
|
|
|
response.condition = child.data();
|
2009-06-10 10:30:48 +02:00
|
|
|
} else if (child.hasName("enabled")) {
|
2010-11-15 17:04:29 +01:00
|
|
|
response.enabled = (child.data() == "y");
|
2009-10-01 12:06:15 +02:00
|
|
|
} else if (child.hasName("pending")) {
|
2009-10-23 18:45:01 +02:00
|
|
|
// Any content here would be interesting only if we did accept
|
|
|
|
|
// spontaneously appearing breakpoints (user using gdb commands).
|
2010-11-26 12:30:05 +01:00
|
|
|
response.pending = true;
|
2009-10-01 12:06:15 +02:00
|
|
|
} else if (child.hasName("at")) {
|
2010-11-26 12:30:05 +01:00
|
|
|
// Happens with gdb 6.4 symbianelf.
|
2009-10-01 12:06:15 +02:00
|
|
|
QByteArray ba = child.data();
|
|
|
|
|
if (ba.startsWith('<') && ba.endsWith('>'))
|
|
|
|
|
ba = ba.mid(1, ba.size() - 2);
|
2010-11-15 17:04:29 +01:00
|
|
|
response.functionName = _(ba);
|
2010-04-29 18:36:18 +02:00
|
|
|
} else if (child.hasName("thread")) {
|
2010-11-30 13:39:01 +01:00
|
|
|
response.threadSpec = child.data().toInt();
|
2010-05-10 12:34:52 +02:00
|
|
|
} else if (child.hasName("type")) {
|
2010-12-16 17:58:43 +01:00
|
|
|
// "breakpoint", "hw breakpoint", "tracepoint"
|
|
|
|
|
if (child.data().contains("tracepoint"))
|
|
|
|
|
response.tracepoint = true;
|
|
|
|
|
else if (!child.data().contains("reakpoint"))
|
2010-11-26 12:30:05 +01:00
|
|
|
response.type = Watchpoint;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2010-05-10 12:34:52 +02:00
|
|
|
// This field is not present. Contents needs to be parsed from
|
|
|
|
|
// the plain "ignore" response.
|
|
|
|
|
//else if (child.hasName("ignore"))
|
2010-11-26 12:30:05 +01:00
|
|
|
// response.ignoreCount = child.data();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-10-23 18:45:01 +02:00
|
|
|
QString name;
|
|
|
|
|
if (!fullName.isEmpty()) {
|
2009-10-28 11:59:57 +01:00
|
|
|
name = cleanupFullName(QFile::decodeName(fullName));
|
2010-11-15 17:04:29 +01:00
|
|
|
response.fileName = name;
|
2010-11-10 16:33:11 +01:00
|
|
|
//if (data->markerFileName().isEmpty())
|
|
|
|
|
// data->setMarkerFileName(name);
|
2009-10-23 18:45:01 +02:00
|
|
|
} else {
|
|
|
|
|
name = QFile::decodeName(file);
|
2010-02-01 17:49:11 +01:00
|
|
|
// Use fullName() once we have a mapping which is more complete than
|
|
|
|
|
// gdb's own. No point in assigning markerFileName for now.
|
2009-10-23 18:45:01 +02:00
|
|
|
}
|
2010-02-01 17:49:11 +01:00
|
|
|
if (!name.isEmpty())
|
2010-11-15 17:04:29 +01:00
|
|
|
response.fileName = name;
|
2010-11-10 16:33:11 +01:00
|
|
|
|
|
|
|
|
breakHandler()->setResponse(id, response);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-10-22 22:54:30 +02:00
|
|
|
QString GdbEngine::breakLocation(const QString &file) const
|
|
|
|
|
{
|
2010-03-03 18:08:18 +01:00
|
|
|
//QTC_ASSERT(!m_breakListOutdated, /* */)
|
2009-10-22 22:54:30 +02:00
|
|
|
QString where = m_fullToShortName.value(file);
|
|
|
|
|
if (where.isEmpty())
|
|
|
|
|
return QFileInfo(file).fileName();
|
|
|
|
|
return where;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-11-10 16:33:11 +01:00
|
|
|
QByteArray GdbEngine::breakpointLocation(BreakpointId id)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-11-10 16:33:11 +01:00
|
|
|
BreakHandler *handler = breakHandler();
|
2010-11-16 11:06:09 +01:00
|
|
|
const BreakpointParameters &data = handler->breakpointData(id);
|
|
|
|
|
QTC_ASSERT(data.type != UnknownType, return QByteArray());
|
2010-11-16 10:23:20 +01:00
|
|
|
// FIXME: Non-GCC-runtime
|
2010-11-16 11:06:09 +01:00
|
|
|
if (data.type == BreakpointAtThrow)
|
2010-11-16 10:23:20 +01:00
|
|
|
return "__cxa_throw";
|
2010-11-16 11:06:09 +01:00
|
|
|
if (data.type == BreakpointAtCatch)
|
2010-11-16 10:23:20 +01:00
|
|
|
return "__cxa_begin_catch";
|
2010-11-16 11:06:09 +01:00
|
|
|
if (data.type == BreakpointAtMain)
|
2010-11-16 10:23:20 +01:00
|
|
|
#ifdef Q_OS_WIN
|
2010-11-25 14:33:13 +01:00
|
|
|
// FIXME: Should be target specific.
|
2010-11-16 10:23:20 +01:00
|
|
|
return "qMain";
|
|
|
|
|
#else
|
|
|
|
|
return "main";
|
|
|
|
|
#endif
|
2010-11-25 14:33:13 +01:00
|
|
|
if (data.type == BreakpointByFunction)
|
|
|
|
|
return data.functionName.toUtf8();
|
|
|
|
|
if (data.type == BreakpointByAddress)
|
|
|
|
|
return addressSpec(data.address);
|
|
|
|
|
|
2011-02-17 13:00:11 +01:00
|
|
|
const QString fileName = data.pathUsage == BreakpointUseFullPath
|
2010-11-16 12:17:00 +01:00
|
|
|
? data.fileName : breakLocation(data.fileName);
|
2010-03-04 15:42:09 +01:00
|
|
|
// The argument is simply a C-quoted version of the argument to the
|
|
|
|
|
// non-MI "break" command, including the "original" quoting it wants.
|
2010-11-10 16:33:11 +01:00
|
|
|
return "\"\\\"" + GdbMi::escapeCString(fileName).toLocal8Bit() + "\\\":"
|
2010-11-25 14:33:13 +01:00
|
|
|
+ QByteArray::number(data.lineNumber) + '"';
|
2010-03-04 15:42:09 +01:00
|
|
|
}
|
|
|
|
|
|
2010-05-07 15:16:31 +02:00
|
|
|
void GdbEngine::handleWatchInsert(const GdbResponse &response)
|
|
|
|
|
{
|
2010-11-10 16:33:11 +01:00
|
|
|
const int id = response.cookie.toInt();
|
2010-05-07 15:16:31 +02:00
|
|
|
if (response.resultClass == GdbResultDone) {
|
|
|
|
|
// "Hardware watchpoint 2: *0xbfffed40\n"
|
|
|
|
|
QByteArray ba = response.data.findChild("consolestreamoutput").data();
|
2010-11-26 12:30:05 +01:00
|
|
|
if (ba.startsWith("Hardware watchpoint ")
|
|
|
|
|
|| ba.startsWith("Watchpoint ")) {
|
|
|
|
|
const int end = ba.indexOf(':');
|
|
|
|
|
const int begin = ba.lastIndexOf(' ', end) + 1;
|
|
|
|
|
const QByteArray address = ba.mid(end + 3).trimmed();
|
2010-11-30 12:47:53 +01:00
|
|
|
BreakHandler *handler = breakHandler();
|
|
|
|
|
BreakpointResponse response = handler->response(id);
|
2010-11-26 12:30:05 +01:00
|
|
|
response.number = ba.mid(begin, end - begin).toInt();
|
|
|
|
|
response.address = address.toULongLong(0, 0);
|
2010-11-30 12:47:53 +01:00
|
|
|
handler->setResponse(id, response);
|
|
|
|
|
QTC_ASSERT(!handler->needsChange(id), /**/);
|
|
|
|
|
handler->notifyBreakpointInsertOk(id);
|
2010-05-07 15:16:31 +02:00
|
|
|
} else {
|
2010-11-26 12:30:05 +01:00
|
|
|
showMessage(_("CANNOT PARSE WATCHPOINT FROM " + ba));
|
2010-05-07 15:16:31 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-18 13:38:58 +01:00
|
|
|
void GdbEngine::attemptAdjustBreakpointLocation(BreakpointId id)
|
|
|
|
|
{
|
|
|
|
|
if (!debuggerCore()->boolSetting(AdjustBreakpointLocations))
|
|
|
|
|
return;
|
|
|
|
|
BreakpointResponse response = breakHandler()->response(id);
|
|
|
|
|
if (response.address == 0 || response.correctedLineNumber != 0)
|
|
|
|
|
return;
|
|
|
|
|
// Prevent endless loop.
|
|
|
|
|
response.correctedLineNumber = -1;
|
|
|
|
|
breakHandler()->setResponse(id, response);
|
|
|
|
|
postCommand("info line *0x" + QByteArray::number(response.address, 16),
|
|
|
|
|
NeedsStop | RebuildBreakpointModel,
|
|
|
|
|
CB(handleInfoLine), id);
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-04 15:42:09 +01:00
|
|
|
void GdbEngine::handleBreakInsert1(const GdbResponse &response)
|
|
|
|
|
{
|
2010-12-16 17:58:43 +01:00
|
|
|
BreakHandler *handler = breakHandler();
|
2010-11-25 14:33:13 +01:00
|
|
|
BreakpointId id(response.cookie.toInt());
|
2010-03-04 15:42:09 +01:00
|
|
|
if (response.resultClass == GdbResultDone) {
|
|
|
|
|
// Interesting only on Mac?
|
|
|
|
|
GdbMi bkpt = response.data.findChild("bkpt");
|
2010-11-16 13:52:21 +01:00
|
|
|
updateBreakpointDataFromOutput(id, bkpt);
|
2010-11-30 12:47:53 +01:00
|
|
|
if (handler->needsChange(id)) {
|
|
|
|
|
handler->notifyBreakpointChangeAfterInsertNeeded(id);
|
|
|
|
|
changeBreakpoint(id);
|
|
|
|
|
} else {
|
|
|
|
|
handler->notifyBreakpointInsertOk(id);
|
|
|
|
|
attemptAdjustBreakpointLocation(id);
|
|
|
|
|
}
|
2010-12-16 17:58:43 +01:00
|
|
|
} else if (response.data.findChild("msg").data().contains("Unknown option")) {
|
|
|
|
|
// Older version of gdb don't know the -a option to set tracepoints
|
|
|
|
|
// ^error,msg="mi_cmd_break_insert: Unknown option ``a''"
|
|
|
|
|
const QString fileName = handler->fileName(id);
|
|
|
|
|
const int lineNumber = handler->lineNumber(id);
|
|
|
|
|
QByteArray cmd = "trace "
|
|
|
|
|
"\"" + GdbMi::escapeCString(fileName).toLocal8Bit() + "\":"
|
|
|
|
|
+ QByteArray::number(lineNumber);
|
|
|
|
|
postCommand(cmd, NeedsStop | RebuildBreakpointModel,
|
|
|
|
|
CB(handleTraceInsert2), id);
|
2010-03-04 15:42:09 +01:00
|
|
|
} else {
|
|
|
|
|
// Some versions of gdb like "GNU gdb (GDB) SUSE (6.8.91.20090930-2.4)"
|
2010-06-15 11:14:44 +02:00
|
|
|
// know how to do pending breakpoints using CLI but not MI. So try
|
2010-03-04 15:42:09 +01:00
|
|
|
// again with MI.
|
2010-11-10 16:33:11 +01:00
|
|
|
QByteArray cmd = "break " + breakpointLocation(id);
|
2010-03-04 15:42:09 +01:00
|
|
|
postCommand(cmd, NeedsStop | RebuildBreakpointModel,
|
2010-11-10 16:33:11 +01:00
|
|
|
CB(handleBreakInsert2), id);
|
2010-03-04 15:42:09 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::handleBreakInsert2(const GdbResponse &response)
|
|
|
|
|
{
|
|
|
|
|
if (response.resultClass == GdbResultError) {
|
|
|
|
|
if (m_gdbVersion < 60800 && !m_isMacGdb) {
|
|
|
|
|
// This gdb version doesn't "do" pending breakpoints.
|
|
|
|
|
// Not much we can do about it except implementing the
|
|
|
|
|
// logic on top of shared library events, and that's not
|
|
|
|
|
// worth the effort.
|
|
|
|
|
} else {
|
|
|
|
|
QTC_ASSERT(false, /**/);
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-12-16 17:58:43 +01:00
|
|
|
void GdbEngine::handleTraceInsert2(const GdbResponse &response)
|
|
|
|
|
{
|
|
|
|
|
if (response.resultClass == GdbResultDone)
|
|
|
|
|
reloadBreakListInternal();
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-09 11:13:20 +01:00
|
|
|
void GdbEngine::reloadBreakListInternal()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-03-03 18:08:18 +01:00
|
|
|
postCommand("-break-list",
|
|
|
|
|
NeedsStop | RebuildBreakpointModel,
|
|
|
|
|
CB(handleBreakList));
|
2009-12-09 11:13:20 +01:00
|
|
|
}
|
2009-10-27 15:36:18 +01:00
|
|
|
|
2009-12-09 11:13:20 +01:00
|
|
|
void GdbEngine::handleBreakList(const GdbResponse &response)
|
|
|
|
|
{
|
2008-12-02 12:01:29 +01:00
|
|
|
// 45^done,BreakpointTable={nr_rows="2",nr_cols="6",hdr=[
|
|
|
|
|
// {width="3",alignment="-1",col_name="number",colhdr="Num"}, ...
|
|
|
|
|
// body=[bkpt={number="1",type="breakpoint",disp="keep",enabled="y",
|
|
|
|
|
// addr="0x000000000040109e",func="main",file="app.cpp",
|
|
|
|
|
// fullname="/home/apoenitz/dev/work/plugintest/app/app.cpp",
|
|
|
|
|
// line="11",times="1"},
|
|
|
|
|
// bkpt={number="2",type="breakpoint",disp="keep",enabled="y",
|
|
|
|
|
// addr="<PENDING>",pending="plugin.cpp:7",times="0"}] ... }
|
|
|
|
|
|
2009-09-24 11:16:00 +02:00
|
|
|
if (response.resultClass == GdbResultDone) {
|
|
|
|
|
GdbMi table = response.data.findChild("BreakpointTable");
|
2008-12-02 12:01:29 +01:00
|
|
|
if (table.isValid())
|
|
|
|
|
handleBreakList(table);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::handleBreakList(const GdbMi &table)
|
|
|
|
|
{
|
2010-05-10 12:34:52 +02:00
|
|
|
const GdbMi body = table.findChild("body");
|
2008-12-02 12:01:29 +01:00
|
|
|
QList<GdbMi> bkpts;
|
|
|
|
|
if (body.isValid()) {
|
|
|
|
|
// Non-Mac
|
|
|
|
|
bkpts = body.children();
|
|
|
|
|
} else {
|
|
|
|
|
// Mac
|
|
|
|
|
bkpts = table.children();
|
2009-10-02 13:24:41 +02:00
|
|
|
// Remove the 'hdr' and artificial items.
|
2008-12-02 12:01:29 +01:00
|
|
|
for (int i = bkpts.size(); --i >= 0; ) {
|
|
|
|
|
int num = bkpts.at(i).findChild("number").data().toInt();
|
2009-10-02 13:24:41 +02:00
|
|
|
if (num <= 0)
|
2008-12-02 12:01:29 +01:00
|
|
|
bkpts.removeAt(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-10 12:34:52 +02:00
|
|
|
foreach (const GdbMi &bkpt, bkpts) {
|
2010-11-10 16:33:11 +01:00
|
|
|
BreakpointResponse needle;
|
2010-12-01 11:43:48 +01:00
|
|
|
needle.number = bkpt.findChild("number").data().toInt();
|
2011-01-24 13:37:07 +01:00
|
|
|
if (isQmlStepBreakpoint2(needle.number))
|
2011-01-19 10:48:39 +01:00
|
|
|
continue;
|
2010-11-10 16:33:11 +01:00
|
|
|
BreakpointId id = breakHandler()->findSimilarBreakpoint(needle);
|
|
|
|
|
if (id != BreakpointId(-1)) {
|
2010-11-16 13:52:21 +01:00
|
|
|
updateBreakpointDataFromOutput(id, bkpt);
|
2011-03-02 13:37:29 +01:00
|
|
|
BreakpointResponse response = breakHandler()->response(id);
|
|
|
|
|
if (response.correctedLineNumber == 0)
|
|
|
|
|
attemptAdjustBreakpointLocation(id);
|
|
|
|
|
if (response.multiple && response.addresses.isEmpty())
|
|
|
|
|
postCommand("info break " + QByteArray::number(response.number),
|
|
|
|
|
NeedsStop, CB(handleBreakListMultiple), QVariant(id));
|
2010-05-10 12:34:52 +02:00
|
|
|
} else {
|
2010-11-10 16:33:11 +01:00
|
|
|
qDebug() << " NOTHING SUITABLE FOUND";
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("CANNOT FIND BP: " + bkpt.toString()));
|
2010-05-10 12:34:52 +02:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-12-09 11:13:20 +01:00
|
|
|
m_breakListOutdated = false;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2011-03-02 13:37:29 +01:00
|
|
|
void GdbEngine::handleBreakListMultiple(const GdbResponse &response)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(response.resultClass == GdbResultDone, /**/)
|
|
|
|
|
const BreakpointId id = response.cookie.toInt();
|
|
|
|
|
BreakHandler *handler = breakHandler();
|
|
|
|
|
BreakpointResponse br = handler->response(id);
|
|
|
|
|
br.addresses.append(0);
|
|
|
|
|
handler->setResponse(id, br);
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-25 13:33:38 +01:00
|
|
|
void GdbEngine::handleBreakDisable(const GdbResponse &response)
|
|
|
|
|
{
|
2010-11-15 16:22:51 +01:00
|
|
|
QTC_ASSERT(response.resultClass == GdbResultDone, /**/)
|
2010-11-18 16:07:42 +01:00
|
|
|
const BreakpointId id = response.cookie.toInt();
|
2010-11-30 12:47:53 +01:00
|
|
|
BreakHandler *handler = breakHandler();
|
2010-11-15 16:22:51 +01:00
|
|
|
// This should only be the requested state.
|
2010-11-30 12:47:53 +01:00
|
|
|
QTC_ASSERT(!handler->isEnabled(id), /* Prevent later recursion */);
|
|
|
|
|
BreakpointResponse br = handler->response(id);
|
2010-11-18 16:07:42 +01:00
|
|
|
br.enabled = false;
|
2010-11-30 12:47:53 +01:00
|
|
|
handler->setResponse(id, br);
|
|
|
|
|
changeBreakpoint(id); // Maybe there's more to do.
|
2010-11-15 16:22:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::handleBreakEnable(const GdbResponse &response)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(response.resultClass == GdbResultDone, /**/)
|
|
|
|
|
const BreakpointId id = response.cookie.toInt();
|
2010-11-30 12:47:53 +01:00
|
|
|
BreakHandler *handler = breakHandler();
|
2010-11-15 16:58:23 +01:00
|
|
|
// This should only be the requested state.
|
2010-11-30 12:47:53 +01:00
|
|
|
QTC_ASSERT(handler->isEnabled(id), /* Prevent later recursion */);
|
|
|
|
|
BreakpointResponse br = handler->response(id);
|
2010-11-18 16:07:42 +01:00
|
|
|
br.enabled = true;
|
2010-11-30 12:47:53 +01:00
|
|
|
handler->setResponse(id, br);
|
|
|
|
|
changeBreakpoint(id); // Maybe there's more to do.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::handleBreakThreadSpec(const GdbResponse &response)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(response.resultClass == GdbResultDone, /**/)
|
|
|
|
|
const BreakpointId id = response.cookie.toInt();
|
|
|
|
|
BreakHandler *handler = breakHandler();
|
|
|
|
|
BreakpointResponse br = handler->response(id);
|
|
|
|
|
br.threadSpec = handler->threadSpec(id);
|
|
|
|
|
handler->setResponse(id, br);
|
2010-11-30 13:39:01 +01:00
|
|
|
handler->notifyBreakpointNeedsReinsertion(id);
|
|
|
|
|
insertBreakpoint(id);
|
2010-03-25 13:33:38 +01:00
|
|
|
}
|
|
|
|
|
|
2009-09-24 11:16:00 +02:00
|
|
|
void GdbEngine::handleBreakIgnore(const GdbResponse &response)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
// gdb 6.8:
|
|
|
|
|
// ignore 2 0:
|
|
|
|
|
// ~"Will stop next time breakpoint 2 is reached.\n"
|
|
|
|
|
// 28^done
|
|
|
|
|
// ignore 2 12:
|
|
|
|
|
// &"ignore 2 12\n"
|
|
|
|
|
// ~"Will ignore next 12 crossings of breakpoint 2.\n"
|
|
|
|
|
// 29^done
|
|
|
|
|
//
|
|
|
|
|
// gdb 6.3 does not produce any console output
|
2010-11-15 16:22:51 +01:00
|
|
|
QTC_ASSERT(response.resultClass == GdbResultDone, /**/)
|
|
|
|
|
QString msg = _(response.data.findChild("consolestreamoutput").data());
|
2010-11-30 12:47:53 +01:00
|
|
|
BreakpointId id = response.cookie.toInt();
|
|
|
|
|
BreakHandler *handler = breakHandler();
|
|
|
|
|
BreakpointResponse br = handler->response(id);
|
2010-11-15 16:22:51 +01:00
|
|
|
//if (msg.contains(__("Will stop next time breakpoint")))
|
2010-11-18 16:07:42 +01:00
|
|
|
// response.ignoreCount = _("0");
|
2010-11-15 16:22:51 +01:00
|
|
|
//else if (msg.contains(__("Will ignore next")))
|
2010-11-18 16:07:42 +01:00
|
|
|
// response.ignoreCount = data->ignoreCount;
|
2010-11-15 16:22:51 +01:00
|
|
|
// FIXME: this assumes it is doing the right thing...
|
2011-02-04 11:33:45 +01:00
|
|
|
const BreakpointParameters ¶meters = handler->breakpointData(id);
|
|
|
|
|
br.ignoreCount = parameters.ignoreCount;
|
|
|
|
|
br.command = parameters.command;
|
2010-11-30 12:47:53 +01:00
|
|
|
handler->setResponse(id, br);
|
|
|
|
|
changeBreakpoint(id); // Maybe there's more to do.
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-09-24 11:16:00 +02:00
|
|
|
void GdbEngine::handleBreakCondition(const GdbResponse &response)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-11-30 12:47:53 +01:00
|
|
|
// Can happen at invalid condition strings.
|
|
|
|
|
//QTC_ASSERT(response.resultClass == GdbResultDone, /**/)
|
2010-11-15 16:22:51 +01:00
|
|
|
const BreakpointId id = response.cookie.toInt();
|
2010-11-30 12:47:53 +01:00
|
|
|
BreakHandler *handler = breakHandler();
|
2010-11-15 16:22:51 +01:00
|
|
|
// We just assume it was successful. Otherwise we had to parse
|
|
|
|
|
// the output stream data.
|
|
|
|
|
// The following happens on Mac:
|
|
|
|
|
// QByteArray msg = response.data.findChild("msg").data();
|
|
|
|
|
// if (1 || msg.startsWith("Error parsing breakpoint condition. "
|
|
|
|
|
// " Will try again when we hit the breakpoint.")) {
|
2010-11-30 12:47:53 +01:00
|
|
|
BreakpointResponse br = handler->response(id);
|
|
|
|
|
br.condition = handler->condition(id);
|
|
|
|
|
handler->setResponse(id, br);
|
|
|
|
|
changeBreakpoint(id); // Maybe there's more to do.
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-11-10 16:33:11 +01:00
|
|
|
void GdbEngine::extractDataFromInfoBreak(const QString &output, BreakpointId id)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
//qDebug() << output;
|
|
|
|
|
if (output.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
// "Num Type Disp Enb Address What
|
|
|
|
|
// 4 breakpoint keep y <MULTIPLE> 0x00000000004066ad
|
|
|
|
|
// 4.1 y 0x00000000004066ad in CTorTester
|
|
|
|
|
// at /data5/dev/ide/main/tests/manual/gdbdebugger/simple/app.cpp:124
|
2009-02-16 18:56:20 +01:00
|
|
|
// - or -
|
2008-12-02 12:01:29 +01:00
|
|
|
// everything on a single line on Windows for constructors of classes
|
|
|
|
|
// within namespaces.
|
|
|
|
|
// Sometimes the path is relative too.
|
2009-02-16 18:56:20 +01:00
|
|
|
|
2009-04-16 12:52:29 +02:00
|
|
|
// 2 breakpoint keep y <MULTIPLE> 0x0040168e
|
|
|
|
|
// 2.1 y 0x0040168e in MainWindow::MainWindow(QWidget*) at mainwindow.cpp:7
|
|
|
|
|
// 2.2 y 0x00401792 in MainWindow::MainWindow(QWidget*) at mainwindow.cpp:7
|
|
|
|
|
|
2009-04-16 13:54:34 +02:00
|
|
|
// tested in ../../../tests/auto/debugger/
|
2009-04-30 15:21:37 +02:00
|
|
|
QRegExp re(_("MULTIPLE.*(0x[0-9a-f]+) in (.*)\\s+at (.*):([\\d]+)([^\\d]|$)"));
|
2008-12-02 12:01:29 +01:00
|
|
|
re.setMinimal(true);
|
|
|
|
|
|
2010-11-10 16:33:11 +01:00
|
|
|
BreakpointResponse response;
|
2010-11-15 17:04:29 +01:00
|
|
|
response.fileName = _("<MULTIPLE>");
|
2010-11-10 16:33:11 +01:00
|
|
|
|
|
|
|
|
QString requestedFileName = breakHandler()->fileName(id);
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
if (re.indexIn(output) != -1) {
|
2010-11-15 17:04:29 +01:00
|
|
|
response.address = re.cap(1).toULongLong(0, 16);
|
|
|
|
|
response.functionName = re.cap(2).trimmed();
|
|
|
|
|
response.lineNumber = re.cap(4).toInt();
|
2008-12-02 12:01:29 +01:00
|
|
|
QString full = fullName(re.cap(3));
|
2009-04-16 13:54:34 +02:00
|
|
|
if (full.isEmpty()) {
|
2010-01-11 10:22:55 +01:00
|
|
|
// FIXME: This happens without UsePreciseBreakpoints regularly.
|
2009-12-10 13:53:29 +01:00
|
|
|
// We need to revive that "fill full name mapping bit by bit"
|
|
|
|
|
// approach of 1.2.x
|
|
|
|
|
//qDebug() << "NO FULL NAME KNOWN FOR" << re.cap(3);
|
2009-10-28 11:59:57 +01:00
|
|
|
full = cleanupFullName(re.cap(3));
|
|
|
|
|
if (full.isEmpty()) {
|
|
|
|
|
qDebug() << "FILE IS NOT RESOLVABLE" << re.cap(3);
|
|
|
|
|
full = re.cap(3); // FIXME: wrong, but prevents recursion
|
|
|
|
|
}
|
2009-04-16 13:54:34 +02:00
|
|
|
}
|
2010-03-04 12:30:51 +01:00
|
|
|
// The variable full could still contain, say "foo.cpp" when we asked
|
|
|
|
|
// for "/full/path/to/foo.cpp". In this case, using the requested
|
|
|
|
|
// instead of the acknowledged name makes sense as it will allow setting
|
|
|
|
|
// the marker in more cases.
|
2010-11-10 16:33:11 +01:00
|
|
|
if (requestedFileName.endsWith(full))
|
|
|
|
|
full = requestedFileName;
|
2010-11-15 17:04:29 +01:00
|
|
|
response.fileName = full;
|
2008-12-02 12:01:29 +01:00
|
|
|
} else {
|
|
|
|
|
qDebug() << "COULD NOT MATCH " << re.pattern() << " AND " << output;
|
2010-11-15 17:04:29 +01:00
|
|
|
response.number = -1; // <unavailable>
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2010-11-10 16:33:11 +01:00
|
|
|
breakHandler()->setResponse(id, response);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-09-24 11:16:00 +02:00
|
|
|
void GdbEngine::handleBreakInfo(const GdbResponse &response)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-09-24 11:16:00 +02:00
|
|
|
if (response.resultClass == GdbResultDone) {
|
2008-12-02 12:01:29 +01:00
|
|
|
// Old-style output for multiple breakpoints, presumably in a
|
2010-02-01 17:49:11 +01:00
|
|
|
// constructor.
|
2010-11-10 16:33:11 +01:00
|
|
|
const BreakpointId id(response.cookie.toInt());
|
|
|
|
|
const QString str = QString::fromLocal8Bit(
|
|
|
|
|
response.data.findChild("consolestreamoutput").data());
|
|
|
|
|
extractDataFromInfoBreak(str, id);
|
2010-03-26 10:29:19 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::handleInfoLine(const GdbResponse &response)
|
|
|
|
|
{
|
|
|
|
|
if (response.resultClass == GdbResultDone) {
|
|
|
|
|
// Old-style output: "Line 1102 of \"simple/app.cpp\" starts
|
|
|
|
|
// at address 0x80526aa <_Z10...+131> and ends at 0x80526b5
|
|
|
|
|
// <_Z10testQStackv+142>.\n"
|
|
|
|
|
QByteArray ba = response.data.findChild("consolestreamoutput").data();
|
2010-11-30 12:47:53 +01:00
|
|
|
const BreakpointId id = response.cookie.toInt();
|
2010-03-26 10:29:19 +01:00
|
|
|
const int pos = ba.indexOf(' ', 5);
|
|
|
|
|
if (ba.startsWith("Line ") && pos != -1) {
|
2010-09-21 14:26:45 +02:00
|
|
|
const int line = ba.mid(5, pos - 5).toInt();
|
2010-11-10 16:33:11 +01:00
|
|
|
BreakpointResponse br = breakHandler()->response(id);
|
2010-11-15 17:04:29 +01:00
|
|
|
br.lineNumber = line;
|
2011-01-05 19:30:24 +01:00
|
|
|
br.correctedLineNumber = line;
|
2010-11-10 16:33:11 +01:00
|
|
|
breakHandler()->setResponse(id, br);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-10 16:33:11 +01:00
|
|
|
bool GdbEngine::stateAcceptsBreakpointChanges() const
|
|
|
|
|
{
|
|
|
|
|
switch (state()) {
|
|
|
|
|
case InferiorSetupRequested:
|
|
|
|
|
case InferiorRunRequested:
|
|
|
|
|
case InferiorRunOk:
|
|
|
|
|
case InferiorStopRequested:
|
|
|
|
|
case InferiorStopOk:
|
|
|
|
|
return true;
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GdbEngine::acceptsBreakpoint(BreakpointId id) const
|
2010-10-05 11:01:14 +02:00
|
|
|
{
|
2010-11-26 13:06:03 +01:00
|
|
|
return DebuggerEngine::isCppBreakpoint(breakHandler()->breakpointData(id));
|
2010-11-10 16:33:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::insertBreakpoint(BreakpointId id)
|
|
|
|
|
{
|
|
|
|
|
// Set up fallback in case of pending breakpoints which aren't handled
|
|
|
|
|
// by the MI interface.
|
|
|
|
|
BreakHandler *handler = breakHandler();
|
|
|
|
|
QTC_ASSERT(handler->state(id) == BreakpointInsertRequested, /**/);
|
2010-11-16 13:52:21 +01:00
|
|
|
handler->notifyBreakpointInsertProceeding(id);
|
2010-11-10 16:33:11 +01:00
|
|
|
if (handler->type(id) == Watchpoint) {
|
|
|
|
|
postCommand("watch " + addressSpec(handler->address(id)),
|
|
|
|
|
NeedsStop | RebuildBreakpointModel,
|
|
|
|
|
CB(handleWatchInsert), id);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QByteArray cmd;
|
2010-12-16 17:58:43 +01:00
|
|
|
if (handler->isTracepoint(id)) {
|
|
|
|
|
cmd = "-break-insert -a -f ";
|
|
|
|
|
} else if (m_isMacGdb) {
|
2010-11-10 16:33:11 +01:00
|
|
|
cmd = "-break-insert -l -1 -f ";
|
|
|
|
|
} else if (m_gdbAdapter->isTrkAdapter()) {
|
|
|
|
|
cmd = "-break-insert -h -f ";
|
|
|
|
|
} else if (m_gdbVersion >= 70000) {
|
2010-11-30 13:39:01 +01:00
|
|
|
int spec = handler->threadSpec(id);
|
2010-11-10 16:33:11 +01:00
|
|
|
cmd = "-break-insert ";
|
2010-12-14 13:00:02 +01:00
|
|
|
if (spec >= 0)
|
2010-11-30 13:39:01 +01:00
|
|
|
cmd += "-p " + QByteArray::number(spec);
|
2010-11-10 16:33:11 +01:00
|
|
|
cmd += " -f ";
|
|
|
|
|
} else if (m_gdbVersion >= 60800) {
|
|
|
|
|
// Probably some earlier version would work as well.
|
|
|
|
|
cmd = "-break-insert -f ";
|
|
|
|
|
} else {
|
|
|
|
|
cmd = "-break-insert ";
|
|
|
|
|
}
|
|
|
|
|
//if (!data->condition.isEmpty())
|
|
|
|
|
// cmd += "-c " + data->condition + ' ';
|
|
|
|
|
cmd += breakpointLocation(id);
|
|
|
|
|
postCommand(cmd, NeedsStop | RebuildBreakpointModel,
|
|
|
|
|
CB(handleBreakInsert1), id);
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-15 16:22:51 +01:00
|
|
|
void GdbEngine::changeBreakpoint(BreakpointId id)
|
2010-11-10 16:33:11 +01:00
|
|
|
{
|
2010-11-18 16:16:23 +01:00
|
|
|
BreakHandler *handler = breakHandler();
|
|
|
|
|
const BreakpointParameters &data = handler->breakpointData(id);
|
2010-11-16 11:06:09 +01:00
|
|
|
QTC_ASSERT(data.type != UnknownType, return);
|
2010-11-18 16:16:23 +01:00
|
|
|
const BreakpointResponse &response = handler->response(id);
|
2010-11-15 17:04:29 +01:00
|
|
|
QTC_ASSERT(response.number > 0, return);
|
|
|
|
|
const QByteArray bpnr = QByteArray::number(response.number);
|
2010-11-18 16:16:23 +01:00
|
|
|
QTC_ASSERT(response.number > 0, return);
|
2010-11-30 12:47:53 +01:00
|
|
|
const BreakpointState state = handler->state(id);
|
|
|
|
|
if (state == BreakpointChangeRequested)
|
2010-11-18 16:16:23 +01:00
|
|
|
handler->notifyBreakpointChangeProceeding(id);
|
2010-11-30 12:47:53 +01:00
|
|
|
const BreakpointState state2 = handler->state(id);
|
|
|
|
|
QTC_ASSERT(state2 == BreakpointChangeProceeding, qDebug() << state2);
|
|
|
|
|
|
2010-11-30 13:39:01 +01:00
|
|
|
if (data.threadSpec != response.threadSpec) {
|
|
|
|
|
// The only way to change this seems to be to re-set the bp completely.
|
|
|
|
|
postCommand("-break-delete " + bpnr,
|
|
|
|
|
NeedsStop | RebuildBreakpointModel,
|
|
|
|
|
CB(handleBreakThreadSpec), id);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2011-02-04 11:33:45 +01:00
|
|
|
if (data.command != response.command) {
|
|
|
|
|
QByteArray breakCommand = "-break-commands " + bpnr;
|
|
|
|
|
foreach (const QString &command, data.command.split(QLatin1String("\\n"))) {
|
|
|
|
|
if (!command.isEmpty()) {
|
|
|
|
|
breakCommand.append(" \"");
|
|
|
|
|
breakCommand.append(command.toLatin1());
|
|
|
|
|
breakCommand.append('"');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
postCommand(breakCommand, NeedsStop | RebuildBreakpointModel,
|
|
|
|
|
CB(handleBreakIgnore), id);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-11-30 12:47:53 +01:00
|
|
|
if (!data.conditionsMatch(response.condition)) {
|
2010-11-16 11:06:09 +01:00
|
|
|
postCommand("condition " + bpnr + ' ' + data.condition,
|
2010-11-15 16:22:51 +01:00
|
|
|
NeedsStop | RebuildBreakpointModel,
|
|
|
|
|
CB(handleBreakCondition), id);
|
2010-11-30 12:47:53 +01:00
|
|
|
return;
|
2010-11-15 16:22:51 +01:00
|
|
|
}
|
2010-11-16 11:06:09 +01:00
|
|
|
if (data.ignoreCount != response.ignoreCount) {
|
|
|
|
|
postCommand("ignore " + bpnr + ' ' + QByteArray::number(data.ignoreCount),
|
2010-11-15 16:22:51 +01:00
|
|
|
NeedsStop | RebuildBreakpointModel,
|
|
|
|
|
CB(handleBreakIgnore), id);
|
2010-11-30 12:47:53 +01:00
|
|
|
return;
|
2010-11-15 16:22:51 +01:00
|
|
|
}
|
2010-11-16 11:06:09 +01:00
|
|
|
if (!data.enabled && response.enabled) {
|
2010-11-15 16:22:51 +01:00
|
|
|
postCommand("-break-disable " + bpnr,
|
|
|
|
|
NeedsStop | RebuildBreakpointModel,
|
|
|
|
|
CB(handleBreakDisable), id);
|
2010-11-30 12:47:53 +01:00
|
|
|
return;
|
2010-11-15 16:22:51 +01:00
|
|
|
}
|
2010-11-16 11:06:09 +01:00
|
|
|
if (data.enabled && !response.enabled) {
|
2010-11-15 16:22:51 +01:00
|
|
|
postCommand("-break-enable " + bpnr,
|
|
|
|
|
NeedsStop | RebuildBreakpointModel,
|
|
|
|
|
CB(handleBreakEnable), id);
|
2010-11-30 12:47:53 +01:00
|
|
|
return;
|
2010-11-15 16:22:51 +01:00
|
|
|
}
|
2010-11-30 12:47:53 +01:00
|
|
|
handler->notifyBreakpointChangeOk(id);
|
2010-11-18 13:38:58 +01:00
|
|
|
attemptAdjustBreakpointLocation(id);
|
2010-11-10 16:33:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::removeBreakpoint(BreakpointId id)
|
|
|
|
|
{
|
|
|
|
|
BreakHandler *handler = breakHandler();
|
|
|
|
|
QTC_ASSERT(handler->state(id) == BreakpointRemoveRequested, /**/);
|
2010-11-16 13:52:21 +01:00
|
|
|
handler->notifyBreakpointRemoveProceeding(id);
|
2010-11-10 16:33:11 +01:00
|
|
|
BreakpointResponse br = handler->response(id);
|
2010-11-25 14:33:13 +01:00
|
|
|
showMessage(_("DELETING BP %1 IN %2").arg(br.number)
|
|
|
|
|
.arg(handler->fileName(id)));
|
2010-11-15 17:04:29 +01:00
|
|
|
postCommand("-break-delete " + QByteArray::number(br.number),
|
2010-11-10 16:33:11 +01:00
|
|
|
NeedsStop | RebuildBreakpointModel);
|
|
|
|
|
// Pretend it succeeds without waiting for response. Feels better.
|
2010-11-16 13:52:21 +01:00
|
|
|
// FIXME: Really?
|
2010-11-10 16:33:11 +01:00
|
|
|
handler->notifyBreakpointRemoveOk(id);
|
2010-10-05 11:01:14 +02:00
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// Modules specific stuff
|
|
|
|
|
//
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void GdbEngine::loadSymbols(const QString &moduleName)
|
|
|
|
|
{
|
|
|
|
|
// FIXME: gdb does not understand quoted names here (tested with 6.8)
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("sharedlibrary " + dotEscape(moduleName.toLocal8Bit()));
|
2009-10-28 22:08:58 +01:00
|
|
|
reloadModulesInternal();
|
2011-02-04 15:32:30 +01:00
|
|
|
reloadBreakListInternal();
|
2010-12-21 12:41:34 +01:00
|
|
|
reloadStack(true);
|
|
|
|
|
updateLocals();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::loadAllSymbols()
|
|
|
|
|
{
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("sharedlibrary .*");
|
2009-10-28 22:08:58 +01:00
|
|
|
reloadModulesInternal();
|
2011-02-04 15:32:30 +01:00
|
|
|
reloadBreakListInternal();
|
2010-12-21 12:41:34 +01:00
|
|
|
reloadStack(true);
|
|
|
|
|
updateLocals();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-12-21 13:34:59 +01:00
|
|
|
void GdbEngine::loadSymbolsForStack()
|
|
|
|
|
{
|
|
|
|
|
bool needUpdate = false;
|
|
|
|
|
const Modules &modules = modulesHandler()->modules();
|
|
|
|
|
foreach (const StackFrame &frame, stackHandler()->frames()) {
|
|
|
|
|
if (frame.function == _("??")) {
|
2010-12-21 14:41:17 +01:00
|
|
|
//qDebug() << "LOAD FOR " << frame.address;
|
2010-12-21 13:34:59 +01:00
|
|
|
foreach (const Module &module, modules) {
|
|
|
|
|
if (module.startAddress <= frame.address
|
|
|
|
|
&& frame.address < module.endAddress) {
|
|
|
|
|
postCommand("sharedlibrary "
|
|
|
|
|
+ dotEscape(module.moduleName.toLocal8Bit()));
|
|
|
|
|
needUpdate = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (needUpdate) {
|
|
|
|
|
reloadModulesInternal();
|
2011-02-04 15:32:30 +01:00
|
|
|
reloadBreakListInternal();
|
2010-12-21 13:34:59 +01:00
|
|
|
reloadStack(true);
|
|
|
|
|
updateLocals();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-03 19:12:52 +02:00
|
|
|
void GdbEngine::requestModuleSymbols(const QString &moduleName)
|
2009-04-15 12:01:58 +02:00
|
|
|
{
|
2010-11-26 09:58:34 +01:00
|
|
|
QTemporaryFile tf(QDir::tempPath() + _("/gdbsymbols"));
|
|
|
|
|
if (!tf.open())
|
|
|
|
|
return;
|
|
|
|
|
QString fileName = tf.fileName();
|
|
|
|
|
tf.close();
|
|
|
|
|
postCommand("maint print msymbols " + fileName.toLocal8Bit()
|
2011-02-25 15:27:13 +01:00
|
|
|
+ ' ' + moduleName.toLocal8Bit(),
|
2010-11-26 09:58:34 +01:00
|
|
|
NeedsStop, CB(handleShowModuleSymbols),
|
|
|
|
|
QVariant(moduleName + QLatin1Char('@') + fileName));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::handleShowModuleSymbols(const GdbResponse &response)
|
|
|
|
|
{
|
|
|
|
|
const QString cookie = response.cookie.toString();
|
|
|
|
|
const QString moduleName = cookie.section(QLatin1Char('@'), 0, 0);
|
|
|
|
|
const QString fileName = cookie.section(QLatin1Char('@'), 1, 1);
|
|
|
|
|
if (response.resultClass == GdbResultDone) {
|
|
|
|
|
Symbols rc;
|
|
|
|
|
QFile file(fileName);
|
|
|
|
|
file.open(QIODevice::ReadOnly);
|
2010-12-16 11:25:28 +01:00
|
|
|
// Object file /opt/dev/qt/lib/libQtNetworkMyns.so.4:
|
2010-11-26 09:58:34 +01:00
|
|
|
// [ 0] A 0x16bd64 _DYNAMIC moc_qudpsocket.cpp
|
2010-12-16 11:25:28 +01:00
|
|
|
// [12] S 0xe94680 _ZN4myns5QFileC1Ev section .plt myns::QFile::QFile()
|
2010-11-26 09:58:34 +01:00
|
|
|
foreach (const QByteArray &line, file.readAll().split('\n')) {
|
|
|
|
|
if (line.isEmpty())
|
|
|
|
|
continue;
|
2010-11-26 13:06:03 +01:00
|
|
|
if (line.at(0) != '[')
|
2010-11-26 09:58:34 +01:00
|
|
|
continue;
|
|
|
|
|
int posCode = line.indexOf(']') + 2;
|
|
|
|
|
int posAddress = line.indexOf("0x", posCode);
|
|
|
|
|
if (posAddress == -1)
|
|
|
|
|
continue;
|
|
|
|
|
int posName = line.indexOf(" ", posAddress);
|
|
|
|
|
int lenAddress = posName - posAddress - 1;
|
|
|
|
|
int posSection = line.indexOf(" section ");
|
|
|
|
|
int lenName = 0;
|
|
|
|
|
int lenSection = 0;
|
|
|
|
|
int posDemangled = 0;
|
|
|
|
|
if (posSection == -1) {
|
|
|
|
|
lenName = line.size() - posName;
|
|
|
|
|
posDemangled = posName;
|
2009-04-15 12:01:58 +02:00
|
|
|
} else {
|
2010-11-26 09:58:34 +01:00
|
|
|
lenName = posSection - posName;
|
|
|
|
|
posSection += 10;
|
|
|
|
|
posDemangled = line.indexOf(' ', posSection + 1);
|
|
|
|
|
if (posDemangled == -1) {
|
|
|
|
|
lenSection = line.size() - posSection;
|
|
|
|
|
} else {
|
|
|
|
|
lenSection = posDemangled - posSection;
|
|
|
|
|
posDemangled += 1;
|
|
|
|
|
}
|
2009-04-15 12:01:58 +02:00
|
|
|
}
|
2010-11-26 09:58:34 +01:00
|
|
|
int lenDemangled = 0;
|
|
|
|
|
if (posDemangled != -1)
|
|
|
|
|
lenDemangled = line.size() - posDemangled;
|
|
|
|
|
Symbol symbol;
|
|
|
|
|
symbol.state = _(line.mid(posCode, 1));
|
|
|
|
|
symbol.address = _(line.mid(posAddress, lenAddress));
|
|
|
|
|
symbol.name = _(line.mid(posName, lenName));
|
|
|
|
|
symbol.section = _(line.mid(posSection, lenSection));
|
|
|
|
|
symbol.demangled = _(line.mid(posDemangled, lenDemangled));
|
|
|
|
|
rc.push_back(symbol);
|
2009-04-15 12:01:58 +02:00
|
|
|
}
|
2010-11-26 09:58:34 +01:00
|
|
|
file.close();
|
|
|
|
|
file.remove();
|
|
|
|
|
debuggerCore()->showModuleSymbols(moduleName, rc);
|
|
|
|
|
} else {
|
|
|
|
|
showMessageBox(QMessageBox::Critical, tr("Cannot Read Symbols"),
|
|
|
|
|
tr("Cannot read symbols for module \"%1\".").arg(fileName));
|
|
|
|
|
}
|
2009-04-15 12:01:58 +02:00
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
void GdbEngine::reloadModules()
|
2009-10-28 22:08:58 +01:00
|
|
|
{
|
2010-07-09 17:07:59 +02:00
|
|
|
if (state() == InferiorRunOk || state() == InferiorStopOk)
|
2009-10-28 22:08:58 +01:00
|
|
|
reloadModulesInternal();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::reloadModulesInternal()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-10-22 15:46:49 +02:00
|
|
|
m_modulesListOutdated = false;
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("info shared", NeedsStop, CB(handleModulesList));
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-09-24 11:16:00 +02:00
|
|
|
void GdbEngine::handleModulesList(const GdbResponse &response)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-11-26 09:58:34 +01:00
|
|
|
Modules modules;
|
2009-09-24 11:16:00 +02:00
|
|
|
if (response.resultClass == GdbResultDone) {
|
2009-10-02 13:24:41 +02:00
|
|
|
// That's console-based output, likely Linux or Windows,
|
|
|
|
|
// but we can avoid the #ifdef here.
|
2010-09-15 14:17:02 +02:00
|
|
|
QString data = QString::fromLocal8Bit(
|
|
|
|
|
response.data.findChild("consolestreamoutput").data());
|
2008-12-02 12:01:29 +01:00
|
|
|
QTextStream ts(&data, QIODevice::ReadOnly);
|
|
|
|
|
while (!ts.atEnd()) {
|
|
|
|
|
QString line = ts.readLine();
|
|
|
|
|
Module module;
|
|
|
|
|
QString symbolsRead;
|
|
|
|
|
QTextStream ts(&line, QIODevice::ReadOnly);
|
2009-10-23 15:46:39 +02:00
|
|
|
if (line.startsWith(__("0x"))) {
|
|
|
|
|
ts >> module.startAddress >> module.endAddress >> symbolsRead;
|
|
|
|
|
module.moduleName = ts.readLine().trimmed();
|
2010-07-22 16:15:50 +02:00
|
|
|
module.symbolsRead =
|
|
|
|
|
(symbolsRead == __("Yes") ? Module::ReadOk : Module::ReadFailed);
|
2009-10-23 15:46:39 +02:00
|
|
|
modules.append(module);
|
|
|
|
|
} else if (line.trimmed().startsWith(__("No"))) {
|
|
|
|
|
// gdb 6.4 symbianelf
|
|
|
|
|
ts >> symbolsRead;
|
|
|
|
|
QTC_ASSERT(symbolsRead == __("No"), continue);
|
2011-01-06 09:34:45 +01:00
|
|
|
module.startAddress = 0;
|
|
|
|
|
module.endAddress = 0;
|
2009-10-23 15:46:39 +02:00
|
|
|
module.moduleName = ts.readLine().trimmed();
|
|
|
|
|
modules.append(module);
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2009-03-12 12:00:53 +01:00
|
|
|
if (modules.isEmpty()) {
|
|
|
|
|
// Mac has^done,shlib-info={num="1",name="dyld",kind="-",
|
|
|
|
|
// dyld-addr="0x8fe00000",reason="dyld",requested-state="Y",
|
|
|
|
|
// state="Y",path="/usr/lib/dyld",description="/usr/lib/dyld",
|
|
|
|
|
// loaded_addr="0x8fe00000",slide="0x0",prefix="__dyld_"},
|
|
|
|
|
// shlib-info={...}...
|
2009-09-24 11:16:00 +02:00
|
|
|
foreach (const GdbMi &item, response.data.children()) {
|
2009-03-12 12:00:53 +01:00
|
|
|
Module module;
|
2010-12-21 13:34:59 +01:00
|
|
|
module.moduleName =
|
|
|
|
|
QString::fromLocal8Bit(item.findChild("path").data());
|
2010-07-22 16:15:50 +02:00
|
|
|
module.symbolsRead = (item.findChild("state").data() == "Y")
|
|
|
|
|
? Module::ReadOk : Module::ReadFailed;
|
2010-12-21 13:34:59 +01:00
|
|
|
module.startAddress =
|
|
|
|
|
item.findChild("loaded_addr").data().toULongLong(0, 0);
|
|
|
|
|
module.endAddress = 0; // FIXME: End address not easily available.
|
2009-03-12 12:00:53 +01:00
|
|
|
modules.append(module);
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2010-06-16 11:08:54 +02:00
|
|
|
modulesHandler()->setModules(modules);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2010-07-22 16:53:01 +02:00
|
|
|
void GdbEngine::examineModules()
|
|
|
|
|
{
|
|
|
|
|
foreach (Module module, modulesHandler()->modules()) {
|
|
|
|
|
if (module.symbolsType == Module::UnknownType) {
|
|
|
|
|
QProcess proc;
|
|
|
|
|
qDebug() << _("objdump -h \"%1\"").arg(module.moduleName);
|
|
|
|
|
proc.start(_("objdump -h \"%1\"").arg(module.moduleName));
|
|
|
|
|
if (!proc.waitForStarted())
|
|
|
|
|
continue;
|
|
|
|
|
if (!proc.waitForFinished())
|
|
|
|
|
continue;
|
|
|
|
|
QByteArray ba = proc.readAllStandardOutput();
|
|
|
|
|
if (ba.contains(".gdb_index"))
|
|
|
|
|
module.symbolsType = Module::FastSymbols;
|
|
|
|
|
else
|
|
|
|
|
module.symbolsType = Module::PlainSymbols;
|
|
|
|
|
modulesHandler()->updateModule(module.moduleName, module);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-17 14:08:49 +01:00
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// Source files specific stuff
|
|
|
|
|
//
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2009-12-09 11:13:20 +01:00
|
|
|
void GdbEngine::invalidateSourcesList()
|
|
|
|
|
{
|
|
|
|
|
m_modulesListOutdated = true;
|
|
|
|
|
m_sourcesListOutdated = true;
|
|
|
|
|
m_breakListOutdated = true;
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-17 14:08:49 +01:00
|
|
|
void GdbEngine::reloadSourceFiles()
|
2009-10-28 22:08:58 +01:00
|
|
|
{
|
2010-07-09 17:07:59 +02:00
|
|
|
if ((state() == InferiorRunOk || state() == InferiorStopOk)
|
2009-10-28 22:08:58 +01:00
|
|
|
&& !m_sourcesListUpdating)
|
|
|
|
|
reloadSourceFilesInternal();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::reloadSourceFilesInternal()
|
2009-02-17 14:08:49 +01:00
|
|
|
{
|
2009-12-09 11:13:20 +01:00
|
|
|
QTC_ASSERT(!m_sourcesListUpdating, /**/);
|
2009-10-22 22:54:30 +02:00
|
|
|
m_sourcesListUpdating = true;
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("-file-list-exec-source-files", NeedsStop, CB(handleQuerySources));
|
2009-11-12 14:49:54 +01:00
|
|
|
#if 0
|
2009-11-10 12:40:23 +01:00
|
|
|
if (m_gdbVersion < 70000 && !m_isMacGdb)
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("set stop-on-solib-events 1");
|
2009-11-12 14:49:54 +01:00
|
|
|
#endif
|
2009-02-17 14:08:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// Stack specific stuff
|
|
|
|
|
//
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2009-10-05 12:00:47 +02:00
|
|
|
void GdbEngine::selectThread(int index)
|
|
|
|
|
{
|
2010-06-15 11:42:49 +02:00
|
|
|
threadsHandler()->setCurrentThread(index);
|
2010-06-16 11:08:54 +02:00
|
|
|
Threads threads = threadsHandler()->threads();
|
2009-10-05 12:00:47 +02:00
|
|
|
QTC_ASSERT(index < threads.size(), return);
|
2010-06-30 17:14:57 +02:00
|
|
|
const int id = threads.at(index).id;
|
2010-11-23 15:43:05 +01:00
|
|
|
showStatusMessage(tr("Retrieving data for stack view thread 0x%1...")
|
|
|
|
|
.arg(id, 0, 16), 10000);
|
|
|
|
|
postCommand("-thread-select " + QByteArray::number(id), Discardable,
|
2010-09-15 14:16:03 +02:00
|
|
|
CB(handleStackSelectThread));
|
2009-10-05 12:00:47 +02:00
|
|
|
}
|
|
|
|
|
|
2009-09-24 11:16:00 +02:00
|
|
|
void GdbEngine::handleStackSelectThread(const GdbResponse &)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == InferiorUnrunnable || state() == InferiorStopOk, /**/);
|
2009-09-14 11:02:36 +02:00
|
|
|
showStatusMessage(tr("Retrieving data for stack view..."), 3000);
|
2010-11-24 11:44:43 +01:00
|
|
|
reloadStack(true); // Will reload registers.
|
2010-01-29 21:33:57 +01:00
|
|
|
updateLocals();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-10-05 12:00:47 +02:00
|
|
|
void GdbEngine::reloadFullStack()
|
|
|
|
|
{
|
2010-01-14 15:25:53 +01:00
|
|
|
PENDING_DEBUG("RELOAD FULL STACK");
|
2010-09-15 14:16:03 +02:00
|
|
|
postCommand("-stack-list-frames", Discardable, CB(handleStackListFrames),
|
2009-10-05 12:00:47 +02:00
|
|
|
QVariant::fromValue<StackCookie>(StackCookie(true, true)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::reloadStack(bool forceGotoLocation)
|
|
|
|
|
{
|
2010-01-14 15:25:53 +01:00
|
|
|
PENDING_DEBUG("RELOAD STACK");
|
2010-01-05 16:51:55 +01:00
|
|
|
QByteArray cmd = "-stack-list-frames";
|
2010-11-10 16:33:11 +01:00
|
|
|
int stackDepth = debuggerCore()->action(MaximalStackDepth)->value().toInt();
|
2009-10-05 12:00:47 +02:00
|
|
|
if (stackDepth && !m_gdbAdapter->isTrkAdapter())
|
2010-01-05 16:51:55 +01:00
|
|
|
cmd += " 0 " + QByteArray::number(stackDepth);
|
2009-10-05 12:00:47 +02:00
|
|
|
// FIXME: gdb 6.4 symbianelf likes to be asked twice. The first time it
|
|
|
|
|
// returns with "^error,msg="Previous frame identical to this frame
|
|
|
|
|
// (corrupt stack?)". Might be related to the fact that we can't
|
|
|
|
|
// access the memory belonging to the lower frames. But as we know
|
|
|
|
|
// this sometimes happens, ask the second time immediately instead
|
|
|
|
|
// of waiting for the first request to fail.
|
2010-01-14 15:25:53 +01:00
|
|
|
// FIXME: Seems to work with 6.8.
|
2010-01-15 11:35:33 +01:00
|
|
|
if (m_gdbAdapter->isTrkAdapter() && m_gdbVersion < 6.8)
|
2010-03-10 17:39:08 +01:00
|
|
|
postCommand(cmd);
|
2010-09-15 14:16:03 +02:00
|
|
|
postCommand(cmd, Discardable, CB(handleStackListFrames),
|
2009-10-05 12:00:47 +02:00
|
|
|
QVariant::fromValue<StackCookie>(StackCookie(false, forceGotoLocation)));
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-10-02 11:45:19 +02:00
|
|
|
StackFrame GdbEngine::parseStackFrame(const GdbMi &frameMi, int level)
|
|
|
|
|
{
|
|
|
|
|
//qDebug() << "HANDLING FRAME:" << frameMi.toString();
|
|
|
|
|
StackFrame frame;
|
|
|
|
|
frame.level = level;
|
2009-10-29 17:50:53 +01:00
|
|
|
GdbMi fullName = frameMi.findChild("fullname");
|
|
|
|
|
if (fullName.isValid())
|
|
|
|
|
frame.file = cleanupFullName(QFile::decodeName(fullName.data()));
|
|
|
|
|
else
|
|
|
|
|
frame.file = QFile::decodeName(frameMi.findChild("file").data());
|
2009-10-02 11:45:19 +02:00
|
|
|
frame.function = _(frameMi.findChild("func").data());
|
|
|
|
|
frame.from = _(frameMi.findChild("from").data());
|
|
|
|
|
frame.line = frameMi.findChild("line").data().toInt();
|
2010-09-21 15:12:33 +02:00
|
|
|
frame.address = frameMi.findChild("addr").data().toULongLong(0, 16);
|
2010-12-01 15:57:13 +01:00
|
|
|
frame.usable = QFileInfo(frame.file).isReadable();
|
2009-10-02 11:45:19 +02:00
|
|
|
return frame;
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-24 11:16:00 +02:00
|
|
|
void GdbEngine::handleStackListFrames(const GdbResponse &response)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-10-22 11:08:06 +02:00
|
|
|
bool handleIt = (m_isMacGdb || response.resultClass == GdbResultDone);
|
2009-10-05 12:00:47 +02:00
|
|
|
if (!handleIt) {
|
|
|
|
|
// That always happens on symbian gdb with
|
|
|
|
|
// ^error,data={msg="Previous frame identical to this frame (corrupt stack?)"
|
|
|
|
|
// logstreamoutput="Previous frame identical to this frame (corrupt stack?)\n"
|
|
|
|
|
//qDebug() << "LISTING STACK FAILED: " << response.toString();
|
2010-11-24 11:44:43 +01:00
|
|
|
reloadRegisters();
|
2009-10-05 12:00:47 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-10-05 12:00:47 +02:00
|
|
|
StackCookie cookie = response.cookie.value<StackCookie>();
|
|
|
|
|
QList<StackFrame> stackFrames;
|
2009-09-14 09:46:34 +02:00
|
|
|
|
2009-10-05 12:00:47 +02:00
|
|
|
GdbMi stack = response.data.findChild("stack");
|
|
|
|
|
if (!stack.isValid()) {
|
|
|
|
|
qDebug() << "FIXME: stack:" << stack.toString();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2009-09-14 09:46:34 +02:00
|
|
|
|
2009-10-05 12:00:47 +02:00
|
|
|
int targetFrame = -1;
|
2009-09-14 09:46:34 +02:00
|
|
|
|
2009-10-05 12:00:47 +02:00
|
|
|
int n = stack.childCount();
|
|
|
|
|
for (int i = 0; i != n; ++i) {
|
|
|
|
|
stackFrames.append(parseStackFrame(stack.childAt(i), i));
|
|
|
|
|
const StackFrame &frame = stackFrames.back();
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2010-06-10 13:12:12 +02:00
|
|
|
# if defined(Q_OS_WIN)
|
2009-10-05 12:00:47 +02:00
|
|
|
const bool isBogus =
|
|
|
|
|
// Assume this is wrong and points to some strange stl_algobase
|
|
|
|
|
// implementation. Happens on Karsten's XP system with Gdb 5.50
|
|
|
|
|
(frame.file.endsWith(__("/bits/stl_algobase.h")) && frame.line == 150)
|
|
|
|
|
// Also wrong. Happens on Vista with Gdb 5.50
|
|
|
|
|
|| (frame.function == __("operator new") && frame.line == 151);
|
|
|
|
|
|
|
|
|
|
// Immediately leave bogus frames.
|
|
|
|
|
if (targetFrame == -1 && isBogus) {
|
2009-10-20 20:47:47 +02:00
|
|
|
setTokenBarrier();
|
2010-07-09 17:07:59 +02:00
|
|
|
notifyInferiorRunRequested();
|
2010-03-10 16:51:06 +01:00
|
|
|
postCommand("-exec-finish", RunRequest, CB(handleExecuteContinue));
|
2009-10-20 20:47:47 +02:00
|
|
|
showStatusMessage(tr("Jumping out of bogus frame..."), 1000);
|
2009-10-05 12:00:47 +02:00
|
|
|
return;
|
2009-09-14 09:46:34 +02:00
|
|
|
}
|
2010-06-10 13:12:12 +02:00
|
|
|
# endif
|
2009-10-05 12:00:47 +02:00
|
|
|
|
|
|
|
|
// Initialize top frame to the first valid frame.
|
2009-10-30 15:07:17 +01:00
|
|
|
const bool isValid = frame.isUsable() && !frame.function.isEmpty();
|
2009-10-05 12:00:47 +02:00
|
|
|
if (isValid && targetFrame == -1)
|
|
|
|
|
targetFrame = i;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-01-29 21:33:57 +01:00
|
|
|
bool canExpand = !cookie.isFull
|
2010-11-10 16:33:11 +01:00
|
|
|
&& (n >= debuggerCore()->action(MaximalStackDepth)->value().toInt());
|
|
|
|
|
debuggerCore()->action(ExpandStack)->setEnabled(canExpand);
|
2010-06-15 11:42:49 +02:00
|
|
|
stackHandler()->setFrames(stackFrames, canExpand);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-10-05 12:00:47 +02:00
|
|
|
// We can't jump to any file if we don't have any frames.
|
|
|
|
|
if (stackFrames.isEmpty())
|
|
|
|
|
return;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-10-05 12:00:47 +02:00
|
|
|
// targetFrame contains the top most frame for which we have source
|
|
|
|
|
// information. That's typically the frame we'd like to jump to, with
|
|
|
|
|
// a few exceptions:
|
|
|
|
|
|
|
|
|
|
// Always jump to frame #0 when stepping by instruction.
|
2010-11-10 16:33:11 +01:00
|
|
|
if (debuggerCore()->boolSetting(OperateByInstruction))
|
2009-10-05 12:00:47 +02:00
|
|
|
targetFrame = 0;
|
|
|
|
|
|
|
|
|
|
// If there is no frame with source, jump to frame #0.
|
|
|
|
|
if (targetFrame == -1)
|
|
|
|
|
targetFrame = 0;
|
|
|
|
|
|
2010-06-15 11:42:49 +02:00
|
|
|
stackHandler()->setCurrentIndex(targetFrame);
|
2010-11-19 08:50:53 +01:00
|
|
|
activateFrame(targetFrame);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::activateFrame(int frameIndex)
|
|
|
|
|
{
|
2010-07-09 17:07:59 +02:00
|
|
|
if (state() != InferiorStopOk && state() != InferiorUnrunnable)
|
2008-12-02 12:01:29 +01:00
|
|
|
return;
|
|
|
|
|
|
2010-06-15 11:42:49 +02:00
|
|
|
StackHandler *handler = stackHandler();
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2010-06-15 11:42:49 +02:00
|
|
|
if (frameIndex == handler->stackSize()) {
|
2009-04-06 17:27:15 +02:00
|
|
|
reloadFullStack();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2009-10-02 12:31:19 +02:00
|
|
|
|
2010-06-15 11:42:49 +02:00
|
|
|
QTC_ASSERT(frameIndex < handler->stackSize(), return);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2010-11-22 17:12:02 +01:00
|
|
|
// Assuming the command always succeeds this saves a roundtrip.
|
|
|
|
|
// Otherwise the lines below would need to get triggered
|
|
|
|
|
// after a response to this -stack-select-frame here.
|
|
|
|
|
handler->setCurrentIndex(frameIndex);
|
2010-12-20 16:28:26 +01:00
|
|
|
QByteArray cmd = "-stack-select-frame";
|
|
|
|
|
//if (!m_currentThread.isEmpty())
|
|
|
|
|
// cmd += " --thread " + m_currentThread;
|
|
|
|
|
cmd += ' ';
|
|
|
|
|
cmd += QByteArray::number(frameIndex);
|
|
|
|
|
postCommand(cmd, Discardable, CB(handleStackSelectFrame));
|
2010-12-16 19:06:33 +01:00
|
|
|
gotoLocation(stackHandler()->currentFrame());
|
2010-11-22 17:12:02 +01:00
|
|
|
updateLocals();
|
|
|
|
|
reloadRegisters();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-03-10 17:39:08 +01:00
|
|
|
void GdbEngine::handleStackSelectFrame(const GdbResponse &response)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(response);
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-17 17:38:31 +02:00
|
|
|
void GdbEngine::handleThreadInfo(const GdbResponse &response)
|
|
|
|
|
{
|
2010-11-22 14:12:53 +01:00
|
|
|
const int id = response.cookie.toInt();
|
2010-05-17 17:38:31 +02:00
|
|
|
if (response.resultClass == GdbResultDone) {
|
2010-11-08 17:15:33 +01:00
|
|
|
int currentThreadId;
|
2010-11-22 14:12:53 +01:00
|
|
|
const Threads threads =
|
|
|
|
|
ThreadsHandler::parseGdbmiThreads(response.data, ¤tThreadId);
|
2010-06-15 11:42:49 +02:00
|
|
|
threadsHandler()->setThreads(threads);
|
2010-07-02 10:58:47 +02:00
|
|
|
threadsHandler()->setCurrentThreadId(currentThreadId);
|
2010-10-27 14:21:33 +02:00
|
|
|
updateViews(); // Adjust Threads combobox.
|
2010-12-20 17:39:51 +01:00
|
|
|
if (m_hasInferiorThreadList && debuggerCore()->boolSetting(ShowThreadNames)) {
|
2010-10-27 14:21:33 +02:00
|
|
|
postCommand("threadnames " +
|
2010-11-10 16:33:11 +01:00
|
|
|
debuggerCore()->action(MaximalStackDepth)->value().toByteArray(),
|
2010-11-22 14:12:53 +01:00
|
|
|
Discardable, CB(handleThreadNames), id);
|
2010-10-27 14:21:33 +02:00
|
|
|
}
|
2010-05-17 17:38:31 +02:00
|
|
|
} else {
|
|
|
|
|
// Fall back for older versions: Try to get at least a list
|
|
|
|
|
// of running threads.
|
2010-11-22 14:12:53 +01:00
|
|
|
postCommand("-thread-list-ids", Discardable, CB(handleThreadListIds), id);
|
2010-05-17 17:38:31 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::handleThreadListIds(const GdbResponse &response)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-07-02 10:58:47 +02:00
|
|
|
const int id = response.cookie.toInt();
|
2008-12-02 12:01:29 +01:00
|
|
|
// "72^done,{thread-ids={thread-id="2",thread-id="1"},number-of-threads="2"}
|
2010-05-17 17:38:31 +02:00
|
|
|
// In gdb 7.1+ additionally: current-thread-id="1"
|
2009-09-24 11:16:00 +02:00
|
|
|
const QList<GdbMi> items = response.data.findChild("thread-ids").children();
|
2010-06-16 11:08:54 +02:00
|
|
|
Threads threads;
|
2008-12-02 12:01:29 +01:00
|
|
|
for (int index = 0, n = items.size(); index != n; ++index) {
|
|
|
|
|
ThreadData thread;
|
|
|
|
|
thread.id = items.at(index).data().toInt();
|
|
|
|
|
threads.append(thread);
|
|
|
|
|
}
|
2010-06-15 11:42:49 +02:00
|
|
|
threadsHandler()->setThreads(threads);
|
2010-07-02 10:58:47 +02:00
|
|
|
threadsHandler()->setCurrentThreadId(id);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-09-13 12:37:30 +02:00
|
|
|
void GdbEngine::handleThreadNames(const GdbResponse &response)
|
|
|
|
|
{
|
|
|
|
|
if (response.resultClass == GdbResultDone) {
|
|
|
|
|
GdbMi contents = response.data.findChild("consolestreamoutput");
|
|
|
|
|
GdbMi names;
|
|
|
|
|
names.fromString(contents.data());
|
|
|
|
|
|
|
|
|
|
Threads threads = threadsHandler()->threads();
|
|
|
|
|
|
|
|
|
|
foreach (const GdbMi &name, names.children()) {
|
|
|
|
|
int id = name.findChild("id").data().toInt();
|
|
|
|
|
for (int index = 0, n = threads.size(); index != n; ++index) {
|
|
|
|
|
ThreadData & thread = threads[index];
|
2010-10-15 10:56:39 +02:00
|
|
|
if (thread.id == (quint64)id) {
|
2010-10-27 14:21:33 +02:00
|
|
|
thread.name = decodeData(name.findChild("value").data(),
|
|
|
|
|
name.findChild("valueencoded").data().toInt());
|
2010-09-13 12:37:30 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
threadsHandler()->setThreads(threads);
|
2010-10-27 14:21:33 +02:00
|
|
|
updateViews();
|
2010-09-13 12:37:30 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2010-02-02 17:25:14 +01:00
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// Snapshot specific stuff
|
|
|
|
|
//
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2010-07-21 09:42:33 +02:00
|
|
|
void GdbEngine::createSnapshot()
|
2010-02-02 17:25:14 +01:00
|
|
|
{
|
|
|
|
|
QString fileName;
|
|
|
|
|
QTemporaryFile tf(QDir::tempPath() + _("/gdbsnapshot"));
|
|
|
|
|
if (tf.open()) {
|
|
|
|
|
fileName = tf.fileName();
|
|
|
|
|
tf.close();
|
|
|
|
|
postCommand("gcore " + fileName.toLocal8Bit(),
|
|
|
|
|
NeedsStop, CB(handleMakeSnapshot), fileName);
|
|
|
|
|
} else {
|
|
|
|
|
showMessageBox(QMessageBox::Critical, tr("Snapshot Creation Error"),
|
|
|
|
|
tr("Cannot create snapshot file."));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::handleMakeSnapshot(const GdbResponse &response)
|
|
|
|
|
{
|
|
|
|
|
if (response.resultClass == GdbResultDone) {
|
2010-07-13 15:57:34 +02:00
|
|
|
DebuggerStartParameters sp = startParameters();
|
|
|
|
|
sp.startMode = AttachCore;
|
|
|
|
|
sp.coreFile = response.cookie.toString();
|
|
|
|
|
//snapshot.setDate(QDateTime::currentDateTime());
|
|
|
|
|
StackFrames frames = stackHandler()->frames();
|
|
|
|
|
QString function = _("<unknown>");
|
|
|
|
|
if (!frames.isEmpty()) {
|
|
|
|
|
const StackFrame &frame = frames.at(0);
|
|
|
|
|
function = frame.function + _(":") + QString::number(frame.line);
|
|
|
|
|
}
|
|
|
|
|
sp.displayName = function + _(": ") + QDateTime::currentDateTime().toString();
|
2010-09-07 14:19:44 +02:00
|
|
|
sp.isSnapshot = true;
|
2010-07-13 15:57:34 +02:00
|
|
|
DebuggerRunControl *rc = DebuggerPlugin::createDebugger(sp);
|
|
|
|
|
DebuggerPlugin::startDebugger(rc);
|
2010-02-02 17:25:14 +01:00
|
|
|
} else {
|
|
|
|
|
QByteArray msg = response.data.findChild("msg").data();
|
|
|
|
|
showMessageBox(QMessageBox::Critical, tr("Snapshot Creation Error"),
|
|
|
|
|
tr("Cannot create snapshot:\n") + QString::fromLocal8Bit(msg));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// Register specific stuff
|
|
|
|
|
//
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void GdbEngine::reloadRegisters()
|
|
|
|
|
{
|
2010-12-03 15:03:51 +01:00
|
|
|
if (!debuggerCore()->isDockVisible(_(Constants::DOCKWIDGET_REGISTER)))
|
2010-06-28 09:10:20 +02:00
|
|
|
return;
|
|
|
|
|
|
2010-07-09 17:07:59 +02:00
|
|
|
if (state() != InferiorStopOk && state() != InferiorUnrunnable)
|
2009-11-30 16:45:15 +01:00
|
|
|
return;
|
2009-10-30 16:42:33 +01:00
|
|
|
if (!m_registerNamesListed) {
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("-data-list-register-names", CB(handleRegisterListNames));
|
2009-10-30 16:42:33 +01:00
|
|
|
m_registerNamesListed = true;
|
2010-02-10 11:37:57 +01:00
|
|
|
// FIXME: Maybe better completely re-do this logic in TRK adapter.
|
|
|
|
|
if (m_gdbAdapter->isTrkAdapter())
|
|
|
|
|
return;
|
2009-10-30 16:42:33 +01:00
|
|
|
}
|
|
|
|
|
|
2009-09-22 11:31:34 +02:00
|
|
|
if (m_gdbAdapter->isTrkAdapter()) {
|
2010-02-04 11:36:49 +01:00
|
|
|
m_gdbAdapter->trkReloadRegisters();
|
2009-09-15 15:32:49 +02:00
|
|
|
} else {
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("-data-list-register-values x",
|
2009-09-15 15:32:49 +02:00
|
|
|
Discardable, CB(handleRegisterListValues));
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-08-18 08:34:48 +02:00
|
|
|
void GdbEngine::setRegisterValue(int nr, const QString &value)
|
|
|
|
|
{
|
2010-06-16 11:08:54 +02:00
|
|
|
Register reg = registerHandler()->registers().at(nr);
|
2009-08-18 08:34:48 +02:00
|
|
|
//qDebug() << "NOT IMPLEMENTED: CHANGE REGISTER " << nr << reg.name << ":"
|
|
|
|
|
// << value;
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("-var-delete \"R@\"");
|
|
|
|
|
postCommand("-var-create \"R@\" * $" + reg.name);
|
|
|
|
|
postCommand("-var-assign \"R@\" " + value.toLatin1());
|
|
|
|
|
postCommand("-var-delete \"R@\"");
|
|
|
|
|
//postCommand("-data-list-register-values d",
|
2009-08-18 08:34:48 +02:00
|
|
|
// Discardable, CB(handleRegisterListValues));
|
|
|
|
|
reloadRegisters();
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-24 11:16:00 +02:00
|
|
|
void GdbEngine::handleRegisterListNames(const GdbResponse &response)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-10-30 16:42:33 +01:00
|
|
|
if (response.resultClass != GdbResultDone) {
|
|
|
|
|
m_registerNamesListed = false;
|
2008-12-02 12:01:29 +01:00
|
|
|
return;
|
2009-10-30 16:42:33 +01:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2010-02-10 11:37:57 +01:00
|
|
|
Registers registers;
|
2009-09-24 11:16:00 +02:00
|
|
|
foreach (const GdbMi &item, response.data.findChild("register-names").children())
|
2010-01-05 16:51:55 +01:00
|
|
|
registers.append(Register(item.data()));
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2010-06-16 11:08:54 +02:00
|
|
|
registerHandler()->setRegisters(registers);
|
2010-02-10 11:37:57 +01:00
|
|
|
|
|
|
|
|
if (m_gdbAdapter->isTrkAdapter())
|
|
|
|
|
m_gdbAdapter->trkReloadRegisters();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-09-24 11:16:00 +02:00
|
|
|
void GdbEngine::handleRegisterListValues(const GdbResponse &response)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-09-24 11:16:00 +02:00
|
|
|
if (response.resultClass != GdbResultDone)
|
2008-12-02 12:01:29 +01:00
|
|
|
return;
|
|
|
|
|
|
2010-06-16 11:08:54 +02:00
|
|
|
Registers registers = registerHandler()->registers();
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
// 24^done,register-values=[{number="0",value="0xf423f"},...]
|
2010-03-22 17:49:56 +01:00
|
|
|
const GdbMi values = response.data.findChild("register-values");
|
|
|
|
|
foreach (const GdbMi &item, values.children()) {
|
|
|
|
|
const int index = item.findChild("number").data().toInt();
|
2008-12-02 12:01:29 +01:00
|
|
|
if (index < registers.size()) {
|
|
|
|
|
Register ® = registers[index];
|
2010-03-22 17:49:56 +01:00
|
|
|
GdbMi val = item.findChild("value");
|
|
|
|
|
QByteArray ba;
|
|
|
|
|
bool handled = false;
|
2011-02-23 15:27:17 +01:00
|
|
|
if (val.data().startsWith('{')) {
|
2010-03-22 17:49:56 +01:00
|
|
|
int pos1 = val.data().indexOf("v2_int32");
|
|
|
|
|
if (pos1 == -1)
|
|
|
|
|
pos1 = val.data().indexOf("v4_int32");
|
|
|
|
|
if (pos1 != -1) {
|
|
|
|
|
// FIXME: This block wastes cycles.
|
2011-02-23 15:27:17 +01:00
|
|
|
pos1 = val.data().indexOf('{', pos1 + 1) + 1;
|
|
|
|
|
int pos2 = val.data().indexOf('}', pos1);
|
2010-03-22 17:49:56 +01:00
|
|
|
QByteArray ba2 = val.data().mid(pos1, pos2 - pos1);
|
|
|
|
|
foreach (QByteArray ba3, ba2.split(',')) {
|
|
|
|
|
ba3 = ba3.trimmed();
|
|
|
|
|
QTC_ASSERT(ba3.size() >= 3, continue);
|
|
|
|
|
QTC_ASSERT(ba3.size() <= 10, continue);
|
|
|
|
|
ba.prepend(QByteArray(10 - ba3.size(), '0'));
|
|
|
|
|
ba.prepend(ba3.mid(2));
|
|
|
|
|
}
|
|
|
|
|
ba.prepend("0x");
|
|
|
|
|
handled = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-09-01 13:56:51 +02:00
|
|
|
reg.value = _(handled ? ba : val.data());
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
2010-09-01 13:56:51 +02:00
|
|
|
registerHandler()->setAndMarkRegisters(registers);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// Thread specific stuff
|
|
|
|
|
//
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
bool GdbEngine::supportsThreads() const
|
|
|
|
|
{
|
2009-08-24 14:11:47 +02:00
|
|
|
// FSF gdb 6.3 crashes happily on -thread-list-ids. So don't use it.
|
2008-12-02 12:01:29 +01:00
|
|
|
// The test below is a semi-random pick, 6.8 works fine
|
2009-10-22 11:08:06 +02:00
|
|
|
return m_isMacGdb || m_gdbVersion > 60500;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-06-24 12:31:09 +02:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// Tooltip specific stuff
|
|
|
|
|
//
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2009-06-24 12:31:09 +02:00
|
|
|
bool GdbEngine::showToolTip()
|
|
|
|
|
{
|
2011-02-11 15:00:13 +01:00
|
|
|
if (m_toolTipContext.isNull())
|
|
|
|
|
return false;
|
|
|
|
|
const QString expression = m_toolTipContext->expression;
|
|
|
|
|
const QByteArray iname = tooltipIName(m_toolTipContext->expression);
|
|
|
|
|
if (DebuggerToolTipManager::debug())
|
|
|
|
|
qDebug() << "GdbEngine::showToolTip " << expression << iname << (*m_toolTipContext);
|
2010-06-29 10:58:09 +02:00
|
|
|
|
2010-11-10 16:33:11 +01:00
|
|
|
if (!debuggerCore()->boolSetting(UseToolTipsInMainEditor)) {
|
2010-06-29 10:58:09 +02:00
|
|
|
watchHandler()->removeData(iname);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-22 17:06:08 +01:00
|
|
|
const QModelIndex index = watchHandler()->itemIndex(iname);
|
|
|
|
|
if (!index.isValid()) {
|
2010-06-29 10:58:09 +02:00
|
|
|
watchHandler()->removeData(iname);
|
2009-06-24 12:31:09 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2011-02-11 15:00:13 +01:00
|
|
|
DebuggerTreeViewToolTipWidget *tw = new DebuggerTreeViewToolTipWidget;
|
|
|
|
|
tw->setDebuggerModel(TooltipsWatch);
|
|
|
|
|
tw->setExpression(expression);
|
|
|
|
|
tw->setContext(*m_toolTipContext);
|
|
|
|
|
tw->acquireEngine(this);
|
2011-02-21 16:45:07 +01:00
|
|
|
DebuggerToolTipManager::instance()->showToolTip(m_toolTipContext->mousePosition,
|
|
|
|
|
m_toolTipContext->editor, tw);
|
2009-06-24 12:31:09 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-11 15:00:13 +01:00
|
|
|
QString GdbEngine::tooltipExpression() const
|
|
|
|
|
{
|
|
|
|
|
return m_toolTipContext.isNull() ? QString() : m_toolTipContext->expression;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::clearToolTip()
|
|
|
|
|
{
|
|
|
|
|
m_toolTipContext.reset();
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-17 10:08:57 +01:00
|
|
|
bool GdbEngine::setToolTipExpression(const QPoint &mousePos,
|
2011-02-11 15:00:13 +01:00
|
|
|
TextEditor::ITextEditor *editor, const DebuggerToolTipContext &contextIn)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-07-09 17:07:59 +02:00
|
|
|
if (state() != InferiorStopOk || !isCppEditor(editor)) {
|
2010-11-29 13:38:02 +01:00
|
|
|
//qDebug() << "SUPPRESSING DEBUGGER TOOLTIP, INFERIOR NOT STOPPED "
|
|
|
|
|
// " OR NOT A CPPEDITOR";
|
2011-02-17 10:08:57 +01:00
|
|
|
return false;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2009-02-16 18:56:20 +01:00
|
|
|
|
2011-02-11 15:00:13 +01:00
|
|
|
DebuggerToolTipContext context = contextIn;
|
2009-05-14 14:29:37 +02:00
|
|
|
int line, column;
|
2011-02-11 15:00:13 +01:00
|
|
|
QString exp = cppExpressionAt(editor, context.position, &line, &column, &context.function);
|
|
|
|
|
if (DebuggerToolTipManager::debug())
|
|
|
|
|
qDebug() << "GdbEngine::setToolTipExpression1 " << exp << context;
|
|
|
|
|
if (exp.isEmpty())
|
2011-02-17 10:08:57 +01:00
|
|
|
return false;
|
2010-09-02 18:32:37 +02:00
|
|
|
|
|
|
|
|
// Extract the first identifier, everything else is considered
|
|
|
|
|
// too dangerous.
|
|
|
|
|
int pos1 = 0, pos2 = exp.size();
|
|
|
|
|
bool inId = false;
|
|
|
|
|
for (int i = 0; i != exp.size(); ++i) {
|
|
|
|
|
const QChar c = exp.at(i);
|
|
|
|
|
const bool isIdChar = c.isLetterOrNumber() || c.unicode() == '_';
|
|
|
|
|
if (inId && !isIdChar) {
|
|
|
|
|
pos2 = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (!inId && isIdChar) {
|
|
|
|
|
inId = true;
|
|
|
|
|
pos1 = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
exp = exp.mid(pos1, pos2 - pos1);
|
|
|
|
|
|
2011-02-11 15:00:13 +01:00
|
|
|
if (exp.isEmpty() || exp.startsWith(_c('#')) || !hasLetterOrNumber(exp) || isKeyWord(exp))
|
2011-02-17 10:08:57 +01:00
|
|
|
return false;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2011-02-11 15:00:13 +01:00
|
|
|
if (exp.startsWith(_c('"')) && exp.endsWith(_c('"')))
|
2011-02-17 10:08:57 +01:00
|
|
|
return false;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-04-30 15:21:37 +02:00
|
|
|
if (exp.startsWith(__("++")) || exp.startsWith(__("--")))
|
2008-12-02 12:01:29 +01:00
|
|
|
exp = exp.mid(2);
|
|
|
|
|
|
2009-04-30 15:21:37 +02:00
|
|
|
if (exp.endsWith(__("++")) || exp.endsWith(__("--")))
|
2008-12-02 12:01:29 +01:00
|
|
|
exp = exp.mid(2);
|
|
|
|
|
|
2009-04-30 15:21:37 +02:00
|
|
|
if (exp.startsWith(_c('<')) || exp.startsWith(_c('[')))
|
2011-02-17 10:08:57 +01:00
|
|
|
return false;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2011-02-11 15:00:13 +01:00
|
|
|
if (hasSideEffects(exp) || exp.isEmpty())
|
2011-02-17 10:08:57 +01:00
|
|
|
return false;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2011-02-11 15:00:13 +01:00
|
|
|
if (!m_toolTipContext.isNull() && m_toolTipContext->expression == exp) {
|
|
|
|
|
showToolTip();
|
2011-02-17 10:08:57 +01:00
|
|
|
return true;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2011-02-11 15:00:13 +01:00
|
|
|
|
|
|
|
|
m_toolTipContext.reset(new GdbToolTipContext(context));
|
|
|
|
|
m_toolTipContext->mousePosition = mousePos;
|
|
|
|
|
m_toolTipContext->expression = exp;
|
2011-02-21 16:45:07 +01:00
|
|
|
m_toolTipContext->editor = editor;
|
2011-02-11 15:00:13 +01:00
|
|
|
if (DebuggerToolTipManager::debug())
|
|
|
|
|
qDebug() << "GdbEngine::setToolTipExpression2 " << exp << (*m_toolTipContext);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2010-08-30 07:52:41 +02:00
|
|
|
if (isSynchronous()) {
|
2010-01-08 18:16:16 +01:00
|
|
|
updateLocals(QVariant());
|
2011-02-17 10:08:57 +01:00
|
|
|
return true;
|
2010-01-08 18:16:16 +01:00
|
|
|
}
|
|
|
|
|
|
2009-06-24 12:31:09 +02:00
|
|
|
WatchData toolTip;
|
2010-01-05 16:51:55 +01:00
|
|
|
toolTip.exp = exp.toLatin1();
|
2009-06-24 12:31:09 +02:00
|
|
|
toolTip.name = exp;
|
2010-07-07 16:30:42 +02:00
|
|
|
toolTip.iname = tooltipIName(exp);
|
2010-06-15 11:42:49 +02:00
|
|
|
watchHandler()->removeData(toolTip.iname);
|
|
|
|
|
watchHandler()->insertData(toolTip);
|
2011-02-17 10:08:57 +01:00
|
|
|
return true;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// Watch specific stuff
|
|
|
|
|
//
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2010-09-23 11:15:56 +02:00
|
|
|
void GdbEngine::reloadLocals()
|
2009-10-06 10:54:08 +02:00
|
|
|
{
|
|
|
|
|
setTokenBarrier();
|
|
|
|
|
updateLocals();
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-01 17:36:09 +02:00
|
|
|
bool GdbEngine::hasDebuggingHelperForType(const QByteArray &type) const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-11-10 16:33:11 +01:00
|
|
|
if (!debuggerCore()->boolSetting(UseDebuggingHelpers))
|
2008-12-02 12:01:29 +01:00
|
|
|
return false;
|
2009-03-03 17:27:25 +01:00
|
|
|
|
2009-11-03 14:06:21 +01:00
|
|
|
if (m_gdbAdapter->dumperHandling() == AbstractGdbAdapter::DumperNotAvailable) {
|
2010-11-29 13:52:43 +01:00
|
|
|
// Inferior calls are not possible in gdb when looking at core files.
|
2010-09-01 17:36:09 +02:00
|
|
|
return type == "QString" || type.endsWith("::QString")
|
|
|
|
|
|| type == "QStringList" || type.endsWith("::QStringList");
|
2009-03-03 17:27:25 +01:00
|
|
|
}
|
|
|
|
|
|
2009-04-07 16:39:17 +02:00
|
|
|
if (m_debuggingHelperState != DebuggingHelperAvailable)
|
2008-12-02 12:01:29 +01:00
|
|
|
return false;
|
2008-12-18 10:11:10 +01:00
|
|
|
|
2010-11-29 13:52:43 +01:00
|
|
|
// Simple types.
|
2009-04-29 14:15:09 +02:00
|
|
|
return m_dumperHelper.type(type) != QtDumperHelper::UnknownType;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2010-09-13 08:32:12 +02:00
|
|
|
void GdbEngine::updateWatchData(const WatchData &data, const WatchUpdateFlags &flags)
|
2009-08-28 17:31:45 +02:00
|
|
|
{
|
2010-08-30 07:52:41 +02:00
|
|
|
if (isSynchronous()) {
|
2009-10-12 14:50:27 +02:00
|
|
|
// This should only be called for fresh expanded items, not for
|
|
|
|
|
// items that had their children retrieved earlier.
|
2009-10-20 16:02:35 +02:00
|
|
|
//qDebug() << "\nUPDATE WATCH DATA: " << data.toString() << "\n";
|
2009-10-12 14:50:27 +02:00
|
|
|
#if 0
|
|
|
|
|
WatchData data1 = data;
|
|
|
|
|
data1.setAllUnneeded();
|
|
|
|
|
insertData(data1);
|
|
|
|
|
rebuildModel();
|
|
|
|
|
#else
|
2010-01-05 16:51:55 +01:00
|
|
|
if (data.iname.endsWith("."))
|
2009-10-12 14:50:27 +02:00
|
|
|
return;
|
2009-10-27 16:36:23 +01:00
|
|
|
|
|
|
|
|
// Avoid endless loops created by faulty dumpers.
|
2010-01-05 16:51:55 +01:00
|
|
|
QByteArray processedName = "1-" + data.iname;
|
2009-10-27 16:36:23 +01:00
|
|
|
//qDebug() << "PROCESSED NAMES: " << processedName << m_processedNames;
|
|
|
|
|
if (m_processedNames.contains(processedName)) {
|
|
|
|
|
WatchData data1 = data;
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("<Breaking endless loop for " + data.iname + '>'),
|
|
|
|
|
LogMiscInput);
|
2009-10-27 16:36:23 +01:00
|
|
|
data1.setAllUnneeded();
|
|
|
|
|
data1.setValue(_("<unavailable>"));
|
|
|
|
|
data1.setHasChildren(false);
|
|
|
|
|
insertData(data1);
|
2010-01-29 21:33:57 +01:00
|
|
|
return;
|
2009-10-27 16:36:23 +01:00
|
|
|
}
|
|
|
|
|
m_processedNames.insert(processedName);
|
|
|
|
|
|
2010-09-13 08:32:12 +02:00
|
|
|
// FIXME: Is this sufficient when "external" changes are
|
|
|
|
|
// triggered e.g. by manually entered command in the gdb console?
|
|
|
|
|
//qDebug() << "TRY PARTIAL: " << flags.tryIncremental
|
|
|
|
|
// << hasPython()
|
|
|
|
|
// << (m_pendingWatchRequests == 0)
|
|
|
|
|
// << (m_pendingBreakpointRequests == 0);
|
|
|
|
|
|
|
|
|
|
bool tryPartial = flags.tryIncremental
|
|
|
|
|
&& hasPython()
|
|
|
|
|
&& m_pendingWatchRequests == 0
|
|
|
|
|
&& m_pendingBreakpointRequests == 0;
|
|
|
|
|
|
|
|
|
|
if (tryPartial)
|
|
|
|
|
updateLocalsPython(true, data.iname);
|
|
|
|
|
else
|
|
|
|
|
updateLocals();
|
2009-10-12 14:50:27 +02:00
|
|
|
#endif
|
|
|
|
|
} else {
|
|
|
|
|
// Bump requests to avoid model rebuilding during the nested
|
|
|
|
|
// updateWatchModel runs.
|
2010-03-02 18:02:30 +01:00
|
|
|
++m_pendingWatchRequests;
|
|
|
|
|
PENDING_DEBUG("UPDATE WATCH BUMPS PENDING UP TO " << m_pendingWatchRequests);
|
2009-08-31 09:14:04 +02:00
|
|
|
#if 1
|
2009-10-12 14:50:27 +02:00
|
|
|
QMetaObject::invokeMethod(this, "updateWatchDataHelper",
|
|
|
|
|
Qt::QueuedConnection, Q_ARG(WatchData, data));
|
2009-08-31 09:14:04 +02:00
|
|
|
#else
|
2009-10-12 14:50:27 +02:00
|
|
|
updateWatchDataHelper(data);
|
2009-08-31 09:14:04 +02:00
|
|
|
#endif
|
2009-10-12 14:50:27 +02:00
|
|
|
}
|
2009-08-28 17:31:45 +02:00
|
|
|
}
|
|
|
|
|
|
2009-08-31 09:14:04 +02:00
|
|
|
void GdbEngine::updateWatchDataHelper(const WatchData &data)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-06-17 16:00:03 +02:00
|
|
|
//m_pendingRequests = 0;
|
|
|
|
|
PENDING_DEBUG("UPDATE WATCH DATA");
|
2010-06-10 13:12:12 +02:00
|
|
|
# if DEBUG_PENDING
|
2009-06-17 16:00:03 +02:00
|
|
|
//qDebug() << "##############################################";
|
2009-08-31 09:14:04 +02:00
|
|
|
qDebug() << "UPDATE MODEL, FOUND INCOMPLETE:";
|
2009-06-17 16:00:03 +02:00
|
|
|
//qDebug() << data.toString();
|
2010-06-10 13:12:12 +02:00
|
|
|
# endif
|
2009-06-17 16:00:03 +02:00
|
|
|
|
2010-01-20 15:38:01 +01:00
|
|
|
updateSubItemClassic(data);
|
2009-06-17 16:00:03 +02:00
|
|
|
//PENDING_DEBUG("INTERNAL TRIGGERING UPDATE WATCH MODEL");
|
2010-03-02 18:02:30 +01:00
|
|
|
--m_pendingWatchRequests;
|
|
|
|
|
PENDING_DEBUG("UPDATE WATCH DONE BUMPS PENDING DOWN TO " << m_pendingWatchRequests);
|
|
|
|
|
if (m_pendingWatchRequests <= 0)
|
|
|
|
|
rebuildWatchModel();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-03-02 18:02:30 +01:00
|
|
|
void GdbEngine::rebuildWatchModel()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-08-31 09:14:04 +02:00
|
|
|
static int count = 0;
|
|
|
|
|
++count;
|
2010-08-30 07:52:41 +02:00
|
|
|
if (!isSynchronous())
|
2009-10-27 16:36:23 +01:00
|
|
|
m_processedNames.clear();
|
2009-08-31 09:14:04 +02:00
|
|
|
PENDING_DEBUG("REBUILDING MODEL" << count);
|
2010-11-10 16:33:11 +01:00
|
|
|
if (debuggerCore()->boolSetting(LogTimeStamps))
|
2010-09-22 16:20:08 +02:00
|
|
|
showMessage(LogWindow::logTimeStamp(), LogMiscInput);
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("<Rebuild Watchmodel %1>").arg(count), LogMiscInput);
|
2010-03-23 14:49:02 +01:00
|
|
|
showStatusMessage(tr("Finished retrieving data"), 400);
|
2010-06-15 11:42:49 +02:00
|
|
|
watchHandler()->endCycle();
|
2009-06-24 12:31:09 +02:00
|
|
|
showToolTip();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-01-20 15:38:01 +01:00
|
|
|
static QByteArray arrayFillCommand(const char *array, const QByteArray ¶ms)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-06-11 13:41:31 +10:00
|
|
|
QString buf;
|
|
|
|
|
buf.sprintf("set {char[%d]} &%s = {", params.size(), array);
|
2008-12-02 12:01:29 +01:00
|
|
|
QByteArray encoded;
|
2010-06-11 13:41:31 +10:00
|
|
|
encoded.append(buf.toLocal8Bit());
|
2009-07-06 17:36:50 +02:00
|
|
|
const int size = params.size();
|
|
|
|
|
for (int i = 0; i != size; ++i) {
|
2010-06-11 13:41:31 +10:00
|
|
|
buf.sprintf("%d,", int(params[i]));
|
|
|
|
|
encoded.append(buf.toLocal8Bit());
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
encoded[encoded.size() - 1] = '}';
|
2010-01-05 16:51:55 +01:00
|
|
|
return encoded;
|
2009-07-06 17:36:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::sendWatchParameters(const QByteArray ¶ms0)
|
|
|
|
|
{
|
|
|
|
|
QByteArray params = params0;
|
|
|
|
|
params.append('\0');
|
2010-01-05 16:51:55 +01:00
|
|
|
const QByteArray inBufferCmd = arrayFillCommand("qDumpInBuffer", params);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-06-02 15:44:03 +02:00
|
|
|
params.replace('\0','!');
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(QString::fromUtf8(params), LogMiscInput);
|
2009-06-02 15:44:03 +02:00
|
|
|
|
2009-07-06 17:36:50 +02:00
|
|
|
params.clear();
|
|
|
|
|
params.append('\0');
|
2010-01-05 16:51:55 +01:00
|
|
|
const QByteArray outBufferCmd = arrayFillCommand("qDumpOutBuffer", params);
|
2009-07-06 17:36:50 +02:00
|
|
|
|
|
|
|
|
postCommand(inBufferCmd);
|
|
|
|
|
postCommand(outBufferCmd);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-09-24 11:16:00 +02:00
|
|
|
void GdbEngine::handleVarAssign(const GdbResponse &)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-10-05 12:00:47 +02:00
|
|
|
// Everything might have changed, force re-evaluation.
|
2009-02-18 13:50:55 +01:00
|
|
|
setTokenBarrier();
|
2008-12-02 12:01:29 +01:00
|
|
|
updateLocals();
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-24 11:16:00 +02:00
|
|
|
void GdbEngine::handleVarCreate(const GdbResponse &response)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-09-24 11:16:00 +02:00
|
|
|
WatchData data = response.cookie.value<WatchData>();
|
2010-03-11 18:55:52 +01:00
|
|
|
// Happens e.g. when we already issued a var-evaluate command.
|
2008-12-02 12:01:29 +01:00
|
|
|
if (!data.isValid())
|
|
|
|
|
return;
|
2009-05-06 20:55:21 +02:00
|
|
|
//qDebug() << "HANDLE VARIABLE CREATION:" << data.toString();
|
2009-09-24 11:16:00 +02:00
|
|
|
if (response.resultClass == GdbResultDone) {
|
2008-12-02 12:01:29 +01:00
|
|
|
data.variable = data.iname;
|
2009-09-24 11:16:00 +02:00
|
|
|
setWatchDataType(data, response.data.findChild("type"));
|
2010-06-16 11:08:54 +02:00
|
|
|
if (watchHandler()->isExpandedIName(data.iname)
|
2009-11-04 17:44:11 +01:00
|
|
|
&& !response.data.findChild("children").isValid())
|
|
|
|
|
data.setChildrenNeeded();
|
|
|
|
|
else
|
|
|
|
|
data.setChildrenUnneeded();
|
|
|
|
|
setWatchDataChildCount(data, response.data.findChild("numchild"));
|
|
|
|
|
insertData(data);
|
2009-10-12 12:00:07 +02:00
|
|
|
} else {
|
2009-09-24 11:16:00 +02:00
|
|
|
data.setError(QString::fromLocal8Bit(response.data.findChild("msg").data()));
|
2008-12-02 12:01:29 +01:00
|
|
|
if (data.isWatcher()) {
|
2009-10-16 16:26:28 +02:00
|
|
|
data.value = WatchData::msgNotInScope();
|
2010-09-01 17:36:09 +02:00
|
|
|
data.type = " ";
|
2008-12-02 12:01:29 +01:00
|
|
|
data.setAllUnneeded();
|
2009-06-22 11:35:08 +02:00
|
|
|
data.setHasChildren(false);
|
2009-08-31 09:14:04 +02:00
|
|
|
data.valueEnabled = false;
|
|
|
|
|
data.valueEditable = false;
|
2008-12-02 12:01:29 +01:00
|
|
|
insertData(data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-24 11:16:00 +02:00
|
|
|
void GdbEngine::handleDebuggingHelperSetup(const GdbResponse &response)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-09-24 11:16:00 +02:00
|
|
|
if (response.resultClass == GdbResultDone) {
|
2009-10-12 12:00:07 +02:00
|
|
|
} else {
|
2009-09-24 11:16:00 +02:00
|
|
|
QString msg = QString::fromLocal8Bit(response.data.findChild("msg").data());
|
2009-09-14 11:02:36 +02:00
|
|
|
showStatusMessage(tr("Custom dumper setup: %1").arg(msg), 10000);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-16 16:26:28 +02:00
|
|
|
void GdbEngine::updateLocals(const QVariant &cookie)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-03-02 18:02:30 +01:00
|
|
|
m_pendingWatchRequests = 0;
|
2010-03-03 18:08:18 +01:00
|
|
|
m_pendingBreakpointRequests = 0;
|
2010-01-20 15:38:01 +01:00
|
|
|
if (hasPython())
|
2010-09-13 08:32:12 +02:00
|
|
|
updateLocalsPython(false, QByteArray());
|
2010-01-29 22:49:55 +01:00
|
|
|
else
|
2010-01-20 15:38:01 +01:00
|
|
|
updateLocalsClassic(cookie);
|
2010-12-13 18:17:31 +01:00
|
|
|
updateMemoryViews();
|
2009-10-12 14:50:27 +02:00
|
|
|
}
|
|
|
|
|
|
2008-12-03 16:51:56 +01:00
|
|
|
|
2010-03-11 18:55:52 +01:00
|
|
|
// Parse a local variable from GdbMi.
|
2009-10-16 16:26:28 +02:00
|
|
|
WatchData GdbEngine::localVariable(const GdbMi &item,
|
|
|
|
|
const QStringList &uninitializedVariables,
|
|
|
|
|
QMap<QByteArray, int> *seen)
|
2009-02-16 18:56:20 +01:00
|
|
|
{
|
2009-10-16 16:26:28 +02:00
|
|
|
// Local variables of inlined code are reported as
|
|
|
|
|
// 26^done,locals={varobj={exp="this",value="",name="var4",exp="this",
|
2009-10-20 16:02:35 +02:00
|
|
|
// numchild="1",type="const QtSharedPointer::Basic<CPlusPlus::..."}}
|
2009-10-16 16:26:28 +02:00
|
|
|
// We do not want these at all. Current hypotheses is that those
|
|
|
|
|
// "spurious" locals have _two_ "exp" field. Try to filter them:
|
2009-10-22 11:08:06 +02:00
|
|
|
QByteArray name;
|
|
|
|
|
if (m_isMacGdb) {
|
|
|
|
|
int numExps = 0;
|
|
|
|
|
foreach (const GdbMi &child, item.children())
|
|
|
|
|
numExps += int(child.name() == "exp");
|
|
|
|
|
if (numExps > 1)
|
|
|
|
|
return WatchData();
|
|
|
|
|
name = item.findChild("exp").data();
|
|
|
|
|
} else {
|
|
|
|
|
name = item.findChild("name").data();
|
|
|
|
|
}
|
2009-10-16 16:26:28 +02:00
|
|
|
const QMap<QByteArray, int>::iterator it = seen->find(name);
|
|
|
|
|
if (it != seen->end()) {
|
|
|
|
|
const int n = it.value();
|
|
|
|
|
++(it.value());
|
|
|
|
|
WatchData data;
|
|
|
|
|
QString nam = _(name);
|
2010-01-05 16:51:55 +01:00
|
|
|
data.iname = "local." + name + QByteArray::number(n + 1);
|
2009-10-16 16:26:28 +02:00
|
|
|
data.name = WatchData::shadowedName(nam, n);
|
|
|
|
|
if (uninitializedVariables.contains(data.name)) {
|
|
|
|
|
data.setError(WatchData::msgNotInScope());
|
|
|
|
|
return data;
|
|
|
|
|
}
|
2010-09-10 10:51:43 +02:00
|
|
|
setWatchDataValue(data, item);
|
2010-01-29 21:33:57 +01:00
|
|
|
//: Type of local variable or parameter shadowed by another
|
2009-10-16 16:26:28 +02:00
|
|
|
//: variable of the same name in a nested block.
|
2010-09-01 17:36:09 +02:00
|
|
|
data.setType(GdbEngine::tr("<shadowed>").toUtf8());
|
2009-10-16 16:26:28 +02:00
|
|
|
data.setHasChildren(false);
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
seen->insert(name, 1);
|
|
|
|
|
WatchData data;
|
|
|
|
|
QString nam = _(name);
|
2010-01-05 16:51:55 +01:00
|
|
|
data.iname = "local." + name;
|
2009-10-16 16:26:28 +02:00
|
|
|
data.name = nam;
|
2010-01-05 16:51:55 +01:00
|
|
|
data.exp = name;
|
2009-10-16 16:26:28 +02:00
|
|
|
setWatchDataType(data, item.findChild("type"));
|
|
|
|
|
if (uninitializedVariables.contains(data.name)) {
|
|
|
|
|
data.setError(WatchData::msgNotInScope());
|
|
|
|
|
return data;
|
|
|
|
|
}
|
2010-08-30 07:52:41 +02:00
|
|
|
if (isSynchronous()) {
|
2010-04-08 14:38:42 +02:00
|
|
|
setWatchDataValue(data, item);
|
2009-10-16 16:26:28 +02:00
|
|
|
// We know that the complete list of children is
|
|
|
|
|
// somewhere in the response.
|
|
|
|
|
data.setChildrenUnneeded();
|
|
|
|
|
} else {
|
2010-03-11 18:55:52 +01:00
|
|
|
// Set value only directly if it is simple enough, otherwise
|
|
|
|
|
// pass through the insertData() machinery.
|
2009-10-16 16:26:28 +02:00
|
|
|
if (isIntOrFloatType(data.type) || isPointerType(data.type))
|
2010-04-08 14:38:42 +02:00
|
|
|
setWatchDataValue(data, item);
|
2009-10-16 16:26:28 +02:00
|
|
|
if (isSymbianIntType(data.type)) {
|
2010-04-08 14:38:42 +02:00
|
|
|
setWatchDataValue(data, item);
|
2009-06-22 11:35:08 +02:00
|
|
|
data.setHasChildren(false);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
2009-10-16 16:26:28 +02:00
|
|
|
|
2010-06-15 12:15:25 +02:00
|
|
|
if (!watchHandler()->isExpandedIName(data.iname))
|
2009-10-16 16:26:28 +02:00
|
|
|
data.setChildrenUnneeded();
|
2010-03-11 18:55:52 +01:00
|
|
|
|
|
|
|
|
GdbMi t = item.findChild("numchild");
|
|
|
|
|
if (t.isValid())
|
|
|
|
|
data.setHasChildren(t.data().toInt() > 0);
|
|
|
|
|
else if (isPointerType(data.type) || data.name == __("this"))
|
2009-10-16 16:26:28 +02:00
|
|
|
data.setHasChildren(true);
|
|
|
|
|
return data;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::insertData(const WatchData &data0)
|
|
|
|
|
{
|
2009-10-01 18:01:23 +02:00
|
|
|
PENDING_DEBUG("INSERT DATA" << data0.toString());
|
2008-12-02 12:01:29 +01:00
|
|
|
WatchData data = data0;
|
2009-04-30 15:21:37 +02:00
|
|
|
if (data.value.startsWith(__("mi_cmd_var_create:"))) {
|
2009-05-06 20:55:21 +02:00
|
|
|
qDebug() << "BOGUS VALUE:" << data.toString();
|
2008-12-02 12:01:29 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2010-06-16 11:08:54 +02:00
|
|
|
watchHandler()->insertData(data);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-11-10 16:33:11 +01:00
|
|
|
void GdbEngine::assignValueInDebugger(const WatchData *,
|
|
|
|
|
const QString &expression, const QVariant &value)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("-var-delete assign");
|
|
|
|
|
postCommand("-var-create assign * " + expression.toLatin1());
|
2010-09-23 13:22:08 +02:00
|
|
|
postCommand("-var-assign assign " + GdbMi::escapeCString(value.toString().toLatin1()),
|
2010-02-11 13:21:38 +01:00
|
|
|
Discardable, CB(handleVarAssign));
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-07-01 12:49:41 +02:00
|
|
|
void GdbEngine::watchPoint(const QPoint &pnt)
|
|
|
|
|
{
|
2010-01-05 16:51:55 +01:00
|
|
|
QByteArray x = QByteArray::number(pnt.x());
|
|
|
|
|
QByteArray y = QByteArray::number(pnt.y());
|
2011-01-17 15:11:11 +01:00
|
|
|
postCommand("print '" + qtNamespace() + "QApplication::widgetAt'("
|
|
|
|
|
+ x + ',' + y + ')',
|
2009-07-01 12:49:41 +02:00
|
|
|
NeedsStop, CB(handleWatchPoint));
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-24 11:16:00 +02:00
|
|
|
void GdbEngine::handleWatchPoint(const GdbResponse &response)
|
2009-07-01 12:49:41 +02:00
|
|
|
{
|
2009-09-24 11:16:00 +02:00
|
|
|
if (response.resultClass == GdbResultDone) {
|
2009-07-01 12:49:41 +02:00
|
|
|
// "$5 = (void *) 0xbfa7ebfc\n"
|
2011-01-17 15:11:11 +01:00
|
|
|
const QByteArray ba = parsePlainConsoleStream(response);
|
|
|
|
|
//qDebug() << "BA: " << ba;
|
2011-01-17 12:27:49 +01:00
|
|
|
const int posWidget = ba.indexOf("QWidget");
|
|
|
|
|
const int pos0x = ba.indexOf("0x", posWidget + 7);
|
2011-01-17 16:33:31 +01:00
|
|
|
if (posWidget == -1 || pos0x == -1) {
|
2011-01-17 15:11:11 +01:00
|
|
|
showStatusMessage(tr("Cannot read widget data: %1").arg(_(ba)));
|
|
|
|
|
} else {
|
|
|
|
|
const QByteArray addr = ba.mid(pos0x);
|
2011-01-17 16:33:31 +01:00
|
|
|
if (addr.toULongLong(0, 0)) { // Non-null pointer
|
|
|
|
|
const QByteArray ns = qtNamespace();
|
|
|
|
|
const QByteArray type = ns.isEmpty() ? "QWidget*" : ("'" + ns + "QWidget'*");
|
|
|
|
|
const QString exp = _("(*(struct %1)%2)").arg(_(type)).arg(_(addr));
|
|
|
|
|
// qDebug() << posNs << posWidget << pos0x << addr << ns << type;
|
|
|
|
|
watchHandler()->watchExpression(exp);
|
|
|
|
|
} else {
|
|
|
|
|
showStatusMessage(tr("Could not find a widget."));
|
|
|
|
|
}
|
2011-01-17 15:11:11 +01:00
|
|
|
}
|
2009-07-01 12:49:41 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-08-18 08:34:48 +02:00
|
|
|
|
|
|
|
|
struct MemoryAgentCookie
|
2009-08-14 13:04:05 +02:00
|
|
|
{
|
2010-02-17 17:33:42 +01:00
|
|
|
MemoryAgentCookie() : agent(0), token(0), address(0) {}
|
2010-12-14 12:29:32 +01:00
|
|
|
MemoryAgentCookie(MemoryAgent *agent_, QObject *token_, quint64 address_)
|
2010-02-17 17:33:42 +01:00
|
|
|
: agent(agent_), token(token_), address(address_)
|
2009-08-18 08:34:48 +02:00
|
|
|
{}
|
2010-12-14 12:29:32 +01:00
|
|
|
QPointer<MemoryAgent> agent;
|
2010-02-17 17:33:42 +01:00
|
|
|
QPointer<QObject> token;
|
2009-08-18 08:34:48 +02:00
|
|
|
quint64 address;
|
|
|
|
|
};
|
2009-08-14 13:04:05 +02:00
|
|
|
|
2011-02-25 13:21:54 +01:00
|
|
|
void GdbEngine::changeMemory(MemoryAgent *agent, QObject *token,
|
|
|
|
|
quint64 addr, const QByteArray &data)
|
|
|
|
|
{
|
|
|
|
|
QByteArray cmd = "-data-write-memory " + QByteArray::number(addr) + " d 1";
|
|
|
|
|
foreach (char c, data) {
|
|
|
|
|
cmd.append(' ');
|
|
|
|
|
cmd.append(QByteArray::number(uint(c)));
|
|
|
|
|
}
|
|
|
|
|
postCommand(cmd, NeedsStop, CB(handleChangeMemory),
|
|
|
|
|
QVariant::fromValue(MemoryAgentCookie(agent, token, addr)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::handleChangeMemory(const GdbResponse &response)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(response);
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-14 12:29:32 +01:00
|
|
|
void GdbEngine::fetchMemory(MemoryAgent *agent, QObject *token, quint64 addr,
|
2010-02-17 17:33:42 +01:00
|
|
|
quint64 length)
|
2009-08-14 13:04:05 +02:00
|
|
|
{
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("-data-read-memory " + QByteArray::number(addr) + " x 1 1 "
|
|
|
|
|
+ QByteArray::number(length),
|
2009-08-18 08:34:48 +02:00
|
|
|
NeedsStop, CB(handleFetchMemory),
|
2010-02-17 17:33:42 +01:00
|
|
|
QVariant::fromValue(MemoryAgentCookie(agent, token, addr)));
|
2009-08-14 13:04:05 +02:00
|
|
|
}
|
|
|
|
|
|
2009-09-24 11:16:00 +02:00
|
|
|
void GdbEngine::handleFetchMemory(const GdbResponse &response)
|
2009-08-12 10:51:25 +02:00
|
|
|
{
|
|
|
|
|
// ^done,addr="0x08910c88",nr-bytes="16",total-bytes="16",
|
|
|
|
|
// next-row="0x08910c98",prev-row="0x08910c78",next-page="0x08910c98",
|
|
|
|
|
// prev-page="0x08910c78",memory=[{addr="0x08910c88",
|
|
|
|
|
// data=["1","0","0","0","5","0","0","0","0","0","0","0","0","0","0","0"]}]
|
2009-09-24 11:16:00 +02:00
|
|
|
MemoryAgentCookie ac = response.cookie.value<MemoryAgentCookie>();
|
2009-08-18 08:34:48 +02:00
|
|
|
QTC_ASSERT(ac.agent, return);
|
2009-08-12 10:51:25 +02:00
|
|
|
QByteArray ba;
|
2009-09-24 11:16:00 +02:00
|
|
|
GdbMi memory = response.data.findChild("memory");
|
2009-08-14 13:04:05 +02:00
|
|
|
QTC_ASSERT(memory.children().size() <= 1, return);
|
2009-08-18 08:34:48 +02:00
|
|
|
if (memory.children().isEmpty())
|
|
|
|
|
return;
|
2009-08-12 10:51:25 +02:00
|
|
|
GdbMi memory0 = memory.children().at(0); // we asked for only one 'row'
|
|
|
|
|
GdbMi data = memory0.findChild("data");
|
|
|
|
|
foreach (const GdbMi &child, data.children()) {
|
2009-08-18 08:34:48 +02:00
|
|
|
bool ok = true;
|
2009-10-05 17:37:15 +02:00
|
|
|
unsigned char c = '?';
|
|
|
|
|
c = child.data().toUInt(&ok, 0);
|
2009-08-12 10:51:25 +02:00
|
|
|
QTC_ASSERT(ok, return);
|
|
|
|
|
ba.append(c);
|
|
|
|
|
}
|
2010-02-17 17:33:42 +01:00
|
|
|
ac.agent->addLazyData(ac.token, ac.address, ba);
|
2009-08-12 10:51:25 +02:00
|
|
|
}
|
|
|
|
|
|
2010-04-12 12:01:40 +02:00
|
|
|
class DisassemblerAgentCookie
|
2009-08-18 08:34:48 +02:00
|
|
|
{
|
2010-04-12 12:01:40 +02:00
|
|
|
public:
|
2011-02-10 13:43:02 +01:00
|
|
|
DisassemblerAgentCookie() : agent(0) {}
|
|
|
|
|
DisassemblerAgentCookie(DisassemblerAgent *agent_) : agent(agent_) {}
|
2010-04-12 12:01:40 +02:00
|
|
|
|
|
|
|
|
public:
|
2010-12-14 12:29:32 +01:00
|
|
|
QPointer<DisassemblerAgent> agent;
|
2009-08-18 08:34:48 +02:00
|
|
|
};
|
|
|
|
|
|
2010-12-14 12:29:32 +01:00
|
|
|
void GdbEngine::fetchDisassembler(DisassemblerAgent *agent)
|
2009-08-12 10:51:25 +02:00
|
|
|
{
|
2011-02-10 13:43:02 +01:00
|
|
|
// As of 7.2 the MI output is often less informative then the CLI version.
|
|
|
|
|
// So globally fall back to CLI.
|
|
|
|
|
if (agent->isMixed())
|
|
|
|
|
fetchDisassemblerByCliPointMixed(agent);
|
|
|
|
|
else
|
|
|
|
|
fetchDisassemblerByCliPointPlain(agent);
|
|
|
|
|
#if 0
|
|
|
|
|
if (agent->isMixed())
|
|
|
|
|
fetchDisassemblerByMiRangeMixed(agent)
|
|
|
|
|
else
|
|
|
|
|
fetchDisassemblerByMiRangePlain(agent);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
void GdbEngine::fetchDisassemblerByMiRangePlain(const DisassemblerAgentCookie &ac0)
|
|
|
|
|
{
|
|
|
|
|
// Disassemble full function:
|
|
|
|
|
const StackFrame &frame = agent->frame();
|
|
|
|
|
DisassemblerAgentCookie ac = ac0;
|
|
|
|
|
QTC_ASSERT(ac.agent, return);
|
|
|
|
|
const quint64 address = ac.agent->address();
|
|
|
|
|
QByteArray cmd = "-data-disassemble"
|
|
|
|
|
" -f " + frame.file.toLocal8Bit() +
|
|
|
|
|
" -l " + QByteArray::number(frame.line) + " -n -1 -- 1";
|
|
|
|
|
postCommand(cmd, Discardable, CB(handleFetchDisassemblerByMiPointMixed),
|
|
|
|
|
QVariant::fromValue(DisassemblerAgentCookie(agent)));
|
2009-08-14 13:04:05 +02:00
|
|
|
}
|
2011-02-10 13:43:02 +01:00
|
|
|
#endif
|
2009-08-14 13:04:05 +02:00
|
|
|
|
2011-02-10 13:43:02 +01:00
|
|
|
#if 0
|
|
|
|
|
void GdbEngine::fetchDisassemblerByMiRangeMixed(const DisassemblerAgentCookie &ac0)
|
2009-08-14 13:04:05 +02:00
|
|
|
{
|
2010-04-12 12:01:40 +02:00
|
|
|
DisassemblerAgentCookie ac = ac0;
|
|
|
|
|
QTC_ASSERT(ac.agent, return);
|
2010-09-21 15:12:33 +02:00
|
|
|
const quint64 address = ac.agent->address();
|
2010-01-05 16:51:55 +01:00
|
|
|
QByteArray start = QByteArray::number(address - 20, 16);
|
|
|
|
|
QByteArray end = QByteArray::number(address + 100, 16);
|
2009-08-14 13:04:05 +02:00
|
|
|
// -data-disassemble [ -s start-addr -e end-addr ]
|
|
|
|
|
// | [ -f filename -l linenum [ -n lines ] ] -- mode
|
2011-02-10 13:43:02 +01:00
|
|
|
postCommand("-data-disassemble -s 0x" + start + " -e 0x" + end + " -- 1",
|
|
|
|
|
Discardable, CB(handleFetchDisassemblerByMiRangeMixed),
|
|
|
|
|
QVariant::fromValue(ac));
|
2009-08-14 13:04:05 +02:00
|
|
|
}
|
2011-02-10 13:43:02 +01:00
|
|
|
#endif
|
2009-08-14 13:04:05 +02:00
|
|
|
|
2011-02-10 13:43:02 +01:00
|
|
|
#if 0
|
|
|
|
|
void GdbEngine::fetchDisassemblerByMiRangePlain(const DisassemblerAgentCookie &ac0)
|
2010-02-09 15:02:55 +01:00
|
|
|
{
|
2010-04-12 12:01:40 +02:00
|
|
|
DisassemblerAgentCookie ac = ac0;
|
|
|
|
|
QTC_ASSERT(ac.agent, return);
|
2010-09-21 15:12:33 +02:00
|
|
|
const quint64 address = ac.agent->address();
|
2011-02-10 13:43:02 +01:00
|
|
|
QByteArray start = QByteArray::number(address - 20, 16);
|
|
|
|
|
QByteArray end = QByteArray::number(address + 100, 16);
|
|
|
|
|
// -data-disassemble [ -s start-addr -e end-addr ]
|
|
|
|
|
// | [ -f filename -l linenum [ -n lines ] ] -- mode
|
|
|
|
|
postCommand("-data-disassemble -s 0x" + start + " -e 0x" + end + " -- 0",
|
|
|
|
|
Discardable, CB(handleFetchDisassemblerByMiRangePlain),
|
|
|
|
|
QVariant::fromValue(ac));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
void GdbEngine::fetchDisassemblerByCliPointMixed(const DisassemblerAgentCookie &ac0)
|
|
|
|
|
{
|
|
|
|
|
DisassemblerAgentCookie ac = ac0;
|
|
|
|
|
QTC_ASSERT(ac.agent, return);
|
|
|
|
|
const quint64 address = ac.agent->address();
|
|
|
|
|
QByteArray cmd = "disassemble /m 0x" + QByteArray::number(address, 16);
|
|
|
|
|
postCommand(cmd, Discardable, CB(handleFetchDisassemblerByCliPointMixed),
|
|
|
|
|
QVariant::fromValue(ac));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::fetchDisassemblerByCliPointPlain(const DisassemblerAgentCookie &ac0)
|
|
|
|
|
{
|
|
|
|
|
DisassemblerAgentCookie ac = ac0;
|
|
|
|
|
QTC_ASSERT(ac.agent, return);
|
|
|
|
|
const quint64 address = ac.agent->address();
|
|
|
|
|
QByteArray cmd = "disassemble 0x" + QByteArray::number(address, 16);
|
|
|
|
|
postCommand(cmd, Discardable, CB(handleFetchDisassemblerByCliPointPlain),
|
|
|
|
|
QVariant::fromValue(ac));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::fetchDisassemblerByCliRangeMixed(const DisassemblerAgentCookie &ac0)
|
|
|
|
|
{
|
|
|
|
|
DisassemblerAgentCookie ac = ac0;
|
|
|
|
|
QTC_ASSERT(ac.agent, return);
|
|
|
|
|
const quint64 address = ac.agent->address();
|
|
|
|
|
QByteArray start = QByteArray::number(address - 20, 16);
|
|
|
|
|
QByteArray end = QByteArray::number(address + 100, 16);
|
|
|
|
|
const char sep = m_disassembleUsesComma ? ',' : ' ';
|
|
|
|
|
QByteArray cmd = "disassemble /m 0x" + start + sep + "0x" + end;
|
|
|
|
|
postCommand(cmd, Discardable, CB(handleFetchDisassemblerByCliRangeMixed),
|
2010-04-12 12:01:40 +02:00
|
|
|
QVariant::fromValue(ac));
|
2010-02-09 15:02:55 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-10 13:43:02 +01:00
|
|
|
void GdbEngine::fetchDisassemblerByCliRangePlain(const DisassemblerAgentCookie &ac0)
|
2010-02-09 15:02:55 +01:00
|
|
|
{
|
2010-04-12 12:01:40 +02:00
|
|
|
DisassemblerAgentCookie ac = ac0;
|
|
|
|
|
QTC_ASSERT(ac.agent, return);
|
2010-09-21 15:12:33 +02:00
|
|
|
const quint64 address = ac.agent->address();
|
2010-02-09 15:02:55 +01:00
|
|
|
QByteArray start = QByteArray::number(address - 20, 16);
|
|
|
|
|
QByteArray end = QByteArray::number(address + 100, 16);
|
2011-02-10 13:43:02 +01:00
|
|
|
const char sep = m_disassembleUsesComma ? ',' : ' ';
|
2010-04-29 12:07:24 +02:00
|
|
|
QByteArray cmd = "disassemble 0x" + start + sep + "0x" + end;
|
2011-02-10 13:43:02 +01:00
|
|
|
postCommand(cmd, Discardable, CB(handleFetchDisassemblerByCliRangePlain),
|
2010-04-12 12:01:40 +02:00
|
|
|
QVariant::fromValue(ac));
|
2010-02-09 15:02:55 +01:00
|
|
|
}
|
|
|
|
|
|
2010-11-24 15:55:09 +01:00
|
|
|
static DisassemblerLine parseLine(const GdbMi &line)
|
2009-08-14 13:04:05 +02:00
|
|
|
{
|
2010-11-24 15:55:09 +01:00
|
|
|
DisassemblerLine dl;
|
2009-08-14 13:04:05 +02:00
|
|
|
QByteArray address = line.findChild("address").data();
|
2010-11-24 15:55:09 +01:00
|
|
|
dl.address = address.toULongLong();
|
|
|
|
|
dl.data = _(line.findChild("inst").data());
|
|
|
|
|
return dl;
|
2009-08-14 13:04:05 +02:00
|
|
|
}
|
|
|
|
|
|
2011-02-10 13:43:02 +01:00
|
|
|
DisassemblerLines GdbEngine::parseMiDisassembler(const GdbMi &lines)
|
2009-08-14 13:04:05 +02:00
|
|
|
{
|
|
|
|
|
// ^done,data={asm_insns=[src_and_asm_line={line="1243",file=".../app.cpp",
|
|
|
|
|
// line_asm_insn=[{address="0x08054857",func-name="main",offset="27",
|
|
|
|
|
// inst="call 0x80545b0 <_Z13testQFileInfov>"}]},
|
|
|
|
|
// src_and_asm_line={line="1244",file=".../app.cpp",
|
|
|
|
|
// line_asm_insn=[{address="0x0805485c",func-name="main",offset="32",
|
|
|
|
|
//inst="call 0x804cba1 <_Z11testObject1v>"}]}]}
|
|
|
|
|
// - or -
|
|
|
|
|
// ^done,asm_insns=[
|
|
|
|
|
// {address="0x0805acf8",func-name="...",offset="25",inst="and $0xe8,%al"},
|
|
|
|
|
// {address="0x0805acfa",func-name="...",offset="27",inst="pop %esp"},
|
|
|
|
|
|
2010-11-24 15:55:09 +01:00
|
|
|
QStringList fileContents;
|
2009-08-14 13:04:05 +02:00
|
|
|
bool fileLoaded = false;
|
2010-11-24 15:55:09 +01:00
|
|
|
DisassemblerLines result;
|
2009-08-14 13:04:05 +02:00
|
|
|
|
|
|
|
|
// FIXME: Performance?
|
|
|
|
|
foreach (const GdbMi &child, lines.children()) {
|
|
|
|
|
if (child.hasName("src_and_asm_line")) {
|
2010-11-19 09:14:08 +01:00
|
|
|
// Mixed mode.
|
2009-08-14 13:04:05 +02:00
|
|
|
if (!fileLoaded) {
|
2009-10-22 20:04:59 +02:00
|
|
|
QString fileName = QFile::decodeName(child.findChild("file").data());
|
2009-12-09 11:13:20 +01:00
|
|
|
fileName = cleanupFullName(fileName);
|
|
|
|
|
QFile file(fileName);
|
2009-08-14 13:04:05 +02:00
|
|
|
file.open(QIODevice::ReadOnly);
|
2010-11-24 15:55:09 +01:00
|
|
|
QTextStream ts(&file);
|
|
|
|
|
fileContents = ts.readAll().split(QLatin1Char('\n'));
|
2009-08-14 13:04:05 +02:00
|
|
|
fileLoaded = true;
|
|
|
|
|
}
|
2009-10-22 20:04:59 +02:00
|
|
|
int line = child.findChild("line").data().toInt();
|
2010-02-04 15:01:42 +01:00
|
|
|
if (line >= 1 && line <= fileContents.size())
|
2010-11-24 15:55:09 +01:00
|
|
|
result.appendComment(fileContents.at(line - 1));
|
2009-08-14 13:04:05 +02:00
|
|
|
GdbMi insn = child.findChild("line_asm_insn");
|
2010-11-24 15:55:09 +01:00
|
|
|
foreach (const GdbMi &item, insn.children())
|
|
|
|
|
result.appendLine(parseLine(item));
|
2009-08-14 13:04:05 +02:00
|
|
|
} else {
|
2010-11-19 09:14:08 +01:00
|
|
|
// The non-mixed version.
|
2010-11-24 15:55:09 +01:00
|
|
|
result.appendLine(parseLine(child));
|
2009-08-14 13:04:05 +02:00
|
|
|
}
|
|
|
|
|
}
|
2010-11-24 15:55:09 +01:00
|
|
|
return result;
|
2009-08-12 10:51:25 +02:00
|
|
|
}
|
|
|
|
|
|
2011-02-10 13:43:02 +01:00
|
|
|
DisassemblerLines GdbEngine::parseCliDisassembler(const GdbMi &output)
|
|
|
|
|
{
|
|
|
|
|
const QString someSpace = _(" ");
|
|
|
|
|
// First line is something like
|
|
|
|
|
// "Dump of assembler code from 0xb7ff598f to 0xb7ff5a07:"
|
|
|
|
|
DisassemblerLines dlines;
|
|
|
|
|
QByteArray lastFunction;
|
|
|
|
|
foreach (const QByteArray &line0, output.data().split('\n')) {
|
|
|
|
|
QByteArray line = line0.trimmed();
|
|
|
|
|
if (line.startsWith("=> "))
|
|
|
|
|
line = line.mid(3);
|
|
|
|
|
if (line.isEmpty())
|
|
|
|
|
continue;
|
|
|
|
|
if (line.startsWith("Current language:"))
|
|
|
|
|
continue;
|
|
|
|
|
if (line.startsWith("Dump of assembler"))
|
|
|
|
|
continue;
|
|
|
|
|
if (line.startsWith("The current source"))
|
|
|
|
|
continue;
|
|
|
|
|
if (line.startsWith("End of assembler"))
|
|
|
|
|
continue;
|
|
|
|
|
if (line.startsWith("0x")) {
|
|
|
|
|
int pos1 = line.indexOf('<') + 1;
|
|
|
|
|
int pos2 = line.indexOf('+', pos1);
|
|
|
|
|
int pos3 = line.indexOf('>', pos1);
|
|
|
|
|
if (pos1 < pos2 && pos2 < pos3) {
|
|
|
|
|
QByteArray function = line.mid(pos1, pos2 - pos1);
|
|
|
|
|
if (function != lastFunction) {
|
|
|
|
|
dlines.appendComment(QString());
|
|
|
|
|
dlines.appendComment(_("Function: ") + _(function));
|
|
|
|
|
lastFunction = function;
|
|
|
|
|
}
|
|
|
|
|
line.replace(pos1, pos2 - pos1, "");
|
|
|
|
|
}
|
2011-02-17 12:50:13 +01:00
|
|
|
if (pos3 - pos2 == 1)
|
|
|
|
|
line.insert(pos2 + 1, "000");
|
|
|
|
|
if (pos3 - pos2 == 2)
|
|
|
|
|
line.insert(pos2 + 1, "00");
|
|
|
|
|
if (pos3 - pos2 == 3)
|
|
|
|
|
line.insert(pos2 + 1, "0");
|
2011-02-10 13:43:02 +01:00
|
|
|
dlines.appendLine(DisassemblerLine(_(line)));
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
dlines.appendComment(someSpace + _(line));
|
|
|
|
|
}
|
|
|
|
|
return dlines;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DisassemblerLines GdbEngine::parseDisassembler(const GdbMi &data)
|
|
|
|
|
{
|
|
|
|
|
// Apple's gdb produces MI output even for CLI commands.
|
|
|
|
|
// FIXME: Check whether wrapping this into -interpreter-exec console
|
|
|
|
|
// (i.e. usgind the 'ConsoleCommand' GdbCommandFlag makes a
|
|
|
|
|
// difference.
|
|
|
|
|
GdbMi lines = data.findChild("asm_insns");
|
|
|
|
|
if (lines.isValid())
|
|
|
|
|
return parseMiDisassembler(lines);
|
|
|
|
|
GdbMi output = data.findChild("consolestreamoutput");
|
|
|
|
|
return parseCliDisassembler(output);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::handleDisassemblerCheck(const GdbResponse &response)
|
|
|
|
|
{
|
|
|
|
|
m_disassembleUsesComma = response.resultClass != GdbResultDone;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::handleFetchDisassemblerByCliPointMixed(const GdbResponse &response)
|
2009-08-14 13:04:05 +02:00
|
|
|
{
|
2009-09-24 11:16:00 +02:00
|
|
|
DisassemblerAgentCookie ac = response.cookie.value<DisassemblerAgentCookie>();
|
2009-08-18 08:34:48 +02:00
|
|
|
QTC_ASSERT(ac.agent, return);
|
2009-08-14 13:04:05 +02:00
|
|
|
|
2009-09-24 11:16:00 +02:00
|
|
|
if (response.resultClass == GdbResultDone) {
|
2011-02-10 13:43:02 +01:00
|
|
|
DisassemblerLines dlines = parseDisassembler(response.data);
|
|
|
|
|
if (dlines.coversAddress(ac.agent->address())) {
|
|
|
|
|
ac.agent->setContents(dlines);
|
|
|
|
|
return;
|
2010-02-01 13:59:30 +01:00
|
|
|
}
|
2009-08-14 13:04:05 +02:00
|
|
|
}
|
2011-02-10 13:43:02 +01:00
|
|
|
fetchDisassemblerByCliPointPlain(ac);
|
2009-08-14 13:04:05 +02:00
|
|
|
}
|
|
|
|
|
|
2011-02-10 13:43:02 +01:00
|
|
|
void GdbEngine::handleFetchDisassemblerByCliPointPlain(const GdbResponse &response)
|
2009-08-14 13:04:05 +02:00
|
|
|
{
|
2009-09-24 11:16:00 +02:00
|
|
|
DisassemblerAgentCookie ac = response.cookie.value<DisassemblerAgentCookie>();
|
2009-08-18 08:34:48 +02:00
|
|
|
QTC_ASSERT(ac.agent, return);
|
2009-08-14 13:04:05 +02:00
|
|
|
|
2009-09-24 11:16:00 +02:00
|
|
|
if (response.resultClass == GdbResultDone) {
|
2011-02-10 13:43:02 +01:00
|
|
|
DisassemblerLines dlines = parseDisassembler(response.data);
|
|
|
|
|
if (dlines.coversAddress(ac.agent->address())) {
|
|
|
|
|
ac.agent->setContents(dlines);
|
|
|
|
|
return;
|
2009-10-02 11:45:19 +02:00
|
|
|
}
|
2009-08-14 13:04:05 +02:00
|
|
|
}
|
2011-02-10 13:43:02 +01:00
|
|
|
if (ac.agent->isMixed())
|
|
|
|
|
fetchDisassemblerByCliRangeMixed(ac);
|
|
|
|
|
else
|
|
|
|
|
fetchDisassemblerByCliRangePlain(ac);
|
2009-08-14 13:04:05 +02:00
|
|
|
}
|
|
|
|
|
|
2011-02-10 13:43:02 +01:00
|
|
|
void GdbEngine::handleFetchDisassemblerByCliRangeMixed(const GdbResponse &response)
|
2009-08-14 13:04:05 +02:00
|
|
|
{
|
2009-09-24 11:16:00 +02:00
|
|
|
DisassemblerAgentCookie ac = response.cookie.value<DisassemblerAgentCookie>();
|
2009-08-18 11:34:26 +02:00
|
|
|
QTC_ASSERT(ac.agent, return);
|
2009-08-14 13:04:05 +02:00
|
|
|
|
2009-09-24 11:16:00 +02:00
|
|
|
if (response.resultClass == GdbResultDone) {
|
2011-02-10 13:43:02 +01:00
|
|
|
DisassemblerLines dlines = parseDisassembler(response.data);
|
|
|
|
|
if (dlines.coversAddress(ac.agent->address())) {
|
|
|
|
|
ac.agent->setContents(dlines);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-02-09 15:02:55 +01:00
|
|
|
}
|
2011-02-10 13:43:02 +01:00
|
|
|
fetchDisassemblerByCliRangePlain(ac);
|
2010-02-09 15:02:55 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-10 13:43:02 +01:00
|
|
|
void GdbEngine::handleFetchDisassemblerByCliRangePlain(const GdbResponse &response)
|
2010-02-09 15:02:55 +01:00
|
|
|
{
|
|
|
|
|
DisassemblerAgentCookie ac = response.cookie.value<DisassemblerAgentCookie>();
|
|
|
|
|
QTC_ASSERT(ac.agent, return);
|
|
|
|
|
|
|
|
|
|
if (response.resultClass == GdbResultDone) {
|
2011-02-10 13:43:02 +01:00
|
|
|
DisassemblerLines dlines = parseDisassembler(response.data);
|
|
|
|
|
if (dlines.size()) {
|
|
|
|
|
ac.agent->setContents(dlines);
|
|
|
|
|
return;
|
2010-02-09 15:02:55 +01:00
|
|
|
}
|
2009-08-14 13:04:05 +02:00
|
|
|
}
|
2011-02-10 13:43:02 +01:00
|
|
|
|
|
|
|
|
// Finally, give up.
|
|
|
|
|
//76^error,msg="No function contains program counter for selected..."
|
|
|
|
|
//76^error,msg="No function contains specified address."
|
|
|
|
|
//>568^error,msg="Line number 0 out of range;
|
|
|
|
|
QByteArray msg = response.data.findChild("msg").data();
|
|
|
|
|
showStatusMessage(tr("Disassembler failed: %1")
|
|
|
|
|
.arg(QString::fromLocal8Bit(msg)), 5000);
|
2009-08-14 13:04:05 +02:00
|
|
|
}
|
|
|
|
|
|
2011-02-25 13:43:06 +01:00
|
|
|
// Binary/configuration check logic.
|
|
|
|
|
|
|
|
|
|
static QString gdbBinary(const DebuggerStartParameters &sp)
|
|
|
|
|
{
|
|
|
|
|
// 1) Environment.
|
|
|
|
|
const QByteArray envBinary = qgetenv("QTC_DEBUGGER_PATH");
|
|
|
|
|
if (!envBinary.isEmpty())
|
|
|
|
|
return QString::fromLocal8Bit(envBinary);
|
|
|
|
|
// 2) Command explicitly specified.
|
|
|
|
|
if (!sp.debuggerCommand.isEmpty()) {
|
|
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
|
// Do not use a CDB binary if we got started for a project with MSVC runtime.
|
|
|
|
|
const bool abiMatch = sp.toolChainAbi.os() != ProjectExplorer::Abi::WindowsOS
|
|
|
|
|
|| sp.toolChainAbi.osFlavor() == ProjectExplorer::Abi::WindowsMSysFlavor;
|
|
|
|
|
#else
|
|
|
|
|
const bool abiMatch = true;
|
|
|
|
|
#endif
|
|
|
|
|
if (abiMatch)
|
|
|
|
|
return sp.debuggerCommand;
|
|
|
|
|
}
|
|
|
|
|
// 3) Find one from toolchains.
|
|
|
|
|
return debuggerCore()->debuggerForAbi(sp.toolChainAbi, GdbEngineType);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool checkGdbConfiguration(const DebuggerStartParameters &sp, ConfigurationCheck *check)
|
|
|
|
|
{
|
|
|
|
|
const QString binary = gdbBinary(sp);
|
|
|
|
|
if (gdbBinary(sp).isEmpty()) {
|
|
|
|
|
check->errorDetails.push_back(msgNoGdbBinaryForToolChain(sp.toolChainAbi));
|
|
|
|
|
check->settingsCategory = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
|
|
|
|
|
check->settingsPage = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
|
// See initialization below, we need an absolute path to be able to locate Python on Windows.
|
|
|
|
|
if (!QFileInfo(binary).isAbsolute()) {
|
|
|
|
|
check->errorDetails.push_back(GdbEngine::tr("The gdb location must be given as an "
|
|
|
|
|
"absolute path in the debugger settings (%1).").arg(binary));
|
|
|
|
|
check->settingsCategory = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
|
|
|
|
|
check->settingsPage = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2011-02-10 13:43:02 +01:00
|
|
|
|
2009-09-21 11:09:38 +02:00
|
|
|
//
|
|
|
|
|
// Starting up & shutting down
|
|
|
|
|
//
|
|
|
|
|
|
2011-02-25 13:43:06 +01:00
|
|
|
bool GdbEngine::startGdb(const QStringList &args, const QString &settingsIdHint)
|
2009-09-21 11:09:38 +02:00
|
|
|
{
|
2010-05-05 12:49:08 +02:00
|
|
|
gdbProc()->disconnect(); // From any previous runs
|
2009-10-20 11:02:16 +02:00
|
|
|
|
2011-02-03 16:49:28 +01:00
|
|
|
const DebuggerStartParameters &sp = startParameters();
|
2011-02-25 13:43:06 +01:00
|
|
|
m_gdb = gdbBinary(sp);
|
2010-04-08 16:55:25 +02:00
|
|
|
if (m_gdb.isEmpty()) {
|
2010-06-16 11:08:54 +02:00
|
|
|
handleAdapterStartFailed(
|
2011-02-24 16:50:15 +01:00
|
|
|
msgNoGdbBinaryForToolChain(sp.toolChainAbi),
|
2011-02-22 13:23:16 +01:00
|
|
|
_(Constants::DEBUGGER_COMMON_SETTINGS_ID));
|
2010-04-08 16:55:25 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2009-10-20 11:02:16 +02:00
|
|
|
QStringList gdbArgs;
|
|
|
|
|
gdbArgs << _("-i");
|
|
|
|
|
gdbArgs << _("mi");
|
2010-12-21 11:52:21 +01:00
|
|
|
if (!debuggerCore()->boolSetting(LoadGdbInit))
|
|
|
|
|
gdbArgs << _("-n");
|
2009-10-20 11:02:16 +02:00
|
|
|
gdbArgs += args;
|
2010-12-02 17:43:14 +01:00
|
|
|
|
2010-02-03 16:50:28 +01:00
|
|
|
#ifdef Q_OS_WIN
|
2010-03-10 15:51:31 +01:00
|
|
|
// Set python path. By convention, python is located below gdb executable.
|
2010-04-01 11:18:02 +02:00
|
|
|
// Extend the environment set on the process in startAdapter().
|
2010-04-08 16:55:25 +02:00
|
|
|
const QFileInfo fi(m_gdb);
|
2011-02-25 13:43:06 +01:00
|
|
|
QTC_ASSERT(fi.isAbsolute(), return false; )
|
2010-12-02 17:43:14 +01:00
|
|
|
|
2011-01-24 13:37:07 +01:00
|
|
|
const QString winPythonVersion = _(winPythonVersionC);
|
2010-12-02 17:43:14 +01:00
|
|
|
const QDir dir = fi.absoluteDir();
|
|
|
|
|
|
|
|
|
|
QProcessEnvironment environment = gdbProc()->processEnvironment();
|
2011-01-24 13:37:07 +01:00
|
|
|
const QString pythonPathVariable = _("PYTHONPATH");
|
2010-12-16 12:10:56 +01:00
|
|
|
QString pythonPath;
|
|
|
|
|
|
2011-01-12 15:31:40 +01:00
|
|
|
const QString environmentPythonPath = environment.value(pythonPathVariable);
|
2010-12-16 12:10:56 +01:00
|
|
|
if (dir.exists(winPythonVersion)) {
|
|
|
|
|
pythonPath = QDir::toNativeSeparators(dir.absoluteFilePath(winPythonVersion));
|
2011-01-24 13:37:07 +01:00
|
|
|
} else if (dir.exists(_("lib"))) {
|
|
|
|
|
// Needed for our gdb 7.2 packages
|
|
|
|
|
pythonPath = QDir::toNativeSeparators(dir.absoluteFilePath(_("lib")));
|
2010-12-02 17:43:14 +01:00
|
|
|
} else {
|
2011-01-12 15:31:40 +01:00
|
|
|
pythonPath = environmentPythonPath;
|
|
|
|
|
}
|
|
|
|
|
if (pythonPath.isEmpty()) {
|
|
|
|
|
const QString nativeGdb = QDir::toNativeSeparators(m_gdb);
|
|
|
|
|
showMessage(_("GDB %1 CANNOT FIND THE PYTHON INSTALLATION.").arg(nativeGdb));
|
|
|
|
|
showStatusMessage(_("%1 cannot find python").arg(nativeGdb));
|
|
|
|
|
const QString msg = tr("The gdb installed at %1 cannot "
|
2011-01-24 13:37:07 +01:00
|
|
|
"find a valid python installation in its %2 subdirectory.\n"
|
2011-02-14 16:36:05 +01:00
|
|
|
"You may set the environment variable PYTHONPATH to point to your installation.")
|
2011-01-12 15:31:40 +01:00
|
|
|
.arg(nativeGdb).arg(winPythonVersion);
|
|
|
|
|
handleAdapterStartFailed(msg, settingsIdHint);
|
|
|
|
|
return false;
|
2010-03-22 14:06:19 +01:00
|
|
|
}
|
2010-12-16 12:10:56 +01:00
|
|
|
showMessage(_("Python path: %1").arg(pythonPath), LogMisc);
|
2011-01-12 15:31:40 +01:00
|
|
|
// Apply to process
|
|
|
|
|
if (pythonPath != environmentPythonPath) {
|
|
|
|
|
environment.insert(pythonPathVariable, pythonPath);
|
|
|
|
|
gdbProc()->setProcessEnvironment(environment);
|
|
|
|
|
}
|
2010-02-03 16:50:28 +01:00
|
|
|
#endif
|
2009-10-20 11:02:16 +02:00
|
|
|
|
2010-05-05 12:49:08 +02:00
|
|
|
connect(gdbProc(), SIGNAL(error(QProcess::ProcessError)),
|
2009-10-20 11:02:16 +02:00
|
|
|
SLOT(handleGdbError(QProcess::ProcessError)));
|
2010-05-05 12:49:08 +02:00
|
|
|
connect(gdbProc(), SIGNAL(finished(int, QProcess::ExitStatus)),
|
2009-10-20 11:02:16 +02:00
|
|
|
SLOT(handleGdbFinished(int, QProcess::ExitStatus)));
|
2010-05-05 12:49:08 +02:00
|
|
|
connect(gdbProc(), SIGNAL(readyReadStandardOutput()),
|
2009-10-20 11:02:16 +02:00
|
|
|
SLOT(readGdbStandardOutput()));
|
2010-05-05 12:49:08 +02:00
|
|
|
connect(gdbProc(), SIGNAL(readyReadStandardError()),
|
2009-10-20 11:02:16 +02:00
|
|
|
SLOT(readGdbStandardError()));
|
|
|
|
|
|
2010-12-21 11:52:21 +01:00
|
|
|
showMessage(_("STARTING ") + m_gdb + _(" ") + gdbArgs.join(_(" ")));
|
2010-05-05 12:49:08 +02:00
|
|
|
gdbProc()->start(m_gdb, gdbArgs);
|
2010-03-10 15:51:31 +01:00
|
|
|
|
2010-05-05 12:49:08 +02:00
|
|
|
if (!gdbProc()->waitForStarted()) {
|
2010-09-06 14:26:19 +02:00
|
|
|
const QString msg = errorMessage(QProcess::FailedToStart);
|
2010-03-10 15:51:31 +01:00
|
|
|
handleAdapterStartFailed(msg, settingsIdHint);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("GDB STARTED, INITIALIZING IT"));
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("show version", CB(handleShowVersion));
|
2009-11-25 15:43:39 +01:00
|
|
|
|
2010-01-05 16:51:55 +01:00
|
|
|
//postCommand("-enable-timings");
|
2010-12-21 11:54:15 +01:00
|
|
|
//postCommand("set print static-members off"); // Seemingly doesn't work.
|
2010-01-05 16:51:55 +01:00
|
|
|
//postCommand("set debug infrun 1");
|
|
|
|
|
//postCommand("define hook-stop\n-thread-list-ids\n-stack-list-frames\nend");
|
|
|
|
|
//postCommand("define hook-stop\nprint 4\nend");
|
|
|
|
|
//postCommand("define hookpost-stop\nprint 5\nend");
|
|
|
|
|
//postCommand("define hook-call\nprint 6\nend");
|
|
|
|
|
//postCommand("define hookpost-call\nprint 7\nend");
|
|
|
|
|
//postCommand("set print object on"); // works with CLI, but not MI
|
|
|
|
|
//postCommand("set step-mode on"); // we can't work with that yes
|
|
|
|
|
//postCommand("set exec-done-display on");
|
|
|
|
|
//postCommand("set print pretty on");
|
|
|
|
|
//postCommand("set confirm off");
|
|
|
|
|
//postCommand("set pagination off");
|
2009-10-05 12:06:29 +02:00
|
|
|
|
|
|
|
|
// The following does not work with 6.3.50-20050815 (Apple version gdb-1344)
|
|
|
|
|
// (Mac OS 10.6), but does so for gdb-966 (10.5):
|
2010-01-05 16:51:55 +01:00
|
|
|
//postCommand("set print inferior-events 1");
|
2009-10-05 12:06:29 +02:00
|
|
|
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("set breakpoint pending on");
|
|
|
|
|
postCommand("set print elements 10000");
|
2010-01-12 13:49:56 +01:00
|
|
|
// Produces a few messages during symtab loading
|
|
|
|
|
//postCommand("set verbose on");
|
2009-09-21 11:09:38 +02:00
|
|
|
|
2010-01-05 16:51:55 +01:00
|
|
|
//postCommand("set substitute-path /var/tmp/qt-x11-src-4.5.0 "
|
|
|
|
|
// "/home/sandbox/qtsdk-2009.01/qt");
|
2009-09-21 11:09:38 +02:00
|
|
|
|
|
|
|
|
// one of the following is needed to prevent crashes in gdb on code like:
|
|
|
|
|
// template <class T> T foo() { return T(0); }
|
|
|
|
|
// int main() { return foo<int>(); }
|
|
|
|
|
// (gdb) call 'int foo<int>'()
|
|
|
|
|
// /build/buildd/gdb-6.8/gdb/valops.c:2069: internal-error:
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("set overload-resolution off");
|
2009-09-21 11:09:38 +02:00
|
|
|
//postCommand(_("set demangle-style none"));
|
|
|
|
|
// From the docs:
|
|
|
|
|
// Stop means reenter debugger if this signal happens (implies print).
|
|
|
|
|
// Print means print a message if this signal happens.
|
|
|
|
|
// Pass means let program see this signal;
|
|
|
|
|
// otherwise program doesn't know.
|
|
|
|
|
// Pass and Stop may be combined.
|
2010-01-11 10:22:55 +01:00
|
|
|
// We need "print" as otherwise we will get no feedback whatsoever
|
|
|
|
|
// when Custom DebuggingHelper crash (which happen regularly when accessing
|
|
|
|
|
// uninitialized variables).
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("handle SIGSEGV nopass stop print");
|
2009-09-21 11:09:38 +02:00
|
|
|
|
|
|
|
|
// This is useful to kill the inferior whenever gdb dies.
|
|
|
|
|
//postCommand(_("handle SIGTERM pass nostop print"));
|
|
|
|
|
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("set unwindonsignal on");
|
2011-01-25 08:18:45 +01:00
|
|
|
postCommand("pwd");
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("set width 0");
|
|
|
|
|
postCommand("set height 0");
|
2011-01-25 08:18:45 +01:00
|
|
|
postCommand("set auto-solib-add on");
|
2009-09-21 11:09:38 +02:00
|
|
|
|
2011-02-03 16:49:28 +01:00
|
|
|
if (debuggerCore()->boolSetting(TargetAsync)) {
|
|
|
|
|
postCommand("set target-async on");
|
|
|
|
|
postCommand("set non-stop on");
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-10 13:43:02 +01:00
|
|
|
// Work around http://bugreports.qt.nokia.com/browse/QTCREATORBUG-2004
|
|
|
|
|
postCommand("maintenance set internal-warning quit no", ConsoleCommand);
|
|
|
|
|
postCommand("maintenance set internal-error quit no", ConsoleCommand);
|
2010-10-14 10:44:36 +02:00
|
|
|
|
2011-02-10 13:43:02 +01:00
|
|
|
postCommand("disassemble 0 0", ConsoleCommand, CB(handleDisassemblerCheck));
|
|
|
|
|
|
|
|
|
|
loadPythonDumpers();
|
2009-09-21 11:09:38 +02:00
|
|
|
|
2010-11-10 16:33:11 +01:00
|
|
|
QString scriptFileName = debuggerCore()->stringSetting(GdbScriptFile);
|
2009-09-21 11:09:38 +02:00
|
|
|
if (!scriptFileName.isEmpty()) {
|
|
|
|
|
if (QFileInfo(scriptFileName).isReadable()) {
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("source " + scriptFileName.toLocal8Bit());
|
2009-09-21 11:09:38 +02:00
|
|
|
} else {
|
2009-09-23 09:54:21 +02:00
|
|
|
showMessageBox(QMessageBox::Warning,
|
2009-09-21 11:09:38 +02:00
|
|
|
tr("Cannot find debugger initialization script"),
|
|
|
|
|
tr("The debugger settings point to a script file at '%1' "
|
|
|
|
|
"which is not accessible. If a script file is not needed, "
|
|
|
|
|
"consider clearing that entry to avoid this warning. "
|
|
|
|
|
).arg(scriptFileName));
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-09-02 18:16:40 +02:00
|
|
|
|
2010-07-15 13:23:44 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::loadPythonDumpers()
|
|
|
|
|
{
|
|
|
|
|
const QByteArray dumperSourcePath =
|
|
|
|
|
Core::ICore::instance()->resourcePath().toLocal8Bit() + "/gdbmacros/";
|
2010-03-02 12:27:03 +01:00
|
|
|
|
2010-06-28 14:53:31 +02:00
|
|
|
postCommand("python execfile('" + dumperSourcePath + "dumper.py')",
|
|
|
|
|
ConsoleCommand|NonCriticalResponse);
|
|
|
|
|
postCommand("python execfile('" + dumperSourcePath + "gdbmacros.py')",
|
|
|
|
|
ConsoleCommand|NonCriticalResponse);
|
|
|
|
|
postCommand("bbsetup",
|
2010-07-15 11:37:06 +02:00
|
|
|
ConsoleCommand, CB(handleHasPython));
|
2009-10-20 11:02:16 +02:00
|
|
|
}
|
2009-10-13 09:53:15 +02:00
|
|
|
|
2009-10-20 11:02:16 +02:00
|
|
|
void GdbEngine::handleGdbError(QProcess::ProcessError error)
|
|
|
|
|
{
|
2010-09-06 14:26:19 +02:00
|
|
|
const QString msg = errorMessage(error);
|
|
|
|
|
showMessage(_("HANDLE GDB ERROR: ") + msg);
|
2010-10-30 21:54:23 +02:00
|
|
|
// Show a message box for asynchronously reported issues.
|
2009-10-20 11:02:16 +02:00
|
|
|
switch (error) {
|
2010-09-06 14:26:19 +02:00
|
|
|
case QProcess::FailedToStart:
|
|
|
|
|
// This should be handled by the code trying to start the process.
|
|
|
|
|
break;
|
2009-10-20 11:02:16 +02:00
|
|
|
case QProcess::Crashed:
|
2010-09-06 14:26:19 +02:00
|
|
|
// This will get a processExited() as well.
|
|
|
|
|
break;
|
2009-10-20 11:02:16 +02:00
|
|
|
case QProcess::ReadError:
|
|
|
|
|
case QProcess::WriteError:
|
|
|
|
|
case QProcess::Timedout:
|
|
|
|
|
default:
|
2010-07-09 17:07:59 +02:00
|
|
|
//gdbProc()->kill();
|
2010-10-27 15:39:25 +02:00
|
|
|
//notifyEngineIll();
|
2010-09-06 14:26:19 +02:00
|
|
|
showMessageBox(QMessageBox::Critical, tr("Gdb I/O Error"), msg);
|
2009-10-20 11:02:16 +02:00
|
|
|
break;
|
|
|
|
|
}
|
2009-09-21 11:09:38 +02:00
|
|
|
}
|
|
|
|
|
|
2009-10-20 11:02:16 +02:00
|
|
|
void GdbEngine::handleGdbFinished(int code, QProcess::ExitStatus type)
|
2009-09-21 11:09:38 +02:00
|
|
|
{
|
2010-11-16 12:42:57 +01:00
|
|
|
if (m_commandTimer.isActive())
|
|
|
|
|
m_commandTimer.stop();
|
2010-11-16 12:38:50 +01:00
|
|
|
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("GDB PROCESS FINISHED, status %1, code %2").arg(type).arg(code));
|
2010-11-18 17:32:41 +01:00
|
|
|
|
2010-11-16 12:38:50 +01:00
|
|
|
switch (state()) {
|
|
|
|
|
case EngineShutdownRequested:
|
2010-07-09 17:07:59 +02:00
|
|
|
notifyEngineShutdownOk();
|
2010-11-16 12:38:50 +01:00
|
|
|
break;
|
|
|
|
|
case InferiorRunOk:
|
2010-07-09 17:07:59 +02:00
|
|
|
// This could either be a real gdb crash or a quickly exited inferior
|
|
|
|
|
// in the terminal adapter. In this case the stub proc will die soon,
|
|
|
|
|
// too, so there's no need to act here.
|
|
|
|
|
showMessage(_("The gdb process exited somewhat unexpectedly."));
|
|
|
|
|
notifyEngineSpontaneousShutdown();
|
2010-11-16 12:38:50 +01:00
|
|
|
break;
|
|
|
|
|
default: {
|
|
|
|
|
notifyEngineIll(); // Initiate shutdown sequence
|
|
|
|
|
const QString msg = type == QProcess::CrashExit ?
|
|
|
|
|
tr("The gdb process crashed.") :
|
|
|
|
|
tr("The gdb process exited unexpectedly (code %1)").arg(code);
|
2010-03-10 10:32:58 +01:00
|
|
|
showMessageBox(QMessageBox::Critical, tr("Unexpected Gdb Exit"), msg);
|
2010-11-16 12:38:50 +01:00
|
|
|
break;
|
2009-10-20 11:02:16 +02:00
|
|
|
}
|
2010-11-16 12:52:02 +01:00
|
|
|
}
|
2009-09-21 11:09:38 +02:00
|
|
|
}
|
|
|
|
|
|
2011-01-24 13:37:07 +01:00
|
|
|
void GdbEngine::handleAdapterStartFailed(const QString &msg,
|
|
|
|
|
const QString &settingsIdHint)
|
2009-09-21 11:09:38 +02:00
|
|
|
{
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state());
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("ADAPTER START FAILED"));
|
2010-02-25 16:05:45 +01:00
|
|
|
if (!msg.isEmpty()) {
|
|
|
|
|
const QString title = tr("Adapter start failed");
|
|
|
|
|
if (settingsIdHint.isEmpty()) {
|
|
|
|
|
Core::ICore::instance()->showWarningWithOptions(title, msg);
|
|
|
|
|
} else {
|
|
|
|
|
Core::ICore::instance()->showWarningWithOptions(title, msg, QString(),
|
2010-03-10 10:32:58 +01:00
|
|
|
_(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY), settingsIdHint);
|
2010-02-25 16:05:45 +01:00
|
|
|
}
|
2009-10-26 11:55:02 +01:00
|
|
|
}
|
2010-07-09 17:07:59 +02:00
|
|
|
notifyEngineSetupFailed();
|
2009-09-21 11:09:38 +02:00
|
|
|
}
|
|
|
|
|
|
2009-10-20 11:02:16 +02:00
|
|
|
void GdbEngine::handleAdapterStarted()
|
2009-09-21 11:09:38 +02:00
|
|
|
{
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state());
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("ADAPTER SUCCESSFULLY STARTED"));
|
2010-07-09 08:48:33 +02:00
|
|
|
notifyEngineSetupOk();
|
2010-07-08 18:10:50 +02:00
|
|
|
}
|
2009-10-20 11:02:16 +02:00
|
|
|
|
2010-07-08 18:10:50 +02:00
|
|
|
void GdbEngine::setupInferior()
|
|
|
|
|
{
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
|
|
|
|
|
showStatusMessage(tr("Setting up inferior..."));
|
2010-07-08 18:10:50 +02:00
|
|
|
m_gdbAdapter->setupInferior();
|
2009-09-21 11:09:38 +02:00
|
|
|
}
|
|
|
|
|
|
2010-08-11 09:46:39 +02:00
|
|
|
void GdbEngine::notifyInferiorSetupFailed()
|
|
|
|
|
{
|
|
|
|
|
// FIXME: that's not enough to stop gdb from getting confused
|
|
|
|
|
// by a timeout of the adapter.
|
|
|
|
|
//resetCommandQueue();
|
|
|
|
|
DebuggerEngine::notifyInferiorSetupFailed();
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-19 19:04:01 +02:00
|
|
|
void GdbEngine::handleInferiorPrepared()
|
2009-09-21 11:09:38 +02:00
|
|
|
{
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
|
2011-02-23 17:38:09 +01:00
|
|
|
const QByteArray qtInstallPath =
|
|
|
|
|
debuggerCore()->action(QtSourcesLocation)->value().toString().toLocal8Bit();
|
2009-11-09 16:25:24 +01:00
|
|
|
if (!qtInstallPath.isEmpty()) {
|
2010-01-05 16:51:55 +01:00
|
|
|
QByteArray qtBuildPath;
|
2009-11-19 19:43:18 +01:00
|
|
|
#if defined(Q_OS_WIN)
|
2011-02-24 14:30:51 +01:00
|
|
|
qtBuildPath = "C:/qt-greenhouse/Trolltech/Code_less_create_more/"
|
|
|
|
|
"Trolltech/Code_less_create_more/Troll/4.6/qt";
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("set substitute-path " + qtBuildPath + ' ' + qtInstallPath);
|
|
|
|
|
qtBuildPath = "C:/iwmake/build_mingw_opensource";
|
|
|
|
|
postCommand("set substitute-path " + qtBuildPath + ' ' + qtInstallPath);
|
2011-02-24 14:30:51 +01:00
|
|
|
qtBuildPath = "C:/ndk_buildrepos/qt-desktop/src";
|
|
|
|
|
postCommand("set substitute-path " + qtBuildPath + ' ' + qtInstallPath);
|
2009-11-19 19:43:18 +01:00
|
|
|
#elif defined(Q_OS_UNIX) && !defined (Q_OS_MAC)
|
2010-02-04 15:09:05 +01:00
|
|
|
qtBuildPath = "/var/tmp/qt-src";
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("set substitute-path " + qtBuildPath + ' ' + qtInstallPath);
|
2009-11-19 19:43:18 +01:00
|
|
|
#endif
|
2009-11-09 16:25:24 +01:00
|
|
|
}
|
|
|
|
|
|
2010-12-21 14:41:17 +01:00
|
|
|
// Initial attempt to set breakpoints.
|
|
|
|
|
if (startParameters().startMode != AttachCore) {
|
|
|
|
|
showStatusMessage(tr("Setting breakpoints..."));
|
|
|
|
|
showMessage(tr("Setting breakpoints..."));
|
|
|
|
|
attemptBreakpointSynchronization();
|
|
|
|
|
}
|
2009-10-13 09:53:15 +02:00
|
|
|
|
2009-10-20 11:02:16 +02:00
|
|
|
if (m_cookieForToken.isEmpty()) {
|
2010-07-08 18:10:50 +02:00
|
|
|
finishInferiorSetup();
|
2009-10-20 11:02:16 +02:00
|
|
|
} else {
|
|
|
|
|
QTC_ASSERT(m_commandsDoneCallback == 0, /**/);
|
2010-07-08 18:10:50 +02:00
|
|
|
m_commandsDoneCallback = &GdbEngine::finishInferiorSetup;
|
2009-10-20 11:02:16 +02:00
|
|
|
}
|
2009-09-24 10:08:17 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-08 18:10:50 +02:00
|
|
|
void GdbEngine::finishInferiorSetup()
|
2009-09-24 10:08:17 +02:00
|
|
|
{
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
|
2010-07-08 18:10:50 +02:00
|
|
|
notifyInferiorSetupOk();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::runEngine()
|
|
|
|
|
{
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
|
|
|
|
|
m_gdbAdapter->runEngine();
|
2009-09-21 11:09:38 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-09 17:07:59 +02:00
|
|
|
void GdbEngine::notifyInferiorSetupFailed(const QString &msg)
|
2009-09-21 17:35:19 +02:00
|
|
|
{
|
2010-03-23 16:56:42 +01:00
|
|
|
showStatusMessage(tr("Failed to start application: ") + msg);
|
2010-07-09 08:48:33 +02:00
|
|
|
if (state() == EngineSetupFailed) {
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("INFERIOR START FAILED, BUT ADAPTER DIED ALREADY"));
|
2009-10-29 11:20:21 +01:00
|
|
|
return; // Adapter crashed meanwhile, so this notification is meaningless.
|
2010-03-10 15:51:31 +01:00
|
|
|
}
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("INFERIOR START FAILED"));
|
2010-03-23 16:56:42 +01:00
|
|
|
showMessageBox(QMessageBox::Critical, tr("Failed to start application"), msg);
|
2010-07-09 17:07:59 +02:00
|
|
|
DebuggerEngine::notifyInferiorSetupFailed();
|
2009-09-21 17:35:19 +02:00
|
|
|
}
|
|
|
|
|
|
2009-09-25 11:06:51 +02:00
|
|
|
void GdbEngine::handleAdapterCrashed(const QString &msg)
|
2009-09-21 17:35:19 +02:00
|
|
|
{
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("ADAPTER CRASHED"));
|
2009-09-21 11:09:38 +02:00
|
|
|
|
2009-10-20 11:02:16 +02:00
|
|
|
// The adapter is expected to have cleaned up after itself when we get here,
|
|
|
|
|
// so the effect is about the same as AdapterStartFailed => use it.
|
|
|
|
|
// Don't bother with state transitions - this can happen in any state and
|
|
|
|
|
// the end result is always the same, so it makes little sense to find a
|
|
|
|
|
// "path" which does not assert.
|
2010-07-09 08:48:33 +02:00
|
|
|
notifyEngineSetupFailed();
|
2009-09-21 17:35:19 +02:00
|
|
|
|
2009-10-20 11:02:16 +02:00
|
|
|
// No point in being friendly here ...
|
2010-05-05 12:49:08 +02:00
|
|
|
gdbProc()->kill();
|
2009-10-20 11:02:16 +02:00
|
|
|
|
2009-10-07 10:25:26 +02:00
|
|
|
if (!msg.isEmpty())
|
|
|
|
|
showMessageBox(QMessageBox::Critical, tr("Adapter crashed"), msg);
|
2009-09-21 17:35:19 +02:00
|
|
|
}
|
|
|
|
|
|
2011-01-24 13:37:07 +01:00
|
|
|
void GdbEngine::setUseDebuggingHelpers(const QVariant &)
|
2010-01-20 15:38:01 +01:00
|
|
|
{
|
|
|
|
|
setTokenBarrier();
|
|
|
|
|
updateLocals();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GdbEngine::hasPython() const
|
2009-10-12 14:50:27 +02:00
|
|
|
{
|
2010-01-20 15:38:01 +01:00
|
|
|
return m_hasPython;
|
2009-10-12 14:50:27 +02:00
|
|
|
}
|
2009-09-24 10:08:17 +02:00
|
|
|
|
2010-03-29 18:44:02 +02:00
|
|
|
void GdbEngine::createFullBacktrace()
|
|
|
|
|
{
|
2010-06-28 14:53:31 +02:00
|
|
|
postCommand("thread apply all bt full",
|
|
|
|
|
NeedsStop|ConsoleCommand, CB(handleCreateFullBacktrace));
|
2010-03-29 18:44:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::handleCreateFullBacktrace(const GdbResponse &response)
|
|
|
|
|
{
|
|
|
|
|
if (response.resultClass == GdbResultDone) {
|
2010-11-10 11:39:01 +01:00
|
|
|
debuggerCore()->openTextEditor(_("Backtrace $"),
|
2010-03-29 18:44:02 +02:00
|
|
|
_(response.data.findChild("consolestreamoutput").data()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-11 09:46:39 +02:00
|
|
|
void GdbEngine::resetCommandQueue()
|
|
|
|
|
{
|
2010-11-16 12:42:57 +01:00
|
|
|
m_commandTimer.stop();
|
2010-08-11 09:46:39 +02:00
|
|
|
if (!m_cookieForToken.isEmpty()) {
|
|
|
|
|
QString msg;
|
|
|
|
|
QTextStream ts(&msg);
|
|
|
|
|
ts << "RESETING COMMAND QUEUE. LEFT OVER TOKENS: ";
|
|
|
|
|
foreach (const GdbCommand &cookie, m_cookieForToken)
|
|
|
|
|
ts << "CMD:" << cookie.command << cookie.callbackName;
|
|
|
|
|
m_cookieForToken.clear();
|
|
|
|
|
showMessage(msg);
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-03-29 18:44:02 +02:00
|
|
|
|
2010-12-10 12:41:44 +01:00
|
|
|
void GdbEngine::handleRemoteSetupDone(int gdbServerPort, int qmlPort)
|
|
|
|
|
{
|
|
|
|
|
m_gdbAdapter->handleRemoteSetupDone(gdbServerPort, qmlPort);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::handleRemoteSetupFailed(const QString &message)
|
|
|
|
|
{
|
|
|
|
|
m_gdbAdapter->handleRemoteSetupFailed(message);
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-19 14:34:25 +01:00
|
|
|
bool GdbEngine::setupQmlStep(bool on)
|
2011-01-14 17:08:59 +01:00
|
|
|
{
|
|
|
|
|
QTC_ASSERT(isSlaveEngine(), return false);
|
2011-01-24 13:37:07 +01:00
|
|
|
m_qmlBreakpointNumbers.clear();
|
|
|
|
|
//qDebug() << "CLEAR: " << m_qmlBreakpointNumbers;
|
2011-01-19 14:34:25 +01:00
|
|
|
postCommand("tbreak '" + qtNamespace() + "QScript::FunctionWrapper::proxyCall'\n"
|
|
|
|
|
"commands\n"
|
|
|
|
|
"set $d=(void*)((FunctionWrapper*)callee)->data->function\n"
|
2011-01-24 13:37:07 +01:00
|
|
|
"tbreak *$d\nprintf \"QMLBP:%d \\n\",$bpnum\ncontinue\nend",
|
2011-01-19 14:34:25 +01:00
|
|
|
NeedsStop, CB(handleSetQmlStepBreakpoint));
|
2011-01-19 10:48:39 +01:00
|
|
|
m_preparedForQmlBreak = on;
|
2011-01-14 17:08:59 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-19 14:34:25 +01:00
|
|
|
void GdbEngine::handleSetQmlStepBreakpoint(const GdbResponse &response)
|
2011-01-14 17:08:59 +01:00
|
|
|
{
|
2011-01-19 14:34:25 +01:00
|
|
|
//QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
|
2011-01-14 17:08:59 +01:00
|
|
|
if (response.resultClass == GdbResultDone) {
|
2011-01-24 13:37:07 +01:00
|
|
|
// "{logstreamoutput="tbreak 'myns::QScript::FunctionWrapper::proxyCall'\n"
|
|
|
|
|
//,consolestreamoutput="Temporary breakpoint 1 at 0xf166e7:
|
|
|
|
|
// file bridge/qscriptfunction.cpp, line 75.\n"}
|
|
|
|
|
QByteArray ba = parsePlainConsoleStream(response);
|
|
|
|
|
const int pos2 = ba.indexOf(" at 0x");
|
|
|
|
|
const int pos1 = ba.lastIndexOf(" ", pos2 - 1) + 1;
|
|
|
|
|
QByteArray mid = ba.mid(pos1, pos2 - pos1);
|
|
|
|
|
const int bpnr = mid.toInt();
|
2011-01-19 14:34:25 +01:00
|
|
|
m_qmlBreakpointNumbers[1] = bpnr;
|
2011-01-24 13:37:07 +01:00
|
|
|
//qDebug() << "SET: " << m_qmlBreakpointNumbers;
|
2011-01-14 17:08:59 +01:00
|
|
|
}
|
|
|
|
|
QTC_ASSERT(masterEngine(), return);
|
2011-01-19 14:34:25 +01:00
|
|
|
masterEngine()->readyToExecuteQmlStep();
|
2011-01-14 17:08:59 +01:00
|
|
|
}
|
|
|
|
|
|
2011-01-24 13:37:07 +01:00
|
|
|
bool GdbEngine::isQmlStepBreakpoint1(int bpnr) const
|
|
|
|
|
{
|
|
|
|
|
//qDebug() << "CHECK 1: " << m_qmlBreakpointNumbers[1] << bpnr;
|
|
|
|
|
return bpnr && m_qmlBreakpointNumbers[1] == bpnr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GdbEngine::isQmlStepBreakpoint2(int bpnr) const
|
|
|
|
|
{
|
|
|
|
|
//qDebug() << "CHECK 2: " << m_qmlBreakpointNumbers[2] << bpnr;
|
|
|
|
|
return bpnr && m_qmlBreakpointNumbers[2] == bpnr;
|
|
|
|
|
}
|
2011-01-14 17:08:59 +01:00
|
|
|
|
2009-09-23 09:54:21 +02:00
|
|
|
//
|
|
|
|
|
// Factory
|
|
|
|
|
//
|
|
|
|
|
|
2011-01-12 12:10:12 +01:00
|
|
|
DebuggerEngine *createGdbEngine(const DebuggerStartParameters &startParameters,
|
|
|
|
|
DebuggerEngine *masterEngine)
|
2010-06-16 11:08:54 +02:00
|
|
|
{
|
2011-01-12 12:10:12 +01:00
|
|
|
return new GdbEngine(startParameters, masterEngine);
|
2010-06-16 11:08:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void addGdbOptionPages(QList<Core::IOptionsPage *> *opts)
|
2009-09-23 09:54:21 +02:00
|
|
|
{
|
2010-06-16 11:08:54 +02:00
|
|
|
opts->push_back(new GdbOptionsPage());
|
2009-09-11 15:37:32 +02:00
|
|
|
}
|
|
|
|
|
|
2009-08-18 08:34:48 +02:00
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace Debugger
|
|
|
|
|
|
2010-12-16 11:25:28 +01:00
|
|
|
Q_DECLARE_METATYPE(Debugger::Internal::MemoryAgentCookie)
|
|
|
|
|
Q_DECLARE_METATYPE(Debugger::Internal::DisassemblerAgentCookie)
|
|
|
|
|
Q_DECLARE_METATYPE(Debugger::Internal::GdbMi)
|