2012-10-02 09:12:39 +02:00
|
|
|
/****************************************************************************
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2015-01-14 18:07:15 +01:00
|
|
|
** Copyright (C) 2015 The Qt Company Ltd.
|
|
|
|
|
** Contact: http://www.qt.io/licensing
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** This file is part of Qt Creator.
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** Commercial License Usage
|
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
2015-01-14 18:07:15 +01:00
|
|
|
** a written agreement between you and The Qt Company. For licensing terms and
|
|
|
|
|
** conditions see http://www.qt.io/terms-conditions. For further information
|
2014-10-01 13:21:18 +02:00
|
|
|
** use the contact form at http://www.qt.io/contact-us.
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
** GNU Lesser General Public License Usage
|
2012-10-02 09:12:39 +02:00
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
2014-10-01 13:21:18 +02:00
|
|
|
** General Public License version 2.1 or version 3 as published by the Free
|
|
|
|
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
|
|
|
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
|
|
|
|
** following information to ensure the GNU Lesser General Public License
|
|
|
|
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
|
|
|
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
2012-10-02 09:12:39 +02:00
|
|
|
**
|
2015-01-14 18:07:15 +01:00
|
|
|
** In addition, as a special exception, The Qt Company gives you certain additional
|
|
|
|
|
** rights. These rights are described in The Qt Company LGPL Exception
|
2010-12-17 16:01:08 +01:00
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
****************************************************************************/
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
#include "gdbengine.h"
|
2010-06-16 11:08:54 +02:00
|
|
|
|
2009-09-23 15:28:50 +02:00
|
|
|
#include "attachgdbadapter.h"
|
|
|
|
|
#include "coregdbadapter.h"
|
2013-09-09 16:23:57 +02:00
|
|
|
#include "gdbplainengine.h"
|
2009-10-07 10:25:26 +02:00
|
|
|
#include "termgdbadapter.h"
|
2010-05-05 12:49:08 +02:00
|
|
|
#include "remotegdbserveradapter.h"
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2013-08-29 16:36:42 +02:00
|
|
|
#include <debugger/debuggerstartparameters.h>
|
|
|
|
|
#include <debugger/debuggerinternalconstants.h>
|
2014-10-17 13:40:04 +02:00
|
|
|
#include <debugger/debuggerruncontrol.h>
|
2013-08-29 16:36:42 +02:00
|
|
|
#include <debugger/disassemblerlines.h>
|
|
|
|
|
|
|
|
|
|
#include <debugger/debuggeractions.h>
|
|
|
|
|
#include <debugger/debuggercore.h>
|
|
|
|
|
#include <debugger/debuggermainwindow.h>
|
|
|
|
|
#include <debugger/debuggerplugin.h>
|
|
|
|
|
#include <debugger/debuggerprotocol.h>
|
|
|
|
|
#include <debugger/debuggerstringutils.h>
|
|
|
|
|
#include <debugger/debuggertooltipmanager.h>
|
|
|
|
|
#include <debugger/disassembleragent.h>
|
|
|
|
|
#include <debugger/memoryagent.h>
|
|
|
|
|
#include <debugger/sourceutils.h>
|
2015-01-26 11:47:51 +01:00
|
|
|
#include <debugger/terminal.h>
|
2013-08-29 16:36:42 +02:00
|
|
|
|
|
|
|
|
#include <debugger/breakhandler.h>
|
|
|
|
|
#include <debugger/moduleshandler.h>
|
|
|
|
|
#include <debugger/registerhandler.h>
|
|
|
|
|
#include <debugger/sourcefileshandler.h>
|
|
|
|
|
#include <debugger/stackhandler.h>
|
|
|
|
|
#include <debugger/threadshandler.h>
|
|
|
|
|
#include <debugger/debuggersourcepathmappingwidget.h>
|
|
|
|
|
#include <debugger/logwindow.h>
|
|
|
|
|
#include <debugger/procinterrupt.h>
|
|
|
|
|
#include <debugger/shared/hostutils.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-04-08 17:25:28 +02:00
|
|
|
#include <coreplugin/icore.h>
|
2014-11-25 13:08:18 +01:00
|
|
|
#include <coreplugin/messagebox.h>
|
2015-03-06 13:36:42 +01:00
|
|
|
|
2013-11-04 09:47:34 +01:00
|
|
|
#include <projectexplorer/devicesupport/deviceprocess.h>
|
2012-03-30 17:36:44 +02:00
|
|
|
#include <projectexplorer/itaskhandler.h>
|
2013-11-04 09:47:34 +01:00
|
|
|
#include <projectexplorer/taskhub.h>
|
2014-10-13 18:49:44 +02:00
|
|
|
|
2014-06-16 18:25:52 +04:00
|
|
|
#include <utils/algorithm.h>
|
2012-08-23 15:53:58 +02:00
|
|
|
#include <utils/hostosinfo.h>
|
2014-10-13 18:49:44 +02:00
|
|
|
#include <utils/macroexpander.h>
|
2012-06-13 10:15:56 +02:00
|
|
|
#include <utils/qtcassert.h>
|
|
|
|
|
#include <utils/qtcprocess.h>
|
2012-08-13 17:39:29 +02:00
|
|
|
#include <utils/savedaction.h>
|
2008-12-09 16:18:28 +01:00
|
|
|
|
2014-12-17 13:14:29 +01:00
|
|
|
#include <QBuffer>
|
2012-03-23 17:53:30 +01:00
|
|
|
#include <QDirIterator>
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QMessageBox>
|
2015-06-08 12:10:11 +02:00
|
|
|
#include <QProcess>
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QPushButton>
|
2014-12-17 13:14:29 +01:00
|
|
|
#include <QTemporaryFile>
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2014-06-05 14:15:54 +02:00
|
|
|
using namespace Core;
|
2011-02-23 16:47:08 +01:00
|
|
|
using namespace ProjectExplorer;
|
2012-06-04 18:06:59 +02:00
|
|
|
using namespace Utils;
|
2011-02-23 16:47:08 +01:00
|
|
|
|
2009-08-18 08:34:48 +02:00
|
|
|
namespace Debugger {
|
|
|
|
|
namespace Internal {
|
2008-12-02 12:01:29 +01:00
|
|
|
|
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
|
|
|
|
2015-02-05 22:46:09 +01:00
|
|
|
#define CB(callback) [this](const DebuggerResponse &r) { callback(r); }
|
2013-11-27 16:10:14 +01:00
|
|
|
|
2015-09-11 16:51:11 +02:00
|
|
|
#define CHECK_STATE(s) do { checkState(s, __FILE__, __LINE__); } while (0)
|
2015-02-24 15:30:42 +01:00
|
|
|
|
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:
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
static QByteArray parsePlainConsoleStream(const DebuggerResponse &response)
|
2009-07-01 12:49:41 +02:00
|
|
|
{
|
2011-07-05 17:57:57 +02:00
|
|
|
QByteArray out = response.consoleStreamOutput;
|
2009-07-01 12:49:41 +02:00
|
|
|
// 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);
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-05 12:50:57 +01:00
|
|
|
static bool isMostlyHarmlessMessage(const QByteArray &msg)
|
|
|
|
|
{
|
|
|
|
|
return msg == "warning: GDB: Failed to set controlling terminal: "
|
|
|
|
|
"Inappropriate ioctl for device\\n"
|
|
|
|
|
|| msg == "warning: GDB: Failed to set controlling terminal: Invalid argument\\n";
|
|
|
|
|
}
|
|
|
|
|
|
2012-03-30 17:36:44 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// Debuginfo Taskhandler
|
|
|
|
|
//
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
class DebugInfoTask
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
QString command;
|
|
|
|
|
};
|
|
|
|
|
|
2014-06-05 14:15:54 +02:00
|
|
|
class DebugInfoTaskHandler : public ITaskHandler
|
2012-03-30 17:36:44 +02:00
|
|
|
{
|
|
|
|
|
public:
|
2012-05-04 20:10:01 +02:00
|
|
|
explicit DebugInfoTaskHandler(GdbEngine *engine)
|
|
|
|
|
: m_engine(engine)
|
2012-03-30 17:36:44 +02:00
|
|
|
{}
|
|
|
|
|
|
2015-09-10 11:01:09 +02:00
|
|
|
bool canHandle(const Task &task) const override
|
2012-03-30 17:36:44 +02:00
|
|
|
{
|
|
|
|
|
return m_debugInfoTasks.contains(task.taskId);
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-10 11:01:09 +02:00
|
|
|
void handle(const Task &task) override
|
2012-03-30 17:36:44 +02:00
|
|
|
{
|
|
|
|
|
m_engine->requestDebugInformation(m_debugInfoTasks.value(task.taskId));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void addTask(unsigned id, const DebugInfoTask &task)
|
|
|
|
|
{
|
|
|
|
|
m_debugInfoTasks[id] = task;
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-10 11:01:09 +02:00
|
|
|
QAction *createAction(QObject *parent) const override
|
2012-03-30 17:36:44 +02:00
|
|
|
{
|
2012-04-24 09:00:05 +02:00
|
|
|
QAction *action = new QAction(DebuggerPlugin::tr("Install &Debug Information"), parent);
|
2012-10-01 10:14:11 +02:00
|
|
|
action->setToolTip(DebuggerPlugin::tr("Tries to install missing debug information."));
|
2012-03-30 17:36:44 +02:00
|
|
|
return action;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
GdbEngine *m_engine;
|
|
|
|
|
QHash<unsigned, DebugInfoTask> m_debugInfoTasks;
|
|
|
|
|
};
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// GdbEngine
|
|
|
|
|
//
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2015-05-27 13:59:56 +02:00
|
|
|
GdbEngine::GdbEngine(const DebuggerRunParameters &startParameters)
|
2012-08-15 14:33:39 +02:00
|
|
|
: DebuggerEngine(startParameters)
|
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_gdbVersion = 100;
|
2012-02-10 07:42:44 +01:00
|
|
|
m_isQnxGdb = false;
|
2010-11-16 12:52:02 +01:00
|
|
|
m_registerNamesListed = false;
|
|
|
|
|
m_sourcesListUpdating = false;
|
|
|
|
|
m_oldestAcceptableToken = -1;
|
2011-12-13 15:40:31 +01:00
|
|
|
m_nonDiscardableCount = 0;
|
2010-11-16 12:52:02 +01:00
|
|
|
m_outputCodec = QTextCodec::codecForLocale();
|
|
|
|
|
m_pendingBreakpointRequests = 0;
|
|
|
|
|
m_commandsDoneCallback = 0;
|
2010-12-17 13:07:17 +01:00
|
|
|
m_stackNeeded = false;
|
2012-08-29 11:42:28 +02:00
|
|
|
m_terminalTrap = startParameters.useTerminal;
|
2015-08-14 17:15:32 +02:00
|
|
|
m_temporaryStopPending = false;
|
2012-04-17 10:00:40 +02:00
|
|
|
m_fullStartDone = false;
|
2013-03-21 17:32:34 +01:00
|
|
|
m_systemDumpersLoaded = false;
|
2015-07-07 16:37:20 +02:00
|
|
|
m_rerunPending = false;
|
2015-07-17 13:30:51 +02:00
|
|
|
m_inUpdateLocals = false;
|
2011-02-10 13:43:02 +01:00
|
|
|
|
2012-03-30 17:36:44 +02:00
|
|
|
m_debugInfoTaskHandler = new DebugInfoTaskHandler(this);
|
2013-02-27 17:07:03 +01:00
|
|
|
//ExtensionSystem::PluginManager::addObject(m_debugInfoTaskHandler);
|
2012-03-30 17:36:44 +02:00
|
|
|
|
2010-11-16 12:52:02 +01:00
|
|
|
m_commandTimer.setSingleShot(true);
|
2015-02-12 13:50:54 +01:00
|
|
|
connect(&m_commandTimer, &QTimer::timeout,
|
|
|
|
|
this, &GdbEngine::commandTimeout);
|
|
|
|
|
|
|
|
|
|
connect(action(AutoDerefPointers), &SavedAction::valueChanged,
|
|
|
|
|
this, &GdbEngine::reloadLocals);
|
|
|
|
|
connect(action(CreateFullBacktrace), &QAction::triggered,
|
|
|
|
|
this, &GdbEngine::createFullBacktrace);
|
|
|
|
|
connect(action(UseDebuggingHelpers), &SavedAction::valueChanged,
|
|
|
|
|
this, &GdbEngine::reloadLocals);
|
|
|
|
|
connect(action(UseDynamicType), &SavedAction::valueChanged,
|
|
|
|
|
this, &GdbEngine::reloadLocals);
|
2010-05-05 12:49:08 +02:00
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
GdbEngine::~GdbEngine()
|
|
|
|
|
{
|
2013-02-27 17:07:03 +01:00
|
|
|
//ExtensionSystem::PluginManager::removeObject(m_debugInfoTaskHandler);
|
2012-03-30 17:36:44 +02:00
|
|
|
delete m_debugInfoTaskHandler;
|
|
|
|
|
m_debugInfoTaskHandler = 0;
|
|
|
|
|
|
2010-03-11 11:07:16 +01:00
|
|
|
// Prevent sending error messages afterwards.
|
2012-06-13 10:15:56 +02:00
|
|
|
disconnect();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DebuggerStartMode GdbEngine::startMode() const
|
|
|
|
|
{
|
2015-05-27 13:59:56 +02:00
|
|
|
return runParameters().startMode;
|
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:
|
2011-03-30 15:58:58 +02:00
|
|
|
return tr("The gdb process failed to start. Either the "
|
2012-10-01 10:14:11 +02: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")
|
2015-06-08 12:10:11 +02:00
|
|
|
.arg(m_gdb, m_gdbProc.errorString());
|
2008-12-02 12:01:29 +01:00
|
|
|
case QProcess::Crashed:
|
2011-10-31 16:15:48 +01:00
|
|
|
if (targetState() == DebuggerFinished)
|
|
|
|
|
return tr("The gdb process crashed some time after starting "
|
|
|
|
|
"successfully.");
|
|
|
|
|
else
|
|
|
|
|
return tr("The gdb process was ended forcefully");
|
2008-12-02 12:01:29 +01:00
|
|
|
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 "
|
2011-03-30 15:58:58 +02:00
|
|
|
"to the gdb process. For example, the process may not be running, "
|
2008-12-02 12:01:29 +01:00
|
|
|
"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 "
|
2011-03-30 15:58:58 +02:00
|
|
|
"the gdb process. For example, the process may not be running.");
|
2008-12-02 12:01:29 +01:00
|
|
|
default:
|
2013-10-17 14:52:10 +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
|
2012-11-02 16:14:43 +01:00
|
|
|
static inline QString msgWinException(const QByteArray &data, unsigned *exCodeIn = 0)
|
2011-01-21 16:42:57 +01:00
|
|
|
{
|
2012-11-02 16:14:43 +01:00
|
|
|
if (exCodeIn)
|
|
|
|
|
*exCodeIn = 0;
|
2011-01-21 16:42:57 +01:00
|
|
|
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);
|
2012-11-02 16:14:43 +01:00
|
|
|
if (exCodeIn)
|
|
|
|
|
*exCodeIn = exCode;
|
2011-01-21 16:42:57 +01:00
|
|
|
const quint64 address = data.mid(addressPos).trimmed().toULongLong(0, 0);
|
|
|
|
|
QString rc;
|
|
|
|
|
QTextStream str(&rc);
|
2013-10-17 14:52:10 +02:00
|
|
|
str << GdbEngine::tr("An exception was triggered:") << ' ';
|
2011-01-21 16:42:57 +01:00
|
|
|
formatWindowsException(exCode, address, 0, 0, 0, str);
|
|
|
|
|
str << '.';
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-15 14:47:14 +01:00
|
|
|
void GdbEngine::readDebugeeOutput(const QByteArray &data)
|
|
|
|
|
{
|
2015-03-05 12:50:57 +01:00
|
|
|
if (isMostlyHarmlessMessage(data.mid(2, data.size() - 4)))
|
|
|
|
|
return;
|
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
|
|
|
}
|
|
|
|
|
|
2011-04-08 15:38:50 +02:00
|
|
|
static bool isNameChar(char c)
|
|
|
|
|
{
|
|
|
|
|
// could be 'stopped' or 'shlibs-added'
|
|
|
|
|
return (c >= 'a' && c <= 'z') || c == '-';
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-30 12:12:29 +02:00
|
|
|
static bool contains(const QByteArray &message, const char *pattern, int size)
|
|
|
|
|
{
|
|
|
|
|
const int s = message.size();
|
|
|
|
|
if (s < size)
|
|
|
|
|
return false;
|
|
|
|
|
const int pos = message.indexOf(pattern);
|
|
|
|
|
if (pos == -1)
|
|
|
|
|
return false;
|
2012-06-13 10:15:56 +02:00
|
|
|
const bool beginFits = pos == 0 || message.at(pos - 1) == '\n';
|
|
|
|
|
const bool endFits = pos + size == s || message.at(pos + size) == '\n';
|
|
|
|
|
return beginFits && endFits;
|
2012-05-30 12:12:29 +02:00
|
|
|
}
|
|
|
|
|
|
2012-05-07 14:29:22 +02:00
|
|
|
static bool isGdbConnectionError(const QByteArray &message)
|
|
|
|
|
{
|
|
|
|
|
// Handle messages gdb client produces when the target exits (gdbserver)
|
|
|
|
|
//
|
|
|
|
|
// we get this as response either to a specific command, e.g.
|
|
|
|
|
// 31^error,msg="Remote connection closed"
|
|
|
|
|
// or as informative output:
|
|
|
|
|
// &Remote connection closed
|
2012-05-30 12:12:29 +02:00
|
|
|
|
|
|
|
|
const char msg1[] = "Remote connection closed";
|
|
|
|
|
const char msg2[] = "Remote communication error. Target disconnected.: No error.";
|
|
|
|
|
const char msg3[] = "Quit";
|
|
|
|
|
|
|
|
|
|
return contains(message, msg1, sizeof(msg1) - 1)
|
|
|
|
|
|| contains(message, msg2, sizeof(msg2) - 1)
|
|
|
|
|
|| contains(message, msg3, sizeof(msg3) - 1);
|
2012-05-07 14:29:22 +02: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
|
|
|
|
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;
|
2011-06-06 18:17:51 +02:00
|
|
|
// Token is a sequence of numbers.
|
2009-02-24 20:34:30 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2011-06-06 18:17:51 +02:00
|
|
|
// Next char decides kind of response.
|
2009-02-24 20:34:30 +01:00
|
|
|
const char c = *from++;
|
|
|
|
|
switch (c) {
|
|
|
|
|
case '*':
|
|
|
|
|
case '+':
|
|
|
|
|
case '=': {
|
|
|
|
|
QByteArray asyncClass;
|
|
|
|
|
for (; from != to; ++from) {
|
|
|
|
|
const char c = *from;
|
|
|
|
|
if (!isNameChar(c))
|
|
|
|
|
break;
|
|
|
|
|
asyncClass += *from;
|
|
|
|
|
}
|
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();
|
2015-02-17 17:44:54 +01:00
|
|
|
result.m_children.push_back(data);
|
2009-09-24 11:16:00 +02:00
|
|
|
result.m_type = GdbMi::Tuple;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
2009-02-24 20:34:30 +01:00
|
|
|
if (asyncClass == "stopped") {
|
2015-07-17 13:30:51 +02:00
|
|
|
if (m_inUpdateLocals) {
|
|
|
|
|
showMessage(_("UNEXPECTED *stopped NOTIFICATION IGNORED"), LogWarning);
|
|
|
|
|
} else {
|
|
|
|
|
handleStopResponse(result);
|
|
|
|
|
m_pendingLogStreamOutput.clear();
|
|
|
|
|
m_pendingConsoleStreamOutput.clear();
|
|
|
|
|
}
|
2009-02-24 20:34:30 +01:00
|
|
|
} else if (asyncClass == "running") {
|
2015-07-17 13:30:51 +02:00
|
|
|
if (m_inUpdateLocals) {
|
|
|
|
|
showMessage(_("UNEXPECTED *running NOTIFICATION IGNORED"), LogWarning);
|
2012-08-21 11:30:59 +02:00
|
|
|
} else {
|
2015-07-17 13:30:51 +02:00
|
|
|
GdbMi threads = result["thread-id"];
|
|
|
|
|
threadsHandler()->notifyRunning(threads.data());
|
|
|
|
|
if (state() == InferiorRunOk || state() == InferiorSetupRequested) {
|
|
|
|
|
// We get multiple *running after thread creation and in Windows terminals.
|
|
|
|
|
showMessage(QString::fromLatin1("NOTE: INFERIOR STILL RUNNING IN STATE %1.").
|
|
|
|
|
arg(QLatin1String(DebuggerEngine::stateName(state()))));
|
|
|
|
|
} else if (HostOsInfo::isWindowsHost() && (state() == InferiorStopRequested
|
|
|
|
|
|| state() == InferiorShutdownRequested)) {
|
|
|
|
|
// FIXME: Breakpoints on Windows are exceptions which are thrown in newly
|
|
|
|
|
// created threads so we have to filter out the running threads messages when
|
|
|
|
|
// we request a stop.
|
|
|
|
|
} else {
|
|
|
|
|
notifyInferiorRunOk();
|
|
|
|
|
}
|
2012-08-21 11:30:59 +02:00
|
|
|
}
|
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"
|
2012-06-02 03:30:21 +02:00
|
|
|
|
|
|
|
|
// id="/lib/i386-linux-gnu/libc.so.6"
|
|
|
|
|
// target-name="/lib/i386-linux-gnu/libc.so.6"
|
|
|
|
|
// host-name="/lib/i386-linux-gnu/libc.so.6"
|
|
|
|
|
// symbols-loaded="0",thread-group="i1"
|
2013-05-03 18:26:10 +02:00
|
|
|
QByteArray id = result["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();
|
2012-06-02 03:30:21 +02:00
|
|
|
Module module;
|
|
|
|
|
module.startAddress = 0;
|
|
|
|
|
module.endAddress = 0;
|
2013-05-03 18:26:10 +02:00
|
|
|
module.hostPath = _(result["host-name"].data());
|
|
|
|
|
module.modulePath = _(result["target-name"].data());
|
2012-06-02 03:30:21 +02:00
|
|
|
module.moduleName = QFileInfo(module.hostPath).baseName();
|
2012-06-04 18:06:59 +02:00
|
|
|
modulesHandler()->updateModule(module);
|
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"
|
2013-05-03 18:26:10 +02:00
|
|
|
QByteArray id = result["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);
|
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"
|
2013-05-03 18:26:10 +02:00
|
|
|
QByteArray id = result["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) {
|
2013-05-03 18:26:10 +02:00
|
|
|
id = result["pid"].data();
|
2010-07-15 14:54:17 +02:00
|
|
|
pid = id.toInt();
|
|
|
|
|
}
|
|
|
|
|
if (pid)
|
|
|
|
|
notifyInferiorPid(pid);
|
2012-03-28 09:37:02 +02:00
|
|
|
handleThreadGroupCreated(result);
|
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"}"
|
2013-05-03 18:26:10 +02:00
|
|
|
QByteArray id = result["id"].data();
|
2010-03-23 14:49:02 +01:00
|
|
|
showStatusMessage(tr("Thread %1 created").arg(_(id)), 1000);
|
2012-10-19 16:37:57 +02:00
|
|
|
ThreadData thread;
|
|
|
|
|
thread.id = ThreadId(id.toLong());
|
2013-05-03 18:26:10 +02:00
|
|
|
thread.groupId = result["group-id"].data();
|
2012-10-19 16:37:57 +02:00
|
|
|
threadsHandler()->updateThread(thread);
|
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"}"
|
2013-05-03 18:26:10 +02:00
|
|
|
QByteArray id = result["id"].data();
|
2010-03-23 14:49:02 +01:00
|
|
|
showStatusMessage(tr("Thread group %1 exited").arg(_(id)), 1000);
|
2012-03-28 09:37:02 +02:00
|
|
|
handleThreadGroupExited(result);
|
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"}"
|
2013-05-03 18:26:10 +02:00
|
|
|
QByteArray id = result["id"].data();
|
|
|
|
|
QByteArray groupid = result["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);
|
2012-10-19 16:37:57 +02:00
|
|
|
threadsHandler()->removeThread(ThreadId(id.toLong()));
|
2009-04-07 09:51:21 +02:00
|
|
|
} else if (asyncClass == "thread-selected") {
|
2013-05-03 18:26:10 +02:00
|
|
|
QByteArray id = result["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"}"
|
2011-06-07 17:28:45 +02:00
|
|
|
} else if (asyncClass == "breakpoint-modified") {
|
|
|
|
|
// New in FSF gdb since 2011-04-27.
|
2011-06-15 14:02:26 +02:00
|
|
|
// "{bkpt={number="3",type="breakpoint",disp="keep",
|
|
|
|
|
// enabled="y",addr="<MULTIPLE>",times="1",
|
|
|
|
|
// original-location="\\",simple_gdbtest_app.cpp\\":135"},
|
|
|
|
|
// {number="3.1",enabled="y",addr="0x0805ff68",
|
|
|
|
|
// func="Vector<int>::Vector(int)",
|
|
|
|
|
// file="simple_gdbtest_app.cpp",
|
2013-11-27 16:10:14 +01:00
|
|
|
// fullname="/data/...line="135"},{number="3.2"...}}.."
|
2011-06-15 14:02:26 +02:00
|
|
|
|
|
|
|
|
// Note the leading comma in original-location. Filter it out.
|
|
|
|
|
// We don't need the field anyway.
|
|
|
|
|
QByteArray ba = result.toString();
|
|
|
|
|
ba = '[' + ba.mid(6, ba.size() - 7) + ']';
|
|
|
|
|
const int pos1 = ba.indexOf(",original-location");
|
|
|
|
|
const int pos2 = ba.indexOf("\":", pos1 + 2);
|
|
|
|
|
const int pos3 = ba.indexOf('"', pos2 + 2);
|
2012-03-06 19:27:49 +01:00
|
|
|
ba.remove(pos1, pos3 - pos1 + 1);
|
2011-06-15 14:02:26 +02:00
|
|
|
result = GdbMi();
|
|
|
|
|
result.fromString(ba);
|
|
|
|
|
BreakHandler *handler = breakHandler();
|
2015-01-10 01:07:01 +01:00
|
|
|
Breakpoint bp;
|
2011-06-15 14:02:26 +02:00
|
|
|
BreakpointResponse br;
|
|
|
|
|
foreach (const GdbMi &bkpt, result.children()) {
|
2013-05-03 18:26:10 +02:00
|
|
|
const QByteArray nr = bkpt["number"].data();
|
2011-06-24 16:25:30 +02:00
|
|
|
BreakpointResponseId rid(nr);
|
2011-09-01 18:56:07 +02:00
|
|
|
if (!isHiddenBreakpoint(rid)) {
|
2011-06-24 16:25:30 +02:00
|
|
|
if (nr.contains('.')) {
|
|
|
|
|
// A sub-breakpoint.
|
|
|
|
|
BreakpointResponse sub;
|
|
|
|
|
updateResponse(sub, bkpt);
|
|
|
|
|
sub.id = rid;
|
|
|
|
|
sub.type = br.type;
|
2015-01-10 01:07:01 +01:00
|
|
|
bp.insertSubBreakpoint(sub);
|
2011-06-24 16:25:30 +02:00
|
|
|
} else {
|
|
|
|
|
// A primary breakpoint.
|
2015-01-10 01:07:01 +01:00
|
|
|
bp = handler->findBreakpointByResponseId(rid);
|
2011-06-24 19:02:12 +02:00
|
|
|
//qDebug() << "NR: " << nr << "RID: " << rid
|
2015-01-10 01:07:01 +01:00
|
|
|
// << "ID: " << bp.id();
|
|
|
|
|
br = bp.response();
|
2011-06-24 16:25:30 +02:00
|
|
|
updateResponse(br, bkpt);
|
2015-01-10 01:07:01 +01:00
|
|
|
bp.setResponse(br);
|
2011-06-24 16:25:30 +02:00
|
|
|
}
|
2011-06-15 14:02:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
2011-06-24 14:17:16 +02:00
|
|
|
} else if (asyncClass == "breakpoint-created") {
|
|
|
|
|
// "{bkpt={number="1",type="breakpoint",disp="del",enabled="y",
|
|
|
|
|
// addr="<PENDING>",pending="main",times="0",
|
2012-03-19 14:12:29 +01:00
|
|
|
// original-location="main"}}" -- or --
|
|
|
|
|
// {bkpt={number="2",type="hw watchpoint",disp="keep",enabled="y",
|
2013-11-27 16:10:14 +01:00
|
|
|
// what="*0xbfffed48",times="0",original-location="*0xbfffed48"}}
|
2011-06-24 19:02:12 +02:00
|
|
|
BreakHandler *handler = breakHandler();
|
2011-06-24 14:17:16 +02:00
|
|
|
foreach (const GdbMi &bkpt, result.children()) {
|
|
|
|
|
BreakpointResponse br;
|
2012-03-19 14:12:29 +01:00
|
|
|
br.type = BreakpointByFileAndLine;
|
2011-06-24 14:17:16 +02:00
|
|
|
updateResponse(br, bkpt);
|
2012-03-19 14:12:29 +01:00
|
|
|
handler->handleAlienBreakpoint(br, this);
|
2011-06-24 14:17:16 +02:00
|
|
|
}
|
|
|
|
|
} else if (asyncClass == "breakpoint-deleted") {
|
|
|
|
|
// "breakpoint-deleted" "{id="1"}"
|
|
|
|
|
// New in FSF gdb since 2011-04-27.
|
2013-05-03 18:26:10 +02:00
|
|
|
QByteArray nr = result["id"].data();
|
2011-06-24 16:25:30 +02:00
|
|
|
BreakpointResponseId rid(nr);
|
2015-01-10 01:07:01 +01:00
|
|
|
if (Breakpoint bp = breakHandler()->findBreakpointByResponseId(rid)) {
|
2012-08-21 14:16:07 +02:00
|
|
|
// This also triggers when a temporary breakpoint is hit.
|
|
|
|
|
// We do not really want that, as this loses all information.
|
|
|
|
|
// FIXME: Use a special marker for this case?
|
2015-07-16 08:49:43 +02:00
|
|
|
// if (!bp.isOneShot()) ... is not sufficient.
|
|
|
|
|
// It keeps temporary "Jump" breakpoints alive.
|
|
|
|
|
bp.removeAlienBreakpoint();
|
2012-08-21 14:16:07 +02:00
|
|
|
}
|
2012-10-15 16:16:51 +02:00
|
|
|
} else if (asyncClass == "cmd-param-changed") {
|
|
|
|
|
// New since 2012-08-09
|
|
|
|
|
// "{param="debug remote",value="1"}"
|
2013-06-06 17:58:59 +02:00
|
|
|
} else if (asyncClass == "memory-changed") {
|
|
|
|
|
// New since 2013
|
|
|
|
|
// "{thread-group="i1",addr="0x0918a7a8",len="0x10"}"
|
2015-06-16 16:12:23 +02:00
|
|
|
} else if (asyncClass == "tsv-created") {
|
|
|
|
|
// New since 2013-02-06
|
|
|
|
|
} else if (asyncClass == "tsv-modified") {
|
|
|
|
|
// New since 2013-02-06
|
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-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
|
2012-11-02 16:14:43 +01:00
|
|
|
unsigned exCode;
|
|
|
|
|
m_lastWinException = msgWinException(data, &exCode);
|
2011-01-21 16:42:57 +01:00
|
|
|
showMessage(m_lastWinException, LogMisc);
|
2012-11-02 16:14:43 +01:00
|
|
|
const Task::TaskType type = isFatalWinException(exCode) ? Task::Error : Task::Warning;
|
2013-08-29 18:25:59 +02:00
|
|
|
TaskHub::addTask(type, m_lastWinException, Constants::TASK_CATEGORY_DEBUGGER_RUNTIME);
|
2009-10-22 20:04:59 +02: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-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);
|
|
|
|
|
// On Windows, the contents seem to depend on the debugger
|
|
|
|
|
// version and/or OS version used.
|
2015-03-05 12:50:57 +01:00
|
|
|
if (data.startsWith("warning:")) {
|
|
|
|
|
if (isMostlyHarmlessMessage(data)) {
|
|
|
|
|
showMessage(_("Mostly harmless terminal warning suppressed."), LogWarning);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2010-11-30 12:47:53 +01:00
|
|
|
showMessage(_(data.mid(9)), AppStuff); // Cut "warning: "
|
2015-03-05 12:50:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_pendingLogStreamOutput += data;
|
2012-03-21 16:13:01 +01:00
|
|
|
|
2012-05-07 14:29:22 +02:00
|
|
|
if (isGdbConnectionError(data)) {
|
2012-03-21 16:13:01 +01:00
|
|
|
notifyInferiorExited();
|
2012-05-07 14:29:22 +02:00
|
|
|
break;
|
2012-03-21 16:13:01 +01:00
|
|
|
}
|
2012-03-30 17:36:44 +02:00
|
|
|
|
2014-07-28 14:23:52 +02:00
|
|
|
if (boolSetting(IdentifyDebugInfoPackages)) {
|
2013-03-21 18:00:18 +01:00
|
|
|
// From SuSE's gdb: >&"Missing separate debuginfo for ...\n"
|
|
|
|
|
// ">&"Try: zypper install -C \"debuginfo(build-id)=c084ee5876ed1ac12730181c9f07c3e027d8e943\"\n"
|
|
|
|
|
if (data.startsWith("Missing separate debuginfo for ")) {
|
|
|
|
|
m_lastMissingDebugInfo = QString::fromLocal8Bit(data.mid(32));
|
|
|
|
|
} else if (data.startsWith("Try: zypper")) {
|
|
|
|
|
QString cmd = QString::fromLocal8Bit(data.mid(4));
|
|
|
|
|
|
|
|
|
|
Task task(Task::Warning,
|
|
|
|
|
tr("Missing debug information for %1\nTry: %2")
|
|
|
|
|
.arg(m_lastMissingDebugInfo).arg(cmd),
|
2014-06-05 14:15:54 +02:00
|
|
|
FileName(), 0, Debugger::Constants::TASK_CATEGORY_DEBUGGER_DEBUGINFO);
|
2013-03-21 18:00:18 +01:00
|
|
|
|
2013-08-19 12:04:51 +02:00
|
|
|
TaskHub::addTask(task);
|
2013-03-21 18:00:18 +01:00
|
|
|
|
|
|
|
|
DebugInfoTask dit;
|
|
|
|
|
dit.command = cmd;
|
|
|
|
|
m_debugInfoTaskHandler->addTask(task.taskId, dit);
|
|
|
|
|
}
|
2012-03-30 17:36:44 +02:00
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-24 20:34:30 +01:00
|
|
|
case '^': {
|
2015-02-05 15:47:07 +01:00
|
|
|
DebuggerResponse 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);
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
if (resultClass == "done")
|
2015-02-05 15:47:07 +01:00
|
|
|
response.resultClass = ResultDone;
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
else if (resultClass == "running")
|
2015-02-05 15:47:07 +01:00
|
|
|
response.resultClass = ResultRunning;
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
else if (resultClass == "connected")
|
2015-02-05 15:47:07 +01:00
|
|
|
response.resultClass = ResultConnected;
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
else if (resultClass == "error")
|
2015-02-05 15:47:07 +01:00
|
|
|
response.resultClass = ResultError;
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
else if (resultClass == "exit")
|
2015-02-05 15:47:07 +01:00
|
|
|
response.resultClass = ResultExit;
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
else
|
2015-02-05 15:47:07 +01:00
|
|
|
response.resultClass = ResultUnknown;
|
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;
|
2011-07-05 17:57:57 +02:00
|
|
|
response.logStreamOutput = m_pendingLogStreamOutput;
|
|
|
|
|
response.consoleStreamOutput = m_pendingConsoleStreamOutput;
|
2009-02-24 20:34:30 +01:00
|
|
|
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: {
|
2012-02-07 14:07:21 +01:00
|
|
|
qDebug() << "UNKNOWN RESPONSE TYPE '" << c << "'. REST: " << from;
|
2009-02-24 20:34:30 +01:00
|
|
|
break;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::readGdbStandardError()
|
|
|
|
|
{
|
2015-06-08 12:10:11 +02:00
|
|
|
QByteArray err = m_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;
|
2011-03-30 15:58:58 +02: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
|
|
|
|
2015-06-08 12:10:11 +02:00
|
|
|
QByteArray out = m_gdbProc.readAllStandardOutput();
|
2012-05-18 02:28:41 +02:00
|
|
|
m_inbuffer.append(out);
|
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;
|
|
|
|
|
if (m_inbuffer.at(end - 1) == '\r') {
|
|
|
|
|
--end;
|
|
|
|
|
if (end == start)
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2009-10-27 12:05:03 +01:00
|
|
|
m_busy = true;
|
2012-05-18 02:28:41 +02:00
|
|
|
QByteArray ba = QByteArray::fromRawData(m_inbuffer.constData() + start, end - start);
|
|
|
|
|
handleResponse(ba);
|
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()
|
|
|
|
|
{
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(InferiorStopRequested);
|
2011-02-03 16:49:28 +01:00
|
|
|
|
2015-01-14 16:58:10 +01:00
|
|
|
if (terminal()->sendInterrupt())
|
|
|
|
|
return;
|
|
|
|
|
|
2012-03-09 15:04:59 +01:00
|
|
|
if (usesExecInterrupt()) {
|
2015-08-14 17:15:32 +02:00
|
|
|
postCommand("-exec-interrupt");
|
2011-02-03 16:49:28 +01:00
|
|
|
} else {
|
|
|
|
|
showStatusMessage(tr("Stop requested..."), 5000);
|
|
|
|
|
showMessage(_("TRYING TO INTERRUPT INFERIOR"));
|
2014-06-05 14:15:54 +02:00
|
|
|
if (HostOsInfo::isWindowsHost() && !m_isQnxGdb) {
|
2013-11-04 09:47:34 +01:00
|
|
|
QTC_ASSERT(state() == InferiorStopRequested, qDebug() << state(); notifyInferiorStopFailed());
|
|
|
|
|
QTC_ASSERT(!m_signalOperation, notifyInferiorStopFailed());
|
2015-05-27 13:59:56 +02:00
|
|
|
m_signalOperation = runParameters().device->signalOperation();
|
2013-11-04 09:47:34 +01:00
|
|
|
QTC_ASSERT(m_signalOperation, notifyInferiorStopFailed());
|
|
|
|
|
connect(m_signalOperation.data(), SIGNAL(finished(QString)),
|
|
|
|
|
SLOT(handleInterruptDeviceInferior(QString)));
|
|
|
|
|
|
2015-05-27 13:59:56 +02:00
|
|
|
m_signalOperation->setDebuggerCommand(runParameters().debuggerCommand);
|
2013-11-04 09:47:34 +01:00
|
|
|
m_signalOperation->interruptProcess(inferiorPid());
|
|
|
|
|
} else {
|
|
|
|
|
interruptInferior2();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::handleInterruptDeviceInferior(const QString &error)
|
|
|
|
|
{
|
|
|
|
|
if (error.isEmpty()) {
|
|
|
|
|
showMessage(QLatin1String("Interrupted ") + QString::number(inferiorPid()));
|
|
|
|
|
notifyInferiorStopOk();
|
|
|
|
|
} else {
|
|
|
|
|
showMessage(error, LogError);
|
|
|
|
|
notifyInferiorStopFailed();
|
2011-02-03 16:49:28 +01:00
|
|
|
}
|
2013-11-04 09:47:34 +01:00
|
|
|
m_signalOperation->disconnect(this);
|
|
|
|
|
m_signalOperation.clear();
|
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
|
|
|
}
|
|
|
|
|
|
2015-02-04 10:48:33 +01:00
|
|
|
void GdbEngine::runCommand(const DebuggerCommand &command)
|
|
|
|
|
{
|
|
|
|
|
QByteArray cmd = command.function + "({" + command.args + "})";
|
2015-02-10 13:40:26 +01:00
|
|
|
postCommand("python theDumper." + cmd, command.flags, command.callback);
|
2015-02-04 10:48:33 +01:00
|
|
|
}
|
|
|
|
|
|
2015-02-06 10:30:25 +01:00
|
|
|
void GdbEngine::postCommand(const QByteArray &command, int flags,
|
|
|
|
|
DebuggerCommand::Callback callback)
|
2009-09-21 11:09:38 +02:00
|
|
|
{
|
2015-02-06 10:30:25 +01:00
|
|
|
DebuggerCommand cmd;
|
|
|
|
|
cmd.function = command;
|
2009-09-21 11:09:38 +02:00
|
|
|
cmd.flags = flags;
|
|
|
|
|
cmd.callback = callback;
|
|
|
|
|
|
2009-09-24 11:45:08 +02:00
|
|
|
if (!stateAcceptsGdbCommands(state())) {
|
2015-02-06 10:30:25 +01:00
|
|
|
PENDING_DEBUG(_("NO GDB PROCESS RUNNING, CMD IGNORED: " + cmd.function));
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("NO GDB PROCESS RUNNING, CMD IGNORED: %1 %2")
|
2015-02-06 10:30:25 +01:00
|
|
|
.arg(_(cmd.function)).arg(state()));
|
2008-12-02 12:01:29 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-18 02:28:41 +02:00
|
|
|
if (cmd.flags & RebuildBreakpointModel) {
|
2010-03-03 18:08:18 +01:00
|
|
|
++m_pendingBreakpointRequests;
|
2015-02-06 10:30:25 +01:00
|
|
|
PENDING_DEBUG(" BRWAKPOINT MODEL:" << cmd.function
|
2010-03-03 18:08:18 +01:00
|
|
|
<< "INCREMENTS PENDING TO" << m_pendingBreakpointRequests);
|
2008-12-02 12:01:29 +01:00
|
|
|
} else {
|
2015-02-06 10:30:25 +01:00
|
|
|
PENDING_DEBUG(" OTHER (IN):" << cmd.function
|
2010-03-03 18:08:18 +01:00
|
|
|
<< "LEAVES PENDING BREAKPOINT AT" << m_pendingBreakpointRequests);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2011-12-13 15:40:31 +01:00
|
|
|
if (!(cmd.flags & Discardable))
|
|
|
|
|
++m_nonDiscardableCount;
|
|
|
|
|
|
2015-08-14 17:15:32 +02:00
|
|
|
if (cmd.flags & NeedsStop) {
|
|
|
|
|
showMessage(_("RUNNING NEEDS-STOP COMMAND " + cmd.function));
|
|
|
|
|
if (state() == InferiorStopRequested) {
|
|
|
|
|
if (cmd.flags & LosesChild) {
|
|
|
|
|
notifyInferiorIll();
|
|
|
|
|
return;
|
2009-10-23 21:54:27 +02:00
|
|
|
}
|
2015-08-14 17:15:32 +02:00
|
|
|
showMessage(_("CHILD ALREADY BEING INTERRUPTED. STILL HOPING."));
|
|
|
|
|
// Calling shutdown() here breaks all situations where two
|
|
|
|
|
// NeedsStop commands are issued in quick succession.
|
|
|
|
|
} else if (!m_temporaryStopPending && state() == InferiorRunOk) {
|
|
|
|
|
showStatusMessage(tr("Stopping temporarily"), 1000);
|
|
|
|
|
m_temporaryStopPending = true;
|
|
|
|
|
requestInterruptInferior();
|
2009-09-25 08:35:31 +02:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2015-08-14 17:15:32 +02:00
|
|
|
flushCommand(cmd);
|
2009-11-02 16:40:16 +01:00
|
|
|
}
|
|
|
|
|
|
2015-02-06 10:30:25 +01:00
|
|
|
void GdbEngine::flushCommand(const DebuggerCommand &cmd0)
|
2009-05-04 18:30:22 +02:00
|
|
|
{
|
2010-07-15 14:03:45 +02:00
|
|
|
if (!stateAcceptsGdbCommands(state())) {
|
2015-02-06 10:30:25 +01:00
|
|
|
showMessage(_(cmd0.function), LogInput);
|
2010-07-15 14:03:45 +02:00
|
|
|
showMessage(_("GDB PROCESS ACCEPTS NO CMD IN STATE %1 ").arg(state()));
|
2009-09-15 12:16:21 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-08 12:10:11 +02:00
|
|
|
QTC_ASSERT(m_gdbProc.state() == QProcess::Running, return);
|
2010-09-07 10:59:40 +02:00
|
|
|
|
2011-06-06 18:17:51 +02:00
|
|
|
const int token = ++currentToken();
|
|
|
|
|
|
2015-02-06 10:30:25 +01:00
|
|
|
DebuggerCommand cmd = cmd0;
|
2015-02-17 17:44:54 +01:00
|
|
|
cmd.postTime = QTime::currentTime().msecsSinceStartOfDay();
|
2015-02-06 10:30:25 +01:00
|
|
|
m_commandForToken[token] = cmd;
|
2010-06-28 14:53:31 +02:00
|
|
|
if (cmd.flags & ConsoleCommand)
|
2015-02-06 10:30:25 +01:00
|
|
|
cmd.function = "-interpreter-exec console \"" + cmd.function + '"';
|
|
|
|
|
cmd.function = QByteArray::number(token) + cmd.function;
|
|
|
|
|
showMessage(_(cmd.function), LogInput);
|
2009-09-15 12:16:21 +02:00
|
|
|
|
2011-06-06 18:17:51 +02:00
|
|
|
if (m_scheduledTestResponses.contains(token)) {
|
|
|
|
|
// Fake response for test cases.
|
|
|
|
|
QByteArray buffer = m_scheduledTestResponses.value(token);
|
|
|
|
|
buffer.replace("@TOKEN@", QByteArray::number(token));
|
|
|
|
|
m_scheduledTestResponses.remove(token);
|
2014-04-17 14:09:47 +02:00
|
|
|
showMessage(_("FAKING TEST RESPONSE (TOKEN: %2, RESPONSE: %3)")
|
2011-06-06 18:17:51 +02:00
|
|
|
.arg(token).arg(_(buffer)));
|
|
|
|
|
QMetaObject::invokeMethod(this, "handleResponse",
|
|
|
|
|
Q_ARG(QByteArray, buffer));
|
|
|
|
|
} else {
|
2015-02-06 10:30:25 +01:00
|
|
|
write(cmd.function + "\r\n");
|
2009-11-02 16:59:57 +01:00
|
|
|
|
2011-06-06 18:17:51 +02:00
|
|
|
// Start Watchdog.
|
|
|
|
|
if (m_commandTimer.interval() <= 20000)
|
|
|
|
|
m_commandTimer.setInterval(commandTimeoutTime());
|
|
|
|
|
// 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.
|
2015-02-06 10:30:25 +01:00
|
|
|
if (!cmd.function.endsWith("-gdb-exit"))
|
2011-06-06 18:17:51 +02:00
|
|
|
m_commandTimer.start();
|
2009-11-03 14:56:27 +01:00
|
|
|
|
2011-06-06 18:17:51 +02:00
|
|
|
//if (cmd.flags & LosesChild)
|
|
|
|
|
// notifyInferiorIll();
|
|
|
|
|
}
|
2009-05-04 18:30:22 +02:00
|
|
|
}
|
|
|
|
|
|
2009-12-11 16:59:34 +01:00
|
|
|
int GdbEngine::commandTimeoutTime() const
|
|
|
|
|
{
|
2014-07-28 14:23:52 +02:00
|
|
|
int time = 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()
|
|
|
|
|
{
|
2015-02-06 10:30:25 +01:00
|
|
|
QList<int> keys = m_commandForToken.keys();
|
2014-06-16 18:25:52 +04:00
|
|
|
Utils::sort(keys);
|
2009-12-11 16:59:34 +01:00
|
|
|
bool killIt = false;
|
2009-11-03 14:56:27 +01:00
|
|
|
foreach (int key, keys) {
|
2015-02-06 10:30:25 +01:00
|
|
|
const DebuggerCommand &cmd = m_commandForToken.value(key);
|
2009-12-11 16:59:34 +01:00
|
|
|
if (!(cmd.flags & NonCriticalResponse))
|
|
|
|
|
killIt = true;
|
2015-02-06 10:30:25 +01:00
|
|
|
showMessage(_(QByteArray::number(key) + ": " + cmd.function));
|
2009-11-03 14:56:27 +01:00
|
|
|
}
|
2009-12-11 16:59:34 +01:00
|
|
|
if (killIt) {
|
2012-05-10 14:49:13 +02:00
|
|
|
QStringList commands;
|
2015-02-06 10:30:25 +01:00
|
|
|
foreach (const DebuggerCommand &cmd, m_commandForToken)
|
2012-05-10 14:49:13 +02:00
|
|
|
commands << QString(_("\"%1\"")).arg(
|
2015-02-06 10:30:25 +01:00
|
|
|
QString::fromLatin1(cmd.function));
|
2012-05-10 14:49:13 +02:00
|
|
|
showMessage(_("TIMED OUT WAITING FOR GDB REPLY. "
|
|
|
|
|
"COMMANDS STILL IN PROGRESS: ") + commands.join(_(", ")));
|
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 "
|
2011-04-29 09:53:34 +02:00
|
|
|
"to a command within %n second(s). 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 "
|
2012-10-01 10:14:11 +02:00
|
|
|
"longer or aborting debugging.", 0, timeOut / 1000);
|
2009-12-11 16:59:34 +01:00
|
|
|
QMessageBox *mb = showMessageBox(QMessageBox::Critical,
|
2011-03-30 15:58:58 +02:00
|
|
|
tr("GDB not responding"), msg,
|
2009-12-11 16:59:34 +01:00
|
|
|
QMessageBox::Ok | QMessageBox::Cancel);
|
2011-03-30 15:58:58 +02:00
|
|
|
mb->button(QMessageBox::Cancel)->setText(tr("Give GDB more time"));
|
2009-12-11 16:59:34 +01:00
|
|
|
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.
|
2015-06-08 12:10:11 +02:00
|
|
|
m_gdbProc.kill();
|
2014-10-20 18:04:15 +02:00
|
|
|
notifyEngineShutdownFailed();
|
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
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleResultRecord(DebuggerResponse *response)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2011-06-06 18:17:51 +02:00
|
|
|
//qDebug() << "TOKEN:" << response->token
|
2009-05-06 20:55:21 +02:00
|
|
|
// << " ACCEPTABLE:" << m_oldestAcceptableToken;
|
2011-06-06 18:17:51 +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;
|
|
|
|
|
|
2015-02-06 10:30:25 +01:00
|
|
|
if (!m_commandForToken.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).
|
2012-02-16 09:55:47 +02:00
|
|
|
arg(QString::fromLatin1(stateName(state()))));
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response->resultClass == ResultError) {
|
2013-05-03 18:26:10 +02:00
|
|
|
QByteArray msg = response->data["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"));
|
2015-02-03 23:58:49 +02:00
|
|
|
AsynchronousMessageBox::critical(
|
2009-10-05 08:56:02 +02:00
|
|
|
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.") {
|
2011-03-30 15:58:58 +02:00
|
|
|
// Handle a case known to appear on GDB 6.4 symbianelf when
|
2009-10-05 08:56:02 +02:00
|
|
|
// 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();
|
2014-11-25 13:08:18 +01:00
|
|
|
//Core::AsynchronousMessageBox::critical(
|
2010-07-09 17:07:59 +02:00
|
|
|
// 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"));
|
2015-02-03 23:58:49 +02:00
|
|
|
AsynchronousMessageBox::critical(
|
2010-11-24 15:55:09 +01:00
|
|
|
tr("Setting breakpoints failed"), QString::fromLocal8Bit(msg));
|
2011-07-29 12:00:11 +02:00
|
|
|
QTC_CHECK(state() == InferiorRunOk);
|
2010-11-24 15:55:09 +01:00
|
|
|
notifyInferiorSpontaneousStop();
|
|
|
|
|
notifyEngineIll();
|
2012-05-07 14:29:22 +02:00
|
|
|
} else if (isGdbConnectionError(msg)) {
|
|
|
|
|
notifyInferiorExited();
|
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);
|
2015-02-03 23:58:49 +02:00
|
|
|
AsynchronousMessageBox::critical(tr("Executable Failed"), logMsg);
|
2011-01-21 16:42:57 +01:00
|
|
|
showStatusMessage(tr("Executable failed: %1").arg(logMsg));
|
2009-09-28 09:18:24 +02:00
|
|
|
}
|
2009-08-13 09:42:34 +02:00
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-06 10:30:25 +01:00
|
|
|
DebuggerCommand cmd = m_commandForToken.take(token);
|
2014-07-28 14:23:52 +02:00
|
|
|
if (boolSetting(LogTimeStamps)) {
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("Response time: %1: %2 s")
|
2015-02-06 10:30:25 +01:00
|
|
|
.arg(_(cmd.function))
|
2015-02-17 17:44:54 +01:00
|
|
|
.arg(QTime::fromMSecsSinceStartOfDay(cmd.postTime).msecsTo(QTime::currentTime()) / 1000.),
|
2010-06-14 17:23:25 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-30 14:18:47 +02:00
|
|
|
bool isExpectedResult =
|
2015-02-05 15:47:07 +01:00
|
|
|
(response->resultClass == ResultError) // Can always happen.
|
|
|
|
|
|| (response->resultClass == ResultRunning && (cmd.flags & RunRequest))
|
|
|
|
|
|| (response->resultClass == ResultExit && (cmd.flags & ExitRequest))
|
|
|
|
|
|| (response->resultClass == ResultDone);
|
|
|
|
|
// ResultDone can almost "always" happen. Known examples are:
|
2015-02-06 10:30:25 +01:00
|
|
|
// (response->resultClass == ResultDone && cmd.function == "continue")
|
2010-06-30 14:39:41 +02:00
|
|
|
// Happens with some incarnations of gdb 6.8 for "jump to line"
|
2015-02-06 10:30:25 +01:00
|
|
|
// (response->resultClass == ResultDone && cmd.function.startsWith("jump"))
|
|
|
|
|
// (response->resultClass == ResultDone && cmd.function.startsWith("detach"))
|
2011-07-04 14:53:37 +02:00
|
|
|
// Happens when stepping finishes very quickly and issues *stopped and ^done
|
|
|
|
|
// instead of ^running and *stopped
|
2015-02-05 15:47:07 +01:00
|
|
|
// (response->resultClass == ResultDone && (cmd.flags & RunRequest));
|
2010-06-30 14:18:47 +02:00
|
|
|
|
2010-06-30 14:39:41 +02:00
|
|
|
if (!isExpectedResult) {
|
2015-05-27 13:59:56 +02:00
|
|
|
const DebuggerRunParameters &rp = runParameters();
|
|
|
|
|
Abi abi = rp.toolChainAbi;
|
2012-06-28 10:00:04 +02:00
|
|
|
if (abi.os() == Abi::WindowsOS
|
2015-02-06 10:30:25 +01:00
|
|
|
&& cmd.function.startsWith("attach")
|
2015-05-27 13:59:56 +02:00
|
|
|
&& (rp.startMode == AttachExternal || rp.useTerminal))
|
2011-03-24 14:21:52 +01:00
|
|
|
{
|
|
|
|
|
// Ignore spurious 'running' responses to 'attach'.
|
|
|
|
|
} else {
|
2015-02-05 15:47:07 +01:00
|
|
|
QByteArray rsp = DebuggerResponse::stringFromResultClass(response->resultClass);
|
2015-02-06 10:30:25 +01:00
|
|
|
rsp = "UNEXPECTED RESPONSE '" + rsp + "' TO COMMAND '" + cmd.function + "'";
|
2010-04-09 12:49:21 +02:00
|
|
|
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
|
|
|
|
2011-12-13 15:40:31 +01:00
|
|
|
if (!(cmd.flags & Discardable))
|
|
|
|
|
--m_nonDiscardableCount;
|
|
|
|
|
|
2015-07-17 13:30:51 +02:00
|
|
|
m_inUpdateLocals = (cmd.flags & InUpdateLocals);
|
|
|
|
|
|
2010-06-30 14:39:41 +02:00
|
|
|
if (cmd.callback)
|
2015-02-05 15:47:07 +01:00
|
|
|
cmd.callback(*response);
|
2010-06-30 14:39:41 +02:00
|
|
|
|
2012-05-18 02:28:41 +02:00
|
|
|
if (cmd.flags & RebuildBreakpointModel) {
|
2010-03-03 18:08:18 +01:00
|
|
|
--m_pendingBreakpointRequests;
|
2015-03-05 18:17:00 +01:00
|
|
|
PENDING_DEBUG(" BREAKPOINT" << cmd.function);
|
2010-03-03 18:08:18 +01:00
|
|
|
if (m_pendingBreakpointRequests <= 0) {
|
|
|
|
|
PENDING_DEBUG("\n\n ... AND TRIGGERS BREAKPOINT MODEL UPDATE\n");
|
|
|
|
|
attemptBreakpointSynchronization();
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
} else {
|
2015-02-06 10:30:25 +01:00
|
|
|
PENDING_DEBUG(" OTHER (OUT):" << cmd.function
|
2010-03-03 18:08:18 +01:00
|
|
|
<< "LEAVES PENDING BREAKPOINT AT" << m_pendingBreakpointRequests);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2009-05-07 12:18:45 +02: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.
|
2015-02-06 10:30:25 +01:00
|
|
|
if (m_commandsDoneCallback && m_commandForToken.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;
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response->resultClass != ResultRunning) //only start if the thing is not already running
|
2014-06-05 17:22:51 +02:00
|
|
|
(this->*cont)();
|
2009-09-21 11:09:38 +02:00
|
|
|
} else {
|
2015-02-06 10:30:25 +01:00
|
|
|
PENDING_DEBUG("MISSING TOKENS: " << m_commandForToken.keys());
|
2009-05-07 12:18:45 +02:00
|
|
|
}
|
2009-11-03 14:56:27 +01:00
|
|
|
|
2015-02-06 10:30:25 +01:00
|
|
|
if (m_commandForToken.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
|
|
|
{
|
2013-02-27 15:12:36 +01:00
|
|
|
return true;
|
2011-02-16 12:41:06 +01:00
|
|
|
return state() == InferiorStopOk
|
|
|
|
|
|| state() == InferiorUnrunnable;
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2012-04-10 09:36:15 +02:00
|
|
|
void GdbEngine::executeDebuggerCommand(const QString &command, DebuggerLanguages languages)
|
2011-02-16 12:41:06 +01:00
|
|
|
{
|
2012-04-10 09:36:15 +02:00
|
|
|
if (!(languages & CppLanguage))
|
|
|
|
|
return;
|
2011-07-29 12:00:11 +02:00
|
|
|
QTC_CHECK(acceptsDebuggerCommands());
|
2015-02-06 10:30:25 +01:00
|
|
|
DebuggerCommand cmd;
|
|
|
|
|
cmd.function = command.toLatin1();
|
2011-11-04 18:14:40 +01:00
|
|
|
flushCommand(cmd);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2015-02-26 15:04:02 +01:00
|
|
|
// This is triggered when switching snapshots.
|
2009-09-23 15:28:50 +02:00
|
|
|
void GdbEngine::updateAll()
|
2009-03-02 15:14:12 +01:00
|
|
|
{
|
2013-11-27 16:10:14 +01:00
|
|
|
//PENDING_DEBUG("UPDATING ALL\n");
|
|
|
|
|
QTC_CHECK(state() == InferiorUnrunnable || state() == InferiorStopOk);
|
|
|
|
|
reloadModulesInternal();
|
2015-02-11 16:05:55 +01:00
|
|
|
DebuggerCommand cmd = stackCommand(action(MaximalStackDepth)->value().toInt());
|
|
|
|
|
cmd.flags = NoFlags;
|
|
|
|
|
cmd.callback = [this](const DebuggerResponse &r) { handleStackListFrames(r, false); };
|
|
|
|
|
runCommand(cmd);
|
2013-11-27 16:10:14 +01:00
|
|
|
stackHandler()->setCurrentIndex(0);
|
2015-02-06 01:26:47 +01:00
|
|
|
postCommand("-thread-info", NoFlags, CB(handleThreadInfo));
|
2013-11-27 16:10:14 +01:00
|
|
|
reloadRegisters();
|
|
|
|
|
updateLocals();
|
2009-09-30 12:27:03 +02:00
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleQuerySources(const DebuggerResponse &response)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-12-09 11:13:20 +01:00
|
|
|
m_sourcesListUpdating = false;
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultDone) {
|
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();
|
2011-06-28 18:00:38 +02:00
|
|
|
// "^done,files=[{file="../../../../bin/dumper/dumper.cpp",
|
|
|
|
|
// fullname="/data5/dev/ide/main/bin/dumper/dumper.cpp"},
|
2013-05-03 18:26:10 +02:00
|
|
|
GdbMi files = response.data["files"];
|
2008-12-02 12:01:29 +01:00
|
|
|
foreach (const GdbMi &item, files.children()) {
|
2013-05-03 18:26:10 +02:00
|
|
|
GdbMi fileName = item["file"];
|
2012-03-23 17:53:30 +01:00
|
|
|
if (fileName.data().endsWith("<built-in>"))
|
|
|
|
|
continue;
|
2013-05-03 18:26:10 +02:00
|
|
|
GdbMi fullName = item["fullname"];
|
2010-03-05 18:24:32 +01:00
|
|
|
QString file = QString::fromLocal8Bit(fileName.data());
|
2012-03-23 17:53:30 +01:00
|
|
|
QString full;
|
2009-10-22 20:04:59 +02:00
|
|
|
if (fullName.isValid()) {
|
2012-03-23 17:53:30 +01:00
|
|
|
full = cleanupFullName(QString::fromLocal8Bit(fullName.data()));
|
2010-03-05 18:24:32 +01:00
|
|
|
m_fullToShortName[full] = file;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2012-03-23 17:53:30 +01:00
|
|
|
m_shortToFullName[file] = full;
|
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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleExecuteJumpToLine(const DebuggerResponse &response)
|
2010-06-30 14:18:47 +02:00
|
|
|
{
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultRunning) {
|
2012-08-21 11:30:59 +02:00
|
|
|
// All is fine. Waiting for a *running
|
|
|
|
|
// and the temporary breakpoint to be hit.
|
|
|
|
|
notifyInferiorRunOk(); // Only needed for gdb < 7.0.
|
2015-02-05 15:47:07 +01:00
|
|
|
} else if (response.resultClass == ResultError) {
|
2014-11-27 14:31:02 +01:00
|
|
|
// Could be "Unreasonable jump request" or similar.
|
|
|
|
|
QString out = tr("Cannot jump. Stopped");
|
|
|
|
|
QByteArray msg = response.data["msg"].data();
|
|
|
|
|
if (!msg.isEmpty())
|
|
|
|
|
out += QString::fromLatin1(". " + msg);
|
|
|
|
|
showStatusMessage(out);
|
|
|
|
|
notifyInferiorRunFailed();
|
2015-02-05 15:47:07 +01:00
|
|
|
} else if (response.resultClass == ResultDone) {
|
2010-06-30 14:18:47 +02:00
|
|
|
// This happens on old gdb. Trigger the effect of a '*stopped'.
|
|
|
|
|
showStatusMessage(tr("Jumped. Stopped"));
|
2010-07-12 15:39:22 +02:00
|
|
|
notifyInferiorSpontaneousStop();
|
2015-02-06 01:26:47 +01:00
|
|
|
handleStop2(response.data);
|
2009-04-30 15:21:37 +02:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleExecuteRunToLine(const DebuggerResponse &response)
|
2010-06-30 14:39:41 +02:00
|
|
|
{
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultRunning) {
|
2012-08-21 11:30:59 +02:00
|
|
|
// All is fine. Waiting for a *running
|
|
|
|
|
// and the temporary breakpoint to be hit.
|
2015-02-05 15:47:07 +01:00
|
|
|
} else if (response.resultClass == ResultDone) {
|
2011-05-03 18:14:18 +02:00
|
|
|
// This happens on old gdb (Mac). gdb is not stopped yet,
|
|
|
|
|
// but merely accepted the continue.
|
2010-06-30 14:39:41 +02:00
|
|
|
// >&"continue\n"
|
|
|
|
|
// >~"Continuing.\n"
|
|
|
|
|
//>~"testArray () at ../simple/app.cpp:241\n"
|
|
|
|
|
//>~"241\t s[1] = \"b\";\n"
|
|
|
|
|
//>122^done
|
|
|
|
|
showStatusMessage(tr("Target line hit. Stopped"));
|
2011-05-03 18:14:18 +02:00
|
|
|
notifyInferiorRunOk();
|
2010-06-30 14:39:41 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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-10-09 10:20:42 +02:00
|
|
|
void GdbEngine::handleStopResponse(const GdbMi &data)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2012-08-29 11:42:28 +02:00
|
|
|
// Ignore trap on Windows terminals, which results in
|
|
|
|
|
// spurious "* stopped" message.
|
|
|
|
|
if (!data.isValid() && m_terminalTrap && Abi::hostAbi().os() == Abi::WindowsOS) {
|
|
|
|
|
m_terminalTrap = false;
|
|
|
|
|
showMessage(_("IGNORING TERMINAL SIGTRAP"), LogMisc);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2009-10-30 17:40:29 +01:00
|
|
|
|
2010-07-13 08:41:27 +02:00
|
|
|
if (isDying()) {
|
|
|
|
|
notifyInferiorStopOk();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-03 18:26:10 +02:00
|
|
|
GdbMi threads = data["stopped-thread"];
|
2012-10-19 16:37:57 +02:00
|
|
|
threadsHandler()->notifyStopped(threads.data());
|
|
|
|
|
|
2013-05-03 18:26:10 +02:00
|
|
|
const QByteArray reason = data["reason"].data();
|
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")
|
2013-05-03 18:26:10 +02:00
|
|
|
.arg(_(data["exit-code"].toString()));
|
2009-04-30 22:22:37 +02:00
|
|
|
} else if (reason == "exited-signalled" || reason == "signal-received") {
|
2010-03-23 14:49:02 +01:00
|
|
|
msg = tr("Application exited after receiving signal %1")
|
2013-05-03 18:26:10 +02:00
|
|
|
.arg(_(data["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
|
|
|
}
|
2015-06-12 15:17:14 +02:00
|
|
|
// Only show the message. Ramp-down will be triggered by -thread-group-exited.
|
2009-09-14 11:02:36 +02:00
|
|
|
showStatusMessage(msg);
|
2009-02-17 16:25:48 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-24 15:30:42 +01:00
|
|
|
// Ignore signals from the process stub.
|
2015-06-12 15:17:14 +02:00
|
|
|
const GdbMi frame = data["frame"];
|
|
|
|
|
const QByteArray func = frame["from"].data();
|
2015-05-27 13:59:56 +02:00
|
|
|
if (runParameters().useTerminal
|
2015-02-24 15:30:42 +01:00
|
|
|
&& data["reason"].data() == "signal-received"
|
|
|
|
|
&& data["signal-name"].data() == "SIGSTOP"
|
|
|
|
|
&& (func.endsWith("/ld-linux.so.2")
|
|
|
|
|
|| func.endsWith("/ld-linux-x86-64.so.2")))
|
|
|
|
|
{
|
|
|
|
|
showMessage(_("INTERNAL CONTINUE AFTER SIGSTOP FROM STUB"), LogMisc);
|
|
|
|
|
notifyInferiorSpontaneousStop();
|
|
|
|
|
continueInferiorInternal();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-14 17:15:32 +02:00
|
|
|
if (m_temporaryStopPending) {
|
|
|
|
|
showMessage(_("INTERNAL CONTINUE AFTER TEMPORARY STOP"), LogMisc);
|
|
|
|
|
m_temporaryStopPending = false;
|
|
|
|
|
notifyInferiorStopOk();
|
|
|
|
|
continueInferiorInternal();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-17 10:00:40 +02:00
|
|
|
bool gotoHandleStop1 = true;
|
|
|
|
|
if (!m_fullStartDone) {
|
|
|
|
|
m_fullStartDone = true;
|
|
|
|
|
postCommand("sharedlibrary .*");
|
2015-02-06 01:26:47 +01:00
|
|
|
postCommand("p 3", NoFlags, [this, data](const DebuggerResponse &) { handleStop1(data); });
|
2012-04-17 10:00:40 +02:00
|
|
|
gotoHandleStop1 = false;
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-03 18:26:10 +02:00
|
|
|
BreakpointResponseId rid(data["bkptno"].data());
|
2012-08-29 11:42:28 +02:00
|
|
|
int lineNumber = 0;
|
|
|
|
|
QString fullName;
|
2015-01-22 12:05:00 +01:00
|
|
|
QByteArray function;
|
2012-08-29 11:42:28 +02:00
|
|
|
if (frame.isValid()) {
|
2013-05-03 18:26:10 +02:00
|
|
|
const GdbMi lineNumberG = frame["line"];
|
2015-01-22 12:05:00 +01:00
|
|
|
function = frame["func"].data();
|
2012-08-29 11:42:28 +02:00
|
|
|
if (lineNumberG.isValid()) {
|
2013-05-07 12:09:54 +02:00
|
|
|
lineNumber = lineNumberG.toInt();
|
2013-05-03 18:26:10 +02:00
|
|
|
fullName = cleanupFullName(QString::fromLocal8Bit(frame["fullname"].data()));
|
2012-08-29 11:42:28 +02:00
|
|
|
if (fullName.isEmpty())
|
2013-05-03 18:26:10 +02:00
|
|
|
fullName = QString::fromLocal8Bit(frame["file"].data());
|
2012-08-29 11:42:28 +02:00
|
|
|
} // found line number
|
|
|
|
|
} else {
|
|
|
|
|
showMessage(_("INVALID STOPPED REASON"), LogWarning);
|
|
|
|
|
}
|
2010-11-23 15:54:57 +01:00
|
|
|
|
2014-12-12 10:20:07 +01:00
|
|
|
if (rid.isValid() && frame.isValid() && !isQFatalBreakpoint(rid)) {
|
2010-11-23 15:54:57 +01:00
|
|
|
// Use opportunity to update the breakpoint marker position.
|
2011-06-24 16:25:30 +02:00
|
|
|
//qDebug() << " PROBLEM: " << m_qmlBreakpointNumbers << rid
|
|
|
|
|
// << isQmlStepBreakpoint1(rid)
|
|
|
|
|
// << isQmlStepBreakpoint2(rid)
|
2015-01-10 01:07:01 +01:00
|
|
|
Breakpoint bp = breakHandler()->findBreakpointByResponseId(rid);
|
|
|
|
|
const BreakpointResponse &response = bp.response();
|
2010-11-18 15:47:50 +01:00
|
|
|
QString fileName = response.fileName;
|
|
|
|
|
if (fileName.isEmpty())
|
2015-01-10 01:07:01 +01:00
|
|
|
fileName = bp.fileName();
|
2010-11-18 15:47:50 +01:00
|
|
|
if (fileName.isEmpty())
|
2010-11-23 15:54:57 +01:00
|
|
|
fileName = fullName;
|
|
|
|
|
if (!fileName.isEmpty())
|
2015-01-10 01:07:01 +01:00
|
|
|
bp.setMarkerFileAndLine(fileName, lineNumber);
|
2010-11-18 12:30:56 +01:00
|
|
|
}
|
|
|
|
|
|
2011-06-24 16:25:30 +02:00
|
|
|
//qDebug() << "BP " << rid << data.toString();
|
2010-11-23 15:54:57 +01:00
|
|
|
// Quickly set the location marker.
|
2014-07-28 14:23:52 +02:00
|
|
|
if (lineNumber && !boolSetting(OperateByInstruction)
|
2014-10-24 10:28:28 +02:00
|
|
|
&& QFileInfo::exists(fullName)
|
2015-01-22 12:05:00 +01:00
|
|
|
&& !isQFatalBreakpoint(rid)
|
|
|
|
|
&& function != "qt_v4TriggeredBreakpointHook")
|
2010-12-16 19:06:33 +01:00
|
|
|
gotoLocation(Location(fullName, lineNumber));
|
2010-11-23 15:54:57 +01:00
|
|
|
|
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"
|
2011-06-06 16:19:56 +02:00
|
|
|
// "&"Cannot access memory at address 0xbfffedd4\n"
|
|
|
|
|
// or, on S40,
|
|
|
|
|
// "*running,thread-id="30""
|
|
|
|
|
// "&"Warning:\n""
|
|
|
|
|
// "&"Cannot insert breakpoint -33.\n"
|
|
|
|
|
// "&"Error accessing memory address 0x11673fc: Input/output error.\n""
|
2010-07-09 17:07:59 +02:00
|
|
|
// 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
|
|
|
|
|
notifyInferiorSpontaneousStop();
|
2012-08-21 10:29:54 +02:00
|
|
|
} else if (state() == InferiorStopOk) {
|
2011-01-24 13:37:07 +01:00
|
|
|
// That's expected.
|
2015-02-24 15:30:42 +01:00
|
|
|
} else if (state() == InferiorStopRequested) {
|
2010-07-09 17:07:59 +02:00
|
|
|
notifyInferiorStopOk();
|
2015-02-24 15:30:42 +01:00
|
|
|
} else if (state() == EngineRunRequested) {
|
|
|
|
|
// This is gdb 7+'s initial *stopped in response to attach that
|
|
|
|
|
// appears before the ^done is seen.
|
|
|
|
|
notifyEngineRunAndInferiorStopOk();
|
2015-05-27 13:59:56 +02:00
|
|
|
const DebuggerRunParameters &rp = runParameters();
|
|
|
|
|
if (rp.useTerminal)
|
2015-03-02 13:02:15 +01:00
|
|
|
continueInferiorInternal();
|
2015-02-24 15:30:42 +01:00
|
|
|
return;
|
|
|
|
|
} else {
|
|
|
|
|
QTC_CHECK(false);
|
2009-10-09 10:54:03 +02:00
|
|
|
}
|
|
|
|
|
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(InferiorStopOk);
|
2012-08-21 10:29:54 +02:00
|
|
|
|
2012-04-17 10:00:40 +02:00
|
|
|
if (gotoHandleStop1)
|
|
|
|
|
handleStop1(data);
|
2010-11-23 12:31:47 +01:00
|
|
|
}
|
|
|
|
|
|
2014-05-19 23:32:59 +03:00
|
|
|
static QByteArray stopSignal(const Abi &abi)
|
2011-03-24 14:21:52 +01:00
|
|
|
{
|
|
|
|
|
return (abi.os() == Abi::WindowsOS) ? QByteArray("SIGTRAP") : QByteArray("SIGINT");
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-17 10:00:40 +02:00
|
|
|
void GdbEngine::handleStop1(const GdbMi &data)
|
|
|
|
|
{
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(InferiorStopOk);
|
2012-08-21 10:29:54 +02:00
|
|
|
QTC_ASSERT(!isDying(), return);
|
2013-05-03 18:26:10 +02:00
|
|
|
const GdbMi frame = data["frame"];
|
|
|
|
|
const QByteArray reason = data["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;
|
2014-07-28 14:23:52 +02:00
|
|
|
if (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());
|
2013-05-03 18:26:10 +02:00
|
|
|
QString funcName = _(frame["func"].data());
|
|
|
|
|
QString fileName = QString::fromLocal8Bit(frame["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"
|
2013-05-03 18:26:10 +02:00
|
|
|
GdbMi resultVar = data["gdb-result-var"];
|
2010-05-19 12:00:01 +02:00
|
|
|
if (resultVar.isValid())
|
|
|
|
|
m_resultVarName = resultVar.data();
|
|
|
|
|
else
|
|
|
|
|
m_resultVarName.clear();
|
|
|
|
|
|
2013-03-21 17:32:34 +01:00
|
|
|
if (!m_systemDumpersLoaded) {
|
|
|
|
|
m_systemDumpersLoaded = true;
|
2014-07-28 14:23:52 +02:00
|
|
|
if (m_gdbVersion >= 70400 && boolSetting(LoadGdbDumpers))
|
2015-01-25 01:36:08 +01:00
|
|
|
postCommand("importPlainDumpers on");
|
|
|
|
|
else
|
|
|
|
|
postCommand("importPlainDumpers off");
|
2013-03-21 17:32:34 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-27 16:10:14 +01:00
|
|
|
handleStop2(data);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2012-04-17 10:00:40 +02:00
|
|
|
void GdbEngine::handleStop2(const GdbMi &data)
|
2009-10-09 10:59:56 +02:00
|
|
|
{
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(InferiorStopOk);
|
2012-08-21 10:29:54 +02:00
|
|
|
QTC_ASSERT(!isDying(), return);
|
2010-07-13 08:41:27 +02:00
|
|
|
|
2011-05-24 12:38:54 +02:00
|
|
|
// A user initiated stop looks like the following. Note that there is
|
|
|
|
|
// this extra "stopper thread" created and "properly" reported by gdb.
|
|
|
|
|
//
|
|
|
|
|
// dNOTE: INFERIOR RUN OK
|
|
|
|
|
// dState changed from InferiorRunRequested(10) to InferiorRunOk(11).
|
|
|
|
|
// >*running,thread-id="all"
|
|
|
|
|
// >=thread-exited,id="11",group-id="i1"
|
|
|
|
|
// sThread 11 in group i1 exited
|
|
|
|
|
// dState changed from InferiorRunOk(11) to InferiorStopRequested(13).
|
|
|
|
|
// dCALL: INTERRUPT INFERIOR
|
|
|
|
|
// sStop requested...
|
|
|
|
|
// dTRYING TO INTERRUPT INFERIOR
|
|
|
|
|
// >=thread-created,id="12",group-id="i1"
|
|
|
|
|
// sThread 12 created
|
|
|
|
|
// >~"[New Thread 8576.0x1154]\n"
|
|
|
|
|
// s[New Thread 8576.0x1154]
|
|
|
|
|
// >*running,thread-id="all"
|
|
|
|
|
// >~"[Switching to Thread 8576.0x1154]\n"
|
|
|
|
|
// >*stopped,reason="signal-received",signal-name="SIGTRAP",
|
|
|
|
|
// signal-meaning="Trace/breakpointtrap",frame={addr="0x7c90120f",func=
|
|
|
|
|
// "ntdll!DbgUiConnectToDbg",args=[],from="C:\\WINDOWS\\system32\\ntdll.dll"},
|
|
|
|
|
// thread-id="12",stopped-threads="all"
|
|
|
|
|
// dNOTE: INFERIOR STOP OK
|
|
|
|
|
// dState changed from InferiorStopRequested(13) to InferiorStopOk(14).
|
|
|
|
|
|
2013-05-03 18:26:10 +02:00
|
|
|
const QByteArray reason = data["reason"].data();
|
2015-05-27 13:59:56 +02:00
|
|
|
const DebuggerRunParameters &rp = runParameters();
|
2010-01-13 10:31:03 +01:00
|
|
|
|
2011-05-24 12:38:54 +02:00
|
|
|
bool isStopperThread = false;
|
|
|
|
|
|
2015-05-27 13:59:56 +02:00
|
|
|
if (rp.toolChainAbi.os() == Abi::WindowsOS
|
|
|
|
|
&& rp.useTerminal
|
2011-03-24 14:21:52 +01:00
|
|
|
&& reason == "signal-received"
|
2013-05-03 18:26:10 +02:00
|
|
|
&& data["signal-name"].data() == "SIGTRAP")
|
2011-03-24 14:21:52 +01:00
|
|
|
{
|
2011-05-24 12:38:54 +02:00
|
|
|
// This is the stopper thread. That also means that the
|
|
|
|
|
// reported thread is not the one we'd like to expose
|
|
|
|
|
// to the user.
|
|
|
|
|
isStopperThread = true;
|
2010-01-13 10:31:03 +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"
|
2013-05-03 18:26:10 +02:00
|
|
|
const GdbMi wpt = data["wpt"];
|
|
|
|
|
const BreakpointResponseId rid(wpt["number"].data());
|
2015-01-10 01:07:01 +01:00
|
|
|
const Breakpoint bp = breakHandler()->findBreakpointByResponseId(rid);
|
2013-05-03 18:26:10 +02:00
|
|
|
const quint64 bpAddress = wpt["exp"].data().mid(1).toULongLong(0, 0);
|
2011-05-09 08:35:58 +02:00
|
|
|
QString msg;
|
2015-01-10 01:07:01 +01:00
|
|
|
if (bp.type() == WatchpointAtExpression)
|
|
|
|
|
msg = bp.msgWatchpointByExpressionTriggered(rid.majorPart(), bp.expression());
|
|
|
|
|
if (bp.type() == WatchpointAtAddress)
|
|
|
|
|
msg = bp.msgWatchpointByAddressTriggered(rid.majorPart(), bpAddress);
|
2013-05-03 18:26:10 +02:00
|
|
|
GdbMi value = data["value"];
|
|
|
|
|
GdbMi oldValue = value["old"];
|
|
|
|
|
GdbMi newValue = value["new"];
|
2011-03-04 19:49:03 +01:00
|
|
|
if (oldValue.isValid() && newValue.isValid()) {
|
|
|
|
|
msg += QLatin1Char(' ');
|
|
|
|
|
msg += tr("Value changed from %1 to %2.")
|
|
|
|
|
.arg(_(oldValue.data())).arg(_(newValue.data()));
|
|
|
|
|
}
|
|
|
|
|
showStatusMessage(msg);
|
2010-11-23 16:14:19 +01:00
|
|
|
} else if (reason == "breakpoint-hit") {
|
2013-05-03 18:26:10 +02:00
|
|
|
GdbMi gNumber = data["bkptno"]; // 'number' or 'bkptno'?
|
2010-11-17 17:16:34 +01:00
|
|
|
if (!gNumber.isValid())
|
2013-05-03 18:26:10 +02:00
|
|
|
gNumber = data["number"];
|
2011-06-24 16:25:30 +02:00
|
|
|
const BreakpointResponseId rid(gNumber.data());
|
2013-05-03 18:26:10 +02:00
|
|
|
const QByteArray threadId = data["thread-id"].data();
|
2015-01-10 01:07:01 +01:00
|
|
|
const Breakpoint bp = breakHandler()->findBreakpointByResponseId(rid);
|
|
|
|
|
showStatusMessage(bp.msgBreakpointTriggered(rid.majorPart(), _(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") {
|
2013-05-03 18:26:10 +02:00
|
|
|
QByteArray name = data["signal-name"].data();
|
|
|
|
|
QByteArray meaning = data["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.
|
2015-05-27 13:59:56 +02:00
|
|
|
if (name == stopSignal(rp.toolChainAbi) || rp.expectedSignals.contains(name)) {
|
2011-03-24 14:21:52 +01:00
|
|
|
showMessage(_(name + " CONSIDERED HARMLESS. CONTINUING."));
|
2010-11-26 14:35:36 +01:00
|
|
|
} else {
|
2011-12-14 15:32:48 +01:00
|
|
|
showMessage(_("HANDLING SIGNAL " + name));
|
2014-07-28 14:23:52 +02:00
|
|
|
if (boolSetting(UseMessageBoxForSignals) && !isStopperThread)
|
2011-02-23 11:28:20 +01:00
|
|
|
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.
|
|
|
|
|
QTimer::singleShot(0, this, SLOT(handleStop2()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::handleStop2()
|
|
|
|
|
{
|
2013-11-27 16:10:14 +01:00
|
|
|
postCommand("-thread-info", Discardable, CB(handleThreadInfo));
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleInfoProc(const DebuggerResponse &response)
|
2009-11-09 22:11:56 +01:00
|
|
|
{
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultDone) {
|
2009-11-09 22:11:56 +01:00
|
|
|
static QRegExp re(_("\\bprocess ([0-9]+)\n"));
|
|
|
|
|
QTC_ASSERT(re.isValid(), return);
|
2011-07-05 17:57:57 +02:00
|
|
|
if (re.indexIn(_(response.consoleStreamOutput)) != -1)
|
2009-11-09 22:11:56 +01:00
|
|
|
maybeHandleInferiorPidChanged(re.cap(1));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleShowVersion(const DebuggerResponse &response)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("PARSING VERSION: " + response.toString()));
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultDone) {
|
2013-11-27 16:10:14 +01:00
|
|
|
bool isMacGdb = false;
|
|
|
|
|
int gdbBuildVersion = -1;
|
2008-12-02 12:01:29 +01:00
|
|
|
m_gdbVersion = 100;
|
2012-02-10 07:42:44 +01:00
|
|
|
m_isQnxGdb = false;
|
2011-07-05 17:57:57 +02:00
|
|
|
QString msg = QString::fromLocal8Bit(response.consoleStreamOutput);
|
2010-05-27 15:41:52 +02:00
|
|
|
extractGdbVersion(msg,
|
2013-11-27 16:10:14 +01:00
|
|
|
&m_gdbVersion, &gdbBuildVersion, &isMacGdb, &m_isQnxGdb);
|
2011-12-16 14:14:15 +01:00
|
|
|
|
2014-01-28 11:21:57 +01:00
|
|
|
// On Mac, FSF GDB does not work sufficiently well,
|
|
|
|
|
// and on Linux and Windows we require at least 7.4.1,
|
|
|
|
|
// on Android 7.3.1.
|
|
|
|
|
bool isSupported = m_gdbVersion >= 70300;
|
2011-12-16 14:14:15 +01:00
|
|
|
if (isSupported)
|
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)
|
2013-11-27 16:10:14 +01:00
|
|
|
.arg(gdbBuildVersion).arg(_(isMacGdb ? " (APPLE)" : "")));
|
2011-06-07 17:28:45 +02:00
|
|
|
|
2013-11-27 16:10:14 +01:00
|
|
|
if (usesExecInterrupt())
|
|
|
|
|
postCommand("set target-async on", ConsoleCommand);
|
|
|
|
|
else
|
|
|
|
|
postCommand("set target-async off", ConsoleCommand);
|
2012-03-28 09:37:02 +02:00
|
|
|
|
2015-05-27 13:59:56 +02:00
|
|
|
if (runParameters().multiProcess)
|
2012-03-28 09:37:02 +02:00
|
|
|
postCommand("set detach-on-fork off", ConsoleCommand);
|
2013-05-28 12:12:42 +02:00
|
|
|
//postCommand("set build-id-verbose 2", ConsoleCommand);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleListFeatures(const DebuggerResponse &response)
|
2011-06-15 14:02:26 +02:00
|
|
|
{
|
|
|
|
|
showMessage(_("FEATURES: " + response.toString()));
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handlePythonSetup(const DebuggerResponse &response)
|
2009-10-14 09:41:14 +02:00
|
|
|
{
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(EngineSetupRequested);
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultDone) {
|
2010-06-25 09:07:14 +02:00
|
|
|
GdbMi data;
|
2011-07-05 17:57:57 +02:00
|
|
|
data.fromStringMultiple(response.consoleStreamOutput);
|
2015-01-23 19:09:08 +01:00
|
|
|
watchHandler()->addDumpers(data["dumpers"]);
|
2014-01-28 11:27:13 +01:00
|
|
|
loadInitScript();
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(EngineSetupRequested);
|
2014-01-28 11:27:13 +01:00
|
|
|
showMessage(_("ENGINE SUCCESSFULLY STARTED"));
|
|
|
|
|
notifyEngineSetupOk();
|
|
|
|
|
} else {
|
2014-04-02 15:08:50 +02:00
|
|
|
QByteArray msg = response.data["msg"].data();
|
|
|
|
|
if (msg.contains("Python scripting is not supported in this copy of GDB.")) {
|
|
|
|
|
QString out1 = _("The selected build of GDB does not support Python scripting.");
|
|
|
|
|
QString out2 = _("It cannot be used in Qt Creator.");
|
|
|
|
|
showStatusMessage(out1 + QLatin1Char(' ') + out2);
|
2015-02-03 23:58:49 +02:00
|
|
|
AsynchronousMessageBox::critical(tr("Execution Error"), out1 + _("<br>") + out2);
|
2014-04-02 15:08:50 +02:00
|
|
|
}
|
2014-01-28 11:27:13 +01:00
|
|
|
notifyEngineSetupFailed();
|
2010-09-23 14:58:45 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-09 17:07:59 +02:00
|
|
|
void GdbEngine::showExecutionError(const QString &message)
|
|
|
|
|
{
|
2015-02-03 23:58:49 +02:00
|
|
|
AsynchronousMessageBox::critical(tr("Execution Error"),
|
2013-10-17 13:48:04 +02:00
|
|
|
tr("Cannot continue debugged process:") + QLatin1Char('\n') + message);
|
2010-07-09 17:07:59 +02:00
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleExecuteContinue(const DebuggerResponse &response)
|
2009-07-21 14:37:31 +02:00
|
|
|
{
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(InferiorRunRequested);
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultRunning) {
|
2012-08-21 11:30:59 +02:00
|
|
|
// All is fine. Waiting for a *running.
|
|
|
|
|
notifyInferiorRunOk(); // Only needed for gdb < 7.0.
|
2010-07-09 17:07:59 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2013-05-03 18:26:10 +02:00
|
|
|
QByteArray msg = response.data["msg"].data();
|
2011-04-06 17:34:08 +02:00
|
|
|
if (msg.startsWith("Cannot find bounds of current function")) {
|
2010-07-09 17:07:59 +02:00
|
|
|
notifyInferiorRunFailed();
|
|
|
|
|
if (isDying())
|
2009-11-02 16:59:57 +01:00
|
|
|
return;
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(InferiorStopOk);
|
2010-07-09 17:07:59 +02:00
|
|
|
showStatusMessage(tr("Stopped."), 5000);
|
2015-02-06 01:26:47 +01:00
|
|
|
reloadStack();
|
2011-09-07 15:20:04 +02:00
|
|
|
} else if (msg.startsWith("Cannot access memory at address")) {
|
|
|
|
|
// Happens on single step on ARM prolog and epilogs.
|
2011-04-06 17:34:08 +02:00
|
|
|
} else if (msg.startsWith("\"finish\" not meaningful in the outermost frame")) {
|
|
|
|
|
notifyInferiorRunFailed();
|
|
|
|
|
if (isDying())
|
|
|
|
|
return;
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(InferiorStopOk);
|
2011-04-06 17:34:08 +02:00
|
|
|
// FIXME: Fix translation in master.
|
|
|
|
|
showStatusMessage(QString::fromLocal8Bit(msg), 5000);
|
|
|
|
|
gotoLocation(stackHandler()->currentFrame());
|
2012-03-03 01:48:43 +01:00
|
|
|
} else if (msg.startsWith("Cannot execute this command while the selected thread is running.")) {
|
|
|
|
|
showExecutionError(QString::fromLocal8Bit(msg));
|
|
|
|
|
notifyInferiorRunFailed() ;
|
2010-07-09 17:07:59 +02:00
|
|
|
} 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();
|
2012-04-17 08:01:25 +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;
|
2011-03-24 14:21:52 +01:00
|
|
|
|
2011-04-28 11:10:01 +02:00
|
|
|
// Gdb running on windows often delivers "fullnames" which
|
|
|
|
|
// (a) have no drive letter and (b) are not normalized.
|
|
|
|
|
if (Abi::hostAbi().os() == Abi::WindowsOS) {
|
2015-06-22 12:29:40 +03:00
|
|
|
if (fileName.isEmpty())
|
|
|
|
|
return QString();
|
2011-03-24 14:21:52 +01:00
|
|
|
QFileInfo fi(fileName);
|
|
|
|
|
if (fi.isReadable())
|
|
|
|
|
cleanFilePath = QDir::cleanPath(fi.absoluteFilePath());
|
|
|
|
|
}
|
2012-03-23 17:53:30 +01:00
|
|
|
|
2014-07-28 14:23:52 +02:00
|
|
|
if (!boolSetting(AutoEnrichParameters))
|
2012-03-23 17:53:30 +01:00
|
|
|
return cleanFilePath;
|
|
|
|
|
|
2015-05-27 13:59:56 +02:00
|
|
|
const QString sysroot = runParameters().sysRoot;
|
2012-03-23 17:53:30 +01:00
|
|
|
if (QFileInfo(cleanFilePath).isReadable())
|
|
|
|
|
return cleanFilePath;
|
|
|
|
|
if (!sysroot.isEmpty() && fileName.startsWith(QLatin1Char('/'))) {
|
|
|
|
|
cleanFilePath = sysroot + fileName;
|
|
|
|
|
if (QFileInfo(cleanFilePath).isReadable())
|
|
|
|
|
return cleanFilePath;
|
|
|
|
|
}
|
|
|
|
|
if (m_baseNameToFullName.isEmpty()) {
|
|
|
|
|
QString debugSource = sysroot + QLatin1String("/usr/src/debug");
|
|
|
|
|
if (QFileInfo(debugSource).isDir()) {
|
|
|
|
|
QDirIterator it(debugSource, QDirIterator::Subdirectories);
|
|
|
|
|
while (it.hasNext()) {
|
|
|
|
|
it.next();
|
|
|
|
|
QString name = it.fileName();
|
|
|
|
|
if (!name.startsWith(QLatin1Char('.'))) {
|
|
|
|
|
QString path = it.filePath();
|
|
|
|
|
m_baseNameToFullName.insert(name, path);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cleanFilePath.clear();
|
2015-01-10 23:40:32 +02:00
|
|
|
const QString base = FileName::fromString(fileName).fileName();
|
2012-03-23 17:53:30 +01:00
|
|
|
|
2015-04-01 11:19:32 +02:00
|
|
|
QMap<QString, QString>::const_iterator jt = m_baseNameToFullName.constFind(base);
|
|
|
|
|
while (jt != m_baseNameToFullName.constEnd() && jt.key() == base) {
|
2012-03-23 17:53:30 +01:00
|
|
|
// FIXME: Use some heuristics to find the "best" match.
|
|
|
|
|
return jt.value();
|
|
|
|
|
//++jt;
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-09 15:27:21 +02:00
|
|
|
return cleanFilePath;
|
|
|
|
|
}
|
2009-10-28 11:59:57 +01:00
|
|
|
|
2010-07-09 17:07:59 +02:00
|
|
|
void GdbEngine::shutdownInferior()
|
|
|
|
|
{
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(InferiorShutdownRequested);
|
2015-05-27 13:59:56 +02:00
|
|
|
switch (runParameters().closeMode) {
|
2012-03-26 16:03:25 +02:00
|
|
|
case KillAtClose:
|
2014-10-09 00:13:33 +02:00
|
|
|
case KillAndExitMonitorAtClose:
|
2012-03-26 16:03:25 +02:00
|
|
|
postCommand("kill", NeedsStop | LosesChild, CB(handleInferiorShutdown));
|
2012-03-30 13:25:09 +02:00
|
|
|
return;
|
2012-03-26 16:03:25 +02:00
|
|
|
case DetachAtClose:
|
|
|
|
|
postCommand("detach", NeedsStop | LosesChild, CB(handleInferiorShutdown));
|
2012-03-30 13:25:09 +02:00
|
|
|
return;
|
2012-03-26 16:03:25 +02:00
|
|
|
}
|
|
|
|
|
QTC_ASSERT(false, notifyInferiorShutdownFailed());
|
2010-07-09 17:07:59 +02:00
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleInferiorShutdown(const DebuggerResponse &response)
|
2010-07-09 17:07:59 +02:00
|
|
|
{
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultDone) {
|
2015-06-16 15:01:46 +02:00
|
|
|
// We'll get async thread-group-exited responses to which we react.
|
|
|
|
|
// Nothing to do here.
|
|
|
|
|
// notifyInferiorShutdownOk();
|
2010-07-09 17:07:59 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2015-06-16 15:01:46 +02:00
|
|
|
// "kill" got stuck, or similar.
|
|
|
|
|
CHECK_STATE(InferiorShutdownRequested);
|
2013-05-03 18:26:10 +02:00
|
|
|
QByteArray ba = response.data["msg"].data();
|
2010-07-09 17:07:59 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2015-02-03 23:58:49 +02:00
|
|
|
AsynchronousMessageBox::critical(
|
2010-07-09 17:07:59 +02:00
|
|
|
tr("Failed to shut down application"),
|
2012-06-13 10:15:56 +02:00
|
|
|
msgInferiorStopFailed(QString::fromLocal8Bit(ba)));
|
2010-07-09 17:07:59 +02:00
|
|
|
notifyInferiorShutdownFailed();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::notifyAdapterShutdownFailed()
|
|
|
|
|
{
|
|
|
|
|
showMessage(_("ADAPTER SHUTDOWN FAILED"));
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(EngineShutdownRequested);
|
2010-07-09 17:07:59 +02:00
|
|
|
notifyEngineShutdownFailed();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::notifyAdapterShutdownOk()
|
|
|
|
|
{
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(EngineShutdownRequested);
|
2010-07-02 12:20:26 +02:00
|
|
|
showMessage(_("INITIATE GDBENGINE SHUTDOWN IN STATE %1, PROC: %2")
|
2015-06-08 12:10:11 +02:00
|
|
|
.arg(lastGoodState()).arg(m_gdbProc.state()));
|
2010-07-09 17:07:59 +02:00
|
|
|
m_commandsDoneCallback = 0;
|
2015-06-08 12:10:11 +02:00
|
|
|
switch (m_gdbProc.state()) {
|
2015-02-11 12:20:21 +01:00
|
|
|
case QProcess::Running: {
|
2015-05-27 13:59:56 +02:00
|
|
|
if (runParameters().closeMode == KillAndExitMonitorAtClose)
|
2014-10-09 00:13:33 +02:00
|
|
|
postCommand("monitor exit");
|
2015-02-11 12:20:21 +01:00
|
|
|
DebuggerCommand cmd("exitGdb");
|
|
|
|
|
cmd.flags = GdbEngine::ExitRequest;
|
|
|
|
|
cmd.callback = CB(handleGdbExit);
|
|
|
|
|
runCommand(cmd);
|
2010-09-29 10:32:41 +02:00
|
|
|
break;
|
2015-02-11 12:20:21 +01:00
|
|
|
}
|
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"));
|
2015-06-08 12:10:11 +02:00
|
|
|
m_gdbProc.kill();
|
2010-09-07 10:59:40 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleGdbExit(const DebuggerResponse &response)
|
2010-07-09 17:07:59 +02:00
|
|
|
{
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultExit) {
|
2010-07-09 17:07:59 +02:00
|
|
|
showMessage(_("GDB CLAIMS EXIT; WAITING"));
|
|
|
|
|
// Don't set state here, this will be handled in handleGdbFinished()
|
|
|
|
|
//notifyEngineShutdownOk();
|
|
|
|
|
} else {
|
2012-06-13 10:15:56 +02:00
|
|
|
QString msg = msgGdbStopFailed(
|
2013-05-03 18:26:10 +02:00
|
|
|
QString::fromLocal8Bit(response.data["msg"].data()));
|
2010-07-09 17:07:59 +02:00
|
|
|
qDebug() << (_("GDB WON'T EXIT (%1); KILLING IT").arg(msg));
|
|
|
|
|
showMessage(_("GDB WON'T EXIT (%1); KILLING IT").arg(msg));
|
2015-06-08 12:10:11 +02:00
|
|
|
m_gdbProc.kill();
|
2014-10-20 18:04:15 +02:00
|
|
|
notifyEngineShutdownFailed();
|
2010-07-09 17:07:59 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-05-07 15:20:44 +02:00
|
|
|
void GdbEngine::detachDebugger()
|
|
|
|
|
{
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(InferiorStopOk);
|
2010-07-09 17:07:59 +02:00
|
|
|
QTC_ASSERT(startMode() != AttachCore, qDebug() << startMode());
|
2010-07-12 15:39:22 +02:00
|
|
|
postCommand("detach", GdbEngine::ExitRequest, CB(handleDetach));
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleDetach(const DebuggerResponse &response)
|
2010-07-12 15:39:22 +02:00
|
|
|
{
|
|
|
|
|
Q_UNUSED(response);
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(InferiorStopOk);
|
2010-07-09 17:07:59 +02:00
|
|
|
notifyInferiorExited();
|
2010-07-02 12:20:26 +02:00
|
|
|
}
|
|
|
|
|
|
2012-03-28 09:37:02 +02:00
|
|
|
void GdbEngine::handleThreadGroupCreated(const GdbMi &result)
|
|
|
|
|
{
|
2015-06-12 15:17:14 +02:00
|
|
|
QByteArray groupId = result["id"].data();
|
|
|
|
|
QByteArray pid = result["pid"].data();
|
|
|
|
|
threadsHandler()->notifyGroupCreated(groupId, pid);
|
2012-03-28 09:37:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::handleThreadGroupExited(const GdbMi &result)
|
|
|
|
|
{
|
2015-06-12 15:17:14 +02:00
|
|
|
QByteArray groupId = result["id"].data();
|
2015-07-07 15:13:10 +02:00
|
|
|
if (threadsHandler()->notifyGroupExited(groupId)) {
|
|
|
|
|
if (m_rerunPending)
|
|
|
|
|
m_rerunPending = false;
|
|
|
|
|
else
|
|
|
|
|
notifyInferiorExited();
|
|
|
|
|
}
|
2012-03-28 09:37:02 +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
|
|
|
}
|
|
|
|
|
|
2012-06-28 10:00:04 +02:00
|
|
|
static QString msgNoGdbBinaryForToolChain(const Abi &tc)
|
2010-04-09 08:43:26 +02:00
|
|
|
{
|
2014-04-17 14:09:47 +02:00
|
|
|
return GdbEngine::tr("There is no GDB binary available for binaries in format \"%1\"")
|
2011-02-01 18:36:00 +01:00
|
|
|
.arg(tc.toString());
|
2009-10-08 17:23:27 +02:00
|
|
|
}
|
|
|
|
|
|
2012-01-12 20:28:17 +01:00
|
|
|
bool GdbEngine::hasCapability(unsigned cap) const
|
2010-02-05 10:23:55 +01:00
|
|
|
{
|
2012-01-12 20:28:17 +01:00
|
|
|
if (cap & (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
|
2011-05-09 08:35:58 +02:00
|
|
|
| WatchpointByAddressCapability
|
|
|
|
|
| WatchpointByExpressionCapability
|
2010-11-26 09:58:34 +01:00
|
|
|
| AddWatcherCapability
|
2011-08-04 14:25:38 +02:00
|
|
|
| WatchWidgetsCapability
|
2011-03-04 19:26:11 +01:00
|
|
|
| ShowModuleSymbolsCapability
|
2012-11-07 18:31:17 +01:00
|
|
|
| ShowModuleSectionsCapability
|
2011-04-01 19:47:39 +02:00
|
|
|
| CatchCapability
|
|
|
|
|
| OperateByInstructionCapability
|
2011-11-30 11:51:23 +01:00
|
|
|
| RunToLineCapability
|
2013-01-09 14:51:49 +01:00
|
|
|
| WatchComplexExpressionsCapability
|
2014-02-07 18:10:02 +01:00
|
|
|
| MemoryAddressCapability
|
2014-06-05 17:22:51 +02:00
|
|
|
| AdditionalQmlStackCapability
|
2014-12-12 08:26:55 +01:00
|
|
|
| NativeMixedCapability
|
2014-06-05 17:22:51 +02:00
|
|
|
| ResetInferiorCapability))
|
2012-01-12 20:28:17 +01:00
|
|
|
return true;
|
2010-07-13 15:57:34 +02:00
|
|
|
|
2015-05-27 13:59:56 +02:00
|
|
|
if (runParameters().startMode == AttachCore)
|
2012-01-12 20:28:17 +01:00
|
|
|
return false;
|
2010-07-13 15:57:34 +02:00
|
|
|
|
2011-06-22 12:42:59 +02:00
|
|
|
// FIXME: Remove in case we have gdb 7.x on Mac.
|
2015-05-27 13:59:56 +02:00
|
|
|
if (runParameters().toolChainAbi.os() == Abi::MacOS)
|
2012-01-12 20:28:17 +01:00
|
|
|
return false;
|
2011-06-22 12:42:59 +02:00
|
|
|
|
2012-01-12 20:28:17 +01:00
|
|
|
return cap == SnapshotCapability;
|
2010-02-05 10:23:55 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-28 10:58:21 +01:00
|
|
|
|
2009-10-08 13:47:56 +02:00
|
|
|
void GdbEngine::continueInferiorInternal()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(InferiorStopOk);
|
2010-07-20 12:14:59 +02:00
|
|
|
notifyInferiorRunRequested();
|
|
|
|
|
showStatusMessage(tr("Running requested..."), 5000);
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(InferiorRunRequested);
|
2010-03-10 16:17:40 +01:00
|
|
|
postCommand("-exec-continue", RunRequest, CB(handleExecuteContinue));
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
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()
|
|
|
|
|
{
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(InferiorStopOk);
|
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
|
|
|
{
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(InferiorStopOk);
|
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);
|
2015-01-22 12:05:00 +01:00
|
|
|
if (isNativeMixedActive()) {
|
2015-02-04 10:48:33 +01:00
|
|
|
runCommand("prepareQmlStep");
|
2015-01-22 12:05:00 +01:00
|
|
|
postCommand("-exec-continue", RunRequest, CB(handleExecuteContinue));
|
|
|
|
|
return;
|
|
|
|
|
}
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
if (isReverseDebugging())
|
2010-03-11 11:07:16 +01:00
|
|
|
postCommand("reverse-step", RunRequest, CB(handleExecuteStep));
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +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
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleExecuteStep(const DebuggerResponse &response)
|
2010-02-01 16:14:57 +01:00
|
|
|
{
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultDone) {
|
2010-10-12 11:52:11 +02:00
|
|
|
// 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.
|
2011-07-29 12:00:11 +02:00
|
|
|
QTC_CHECK(state() == InferiorStopOk);
|
2010-10-12 11:52:11 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(InferiorRunRequested);
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultRunning) {
|
2012-08-21 11:30:59 +02:00
|
|
|
// All is fine. Waiting for a *running.
|
|
|
|
|
notifyInferiorRunOk(); // Only needed for gdb < 7.0.
|
2010-07-09 17:07:59 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2013-05-03 18:26:10 +02:00
|
|
|
QByteArray msg = response.data["msg"].data();
|
2011-06-06 16:19:56 +02:00
|
|
|
if (msg.startsWith("Cannot find bounds of current function")
|
2011-09-07 15:20:04 +02:00
|
|
|
|| msg.contains("Error accessing memory address")
|
|
|
|
|
|| msg.startsWith("Cannot access memory at address")) {
|
2011-06-06 16:19:56 +02:00
|
|
|
// On S40: "40^error,msg="Warning:\nCannot insert breakpoint -39.\n"
|
|
|
|
|
//" Error accessing memory address 0x11673fc: Input/output error.\n"
|
2010-07-09 17:07:59 +02:00
|
|
|
notifyInferiorRunFailed();
|
|
|
|
|
if (isDying())
|
2010-02-01 16:14:57 +01:00
|
|
|
return;
|
2010-07-09 17:07:59 +02:00
|
|
|
executeStepI(); // Fall back to instruction-wise stepping.
|
2012-03-05 19:22:07 +01:00
|
|
|
} else if (msg.startsWith("Cannot execute this command while the selected thread is running.")) {
|
|
|
|
|
showExecutionError(QString::fromLocal8Bit(msg));
|
|
|
|
|
notifyInferiorRunFailed();
|
2012-04-13 18:23:44 +02:00
|
|
|
} else if (msg.startsWith("warning: SuspendThread failed")) {
|
|
|
|
|
// On Win: would lead to "PC register is not available" or "\312"
|
|
|
|
|
continueInferiorInternal();
|
2010-07-09 17:07:59 +02:00
|
|
|
} 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
|
|
|
{
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(InferiorStopOk);
|
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
|
|
|
{
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(InferiorStopOk);
|
2012-01-12 18:01:58 +01:00
|
|
|
postCommand("-stack-select-frame 0", Discardable);
|
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));
|
2015-09-01 15:24:10 +02:00
|
|
|
// -exec-finish in 'main' results (correctly) in
|
|
|
|
|
// 40^error,msg="\"finish\" not meaningful in the outermost frame."
|
|
|
|
|
// However, this message does not seem to get flushed before
|
|
|
|
|
// anything else happen - i.e. "never". Force some extra output.
|
|
|
|
|
postCommand("print 32");
|
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
|
|
|
{
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(InferiorStopOk);
|
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);
|
2015-01-22 12:05:00 +01:00
|
|
|
if (isNativeMixedActive()) {
|
2015-02-04 15:47:32 +01:00
|
|
|
runCommand("prepareQmlStep");
|
2015-01-22 12:05:00 +01:00
|
|
|
postCommand("-exec-continue", RunRequest, CB(handleExecuteContinue));
|
|
|
|
|
return;
|
|
|
|
|
}
|
2011-06-06 18:17:51 +02:00
|
|
|
if (isReverseDebugging()) {
|
2010-03-11 11:07:16 +01:00
|
|
|
postCommand("reverse-next", RunRequest, CB(handleExecuteNext));
|
2011-06-06 18:17:51 +02:00
|
|
|
} else {
|
2012-01-23 16:45:00 +01:00
|
|
|
scheduleTestResponse(TestNoBoundsOfCurrentFunction,
|
2011-06-06 18:17:51 +02:00
|
|
|
"@TOKEN@^error,msg=\"Warning:\\nCannot insert breakpoint -39.\\n"
|
|
|
|
|
" Error accessing memory address 0x11673fc: Input/output error.\\n\"");
|
2010-03-10 16:17:40 +01:00
|
|
|
postCommand("-exec-next", RunRequest, CB(handleExecuteNext));
|
2011-06-06 18:17:51 +02:00
|
|
|
}
|
2010-02-01 16:14:57 +01:00
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleExecuteNext(const DebuggerResponse &response)
|
2010-02-01 16:14:57 +01:00
|
|
|
{
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultDone) {
|
2010-10-12 11:52:11 +02:00
|
|
|
// 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.
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(InferiorStopOk);
|
2010-10-12 11:52:11 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(InferiorRunRequested);
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultRunning) {
|
2012-08-21 11:30:59 +02:00
|
|
|
// All is fine. Waiting for a *running.
|
|
|
|
|
notifyInferiorRunOk(); // Only needed for gdb < 7.0.
|
2010-07-09 17:07:59 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(InferiorStopOk);
|
2013-05-03 18:26:10 +02:00
|
|
|
QByteArray msg = response.data["msg"].data();
|
2011-06-21 16:45:23 +02:00
|
|
|
if (msg.startsWith("Cannot find bounds of current function")
|
2011-06-06 18:17:51 +02:00
|
|
|
|| msg.contains("Error accessing memory address ")) {
|
2010-07-09 17:07:59 +02:00
|
|
|
notifyInferiorRunFailed();
|
|
|
|
|
if (!isDying())
|
2010-03-10 16:17:40 +01:00
|
|
|
executeNextI(); // Fall back to instruction-wise stepping.
|
2012-03-05 19:22:07 +01:00
|
|
|
} else if (msg.startsWith("Cannot execute this command while the selected thread is running.")) {
|
|
|
|
|
showExecutionError(QString::fromLocal8Bit(msg));
|
|
|
|
|
notifyInferiorRunFailed();
|
2010-07-09 17:07:59 +02:00
|
|
|
} else {
|
2015-02-03 23:58:49 +02:00
|
|
|
AsynchronousMessageBox::critical(tr("Execution Error"),
|
2013-10-17 13:48:04 +02:00
|
|
|
tr("Cannot continue debugged process:") + QLatin1Char('\n') + QString::fromLocal8Bit(msg));
|
2010-07-09 17:07:59 +02:00
|
|
|
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
|
|
|
{
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(InferiorStopOk);
|
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
|
|
|
{
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(InferiorStopOk);
|
2008-12-02 12:01:29 +01:00
|
|
|
setTokenBarrier();
|
2011-05-03 18:14:18 +02:00
|
|
|
resetLocation();
|
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
|
|
|
QByteArray loc;
|
2013-11-27 16:10:14 +01:00
|
|
|
if (data.address)
|
|
|
|
|
loc = addressSpec(data.address);
|
|
|
|
|
else
|
|
|
|
|
loc = '"' + breakLocation(data.fileName).toLocal8Bit() + '"' + ':'
|
|
|
|
|
+ QByteArray::number(data.lineNumber);
|
|
|
|
|
postCommand("tbreak " + loc);
|
|
|
|
|
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
|
|
|
{
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(InferiorStopOk);
|
2008-12-02 12:01:29 +01:00
|
|
|
setTokenBarrier();
|
2011-05-03 18:14:18 +02:00
|
|
|
resetLocation();
|
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
|
|
|
{
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(InferiorStopOk);
|
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
|
|
|
{
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(InferiorStopOk);
|
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
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleExecuteReturn(const DebuggerResponse &response)
|
2010-02-15 16:02:41 +01:00
|
|
|
{
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultDone) {
|
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
|
|
|
/*!
|
2013-09-06 17:16:18 +02:00
|
|
|
Discards the results of all pending watch-updating commands.
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2013-10-07 13:34:40 +02:00
|
|
|
This function is called at the beginning of all step, next, finish, and so on,
|
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()
|
|
|
|
|
{
|
2012-03-01 11:23:47 +01:00
|
|
|
//QTC_ASSERT(m_nonDiscardableCount == 0, /**/);
|
2011-12-13 15:40:31 +01:00
|
|
|
bool good = true;
|
2015-02-06 10:30:25 +01:00
|
|
|
QHashIterator<int, DebuggerCommand> it(m_commandForToken);
|
2011-12-14 15:32:48 +01:00
|
|
|
while (it.hasNext()) {
|
|
|
|
|
it.next();
|
|
|
|
|
if (!(it.value().flags & Discardable)) {
|
2015-02-06 10:30:25 +01:00
|
|
|
qDebug() << "TOKEN: " << it.key() << "CMD:" << it.value().function
|
2015-02-05 22:46:09 +01:00
|
|
|
<< " FLAGS:" << it.value().flags;
|
2011-12-13 15:40:31 +01:00
|
|
|
good = false;
|
|
|
|
|
}
|
2009-04-14 12:14:01 +02:00
|
|
|
}
|
2011-12-13 15:40:31 +01:00
|
|
|
QTC_ASSERT(good, 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);
|
2014-07-28 14:23:52 +02:00
|
|
|
if (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
|
|
|
|
|
//
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2011-06-15 14:02:26 +02:00
|
|
|
void GdbEngine::updateResponse(BreakpointResponse &response, 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
|
|
|
|
2011-06-24 14:17:16 +02:00
|
|
|
QByteArray originalLocation;
|
|
|
|
|
|
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")) {
|
2011-06-24 16:25:30 +02:00
|
|
|
response.id = BreakpointResponseId(child.data());
|
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-21 15:04:31 +01:00
|
|
|
// <MULTIPLE> happens in constructors, inline functions, and
|
2011-03-02 13:37:29 +01:00
|
|
|
// 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>");
|
2011-05-11 15:51:35 +02:00
|
|
|
if (child.data() == "<MULTIPLE>")
|
|
|
|
|
response.multiple = true;
|
2011-06-24 16:25:30 +02:00
|
|
|
if (child.data().startsWith("0x"))
|
2011-05-10 18:58:06 +02:00
|
|
|
response.address = child.toAddress();
|
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)
|
2013-05-07 12:09:54 +02:00
|
|
|
response.lineNumber = child.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");
|
2014-11-27 14:43:45 +01:00
|
|
|
} else if (child.hasName("disp")) {
|
|
|
|
|
response.oneShot = child.data() == "del";
|
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).
|
2013-04-10 09:27:07 +02:00
|
|
|
if (file.isEmpty())
|
|
|
|
|
file = child.data();
|
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")) {
|
2013-05-07 12:09:54 +02:00
|
|
|
response.threadSpec = child.toInt();
|
2010-05-10 12:34:52 +02:00
|
|
|
} else if (child.hasName("type")) {
|
2012-03-19 14:12:29 +01:00
|
|
|
// "breakpoint", "hw breakpoint", "tracepoint", "hw watchpoint"
|
|
|
|
|
// {bkpt={number="2",type="hw watchpoint",disp="keep",enabled="y",
|
2013-11-27 16:10:14 +01:00
|
|
|
// what="*0xbfffed48",times="0",original-location="*0xbfffed48"}}
|
2012-03-19 14:12:29 +01:00
|
|
|
if (child.data().contains("tracepoint")) {
|
2010-12-16 17:58:43 +01:00
|
|
|
response.tracepoint = true;
|
2012-03-19 14:12:29 +01:00
|
|
|
} else if (child.data() == "hw watchpoint" || child.data() == "watchpoint") {
|
2013-05-03 18:26:10 +02:00
|
|
|
QByteArray what = bkpt["what"].data();
|
2012-03-19 14:12:29 +01:00
|
|
|
if (what.startsWith("*0x")) {
|
|
|
|
|
response.type = WatchpointAtAddress;
|
|
|
|
|
response.address = what.mid(1).toULongLong(0, 0);
|
|
|
|
|
} else {
|
|
|
|
|
response.type = WatchpointAtExpression;
|
|
|
|
|
response.expression = QString::fromLocal8Bit(what);
|
|
|
|
|
}
|
2013-09-19 14:15:58 +02:00
|
|
|
} else if (child.data() == "breakpoint") {
|
|
|
|
|
QByteArray catchType = bkpt["catch-type"].data();
|
|
|
|
|
if (catchType == "throw")
|
|
|
|
|
response.type = BreakpointAtThrow;
|
|
|
|
|
else if (catchType == "catch")
|
|
|
|
|
response.type = BreakpointAtCatch;
|
|
|
|
|
else if (catchType == "fork")
|
|
|
|
|
response.type = BreakpointAtFork;
|
|
|
|
|
else if (catchType == "exec")
|
|
|
|
|
response.type = BreakpointAtExec;
|
|
|
|
|
else if (catchType == "syscall")
|
|
|
|
|
response.type = BreakpointAtSysCall;
|
2012-03-19 14:12:29 +01:00
|
|
|
}
|
2011-06-24 14:17:16 +02:00
|
|
|
} else if (child.hasName("original-location")) {
|
|
|
|
|
originalLocation = child.data();
|
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;
|
2011-06-24 14:17:16 +02:00
|
|
|
|
2011-06-24 16:25:30 +02:00
|
|
|
if (response.fileName.isEmpty())
|
2011-07-04 14:24:42 +02:00
|
|
|
response.updateLocation(originalLocation);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-10-22 22:54:30 +02:00
|
|
|
QString GdbEngine::breakLocation(const QString &file) const
|
|
|
|
|
{
|
|
|
|
|
QString where = m_fullToShortName.value(file);
|
|
|
|
|
if (where.isEmpty())
|
2015-01-10 23:40:32 +02:00
|
|
|
return FileName::fromString(file).fileName();
|
2009-10-22 22:54:30 +02:00
|
|
|
return where;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2015-01-10 01:07:01 +01:00
|
|
|
QByteArray GdbEngine::breakpointLocation(const BreakpointParameters &data)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2013-04-09 10:59:36 +02:00
|
|
|
QTC_ASSERT(data.type != UnknownBreakpointType, 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";
|
2011-03-24 14:21:52 +01:00
|
|
|
if (data.type == BreakpointAtMain) {
|
2015-05-27 13:59:56 +02:00
|
|
|
const Abi abi = runParameters().toolChainAbi;
|
2011-03-24 14:21:52 +01:00
|
|
|
return (abi.os() == Abi::WindowsOS) ? "qMain" : "main";
|
|
|
|
|
}
|
2010-11-25 14:33:13 +01:00
|
|
|
if (data.type == BreakpointByFunction)
|
2012-04-17 10:00:40 +02:00
|
|
|
return '"' + data.functionName.toUtf8() + '"';
|
2010-11-25 14:33:13 +01:00
|
|
|
if (data.type == BreakpointByAddress)
|
|
|
|
|
return addressSpec(data.address);
|
|
|
|
|
|
2013-04-10 09:27:07 +02:00
|
|
|
BreakpointPathUsage usage = data.pathUsage;
|
|
|
|
|
if (usage == BreakpointPathUsageEngineDefault)
|
2013-05-24 12:40:06 +02:00
|
|
|
usage = BreakpointUseShortPath;
|
2013-04-10 09:27:07 +02:00
|
|
|
|
|
|
|
|
const QString fileName = usage == 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.
|
2012-08-24 10:25:32 +02: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
|
|
|
}
|
|
|
|
|
|
2015-01-10 01:07:01 +01:00
|
|
|
QByteArray GdbEngine::breakpointLocation2(const BreakpointParameters &data)
|
2011-03-04 18:08:56 +01:00
|
|
|
{
|
2013-04-10 09:27:07 +02:00
|
|
|
BreakpointPathUsage usage = data.pathUsage;
|
|
|
|
|
if (usage == BreakpointPathUsageEngineDefault)
|
2013-05-24 12:40:06 +02:00
|
|
|
usage = BreakpointUseShortPath;
|
2013-04-10 09:27:07 +02:00
|
|
|
|
|
|
|
|
const QString fileName = usage == BreakpointUseFullPath
|
2011-03-04 18:08:56 +01:00
|
|
|
? data.fileName : breakLocation(data.fileName);
|
2012-08-24 10:25:32 +02:00
|
|
|
return GdbMi::escapeCString(fileName.toLocal8Bit()) + ':'
|
2011-03-04 18:08:56 +01:00
|
|
|
+ QByteArray::number(data.lineNumber);
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-06 01:26:47 +01:00
|
|
|
void GdbEngine::handleWatchInsert(const DebuggerResponse &response, Breakpoint bp)
|
2010-05-07 15:16:31 +02:00
|
|
|
{
|
2015-02-05 15:47:07 +01:00
|
|
|
if (bp && response.resultClass == ResultDone) {
|
2015-01-10 01:07:01 +01:00
|
|
|
BreakpointResponse br = bp.response();
|
2010-05-07 15:16:31 +02:00
|
|
|
// "Hardware watchpoint 2: *0xbfffed40\n"
|
2011-07-05 17:57:57 +02:00
|
|
|
QByteArray ba = response.consoleStreamOutput;
|
2013-05-03 18:26:10 +02:00
|
|
|
GdbMi wpt = response.data["wpt"];
|
2011-05-06 14:10:44 +02:00
|
|
|
if (wpt.isValid()) {
|
|
|
|
|
// Mac yields:
|
|
|
|
|
//>32^done,wpt={number="4",exp="*4355182176"}
|
2013-05-03 18:26:10 +02:00
|
|
|
br.id = BreakpointResponseId(wpt["number"].data());
|
|
|
|
|
QByteArray exp = wpt["exp"].data();
|
2011-05-11 15:24:50 +02:00
|
|
|
if (exp.startsWith('*'))
|
2011-06-15 14:02:26 +02:00
|
|
|
br.address = exp.mid(1).toULongLong(0, 0);
|
2015-01-10 01:07:01 +01:00
|
|
|
bp.setResponse(br);
|
|
|
|
|
QTC_CHECK(!bp.needsChange());
|
|
|
|
|
bp.notifyBreakpointInsertOk();
|
2011-05-06 14:10:44 +02:00
|
|
|
} else if (ba.startsWith("Hardware watchpoint ")
|
2010-11-26 12:30:05 +01:00
|
|
|
|| ba.startsWith("Watchpoint ")) {
|
2011-05-06 14:10:44 +02:00
|
|
|
// Non-Mac: "Hardware watchpoint 2: *0xbfffed40\n"
|
2010-11-26 12:30:05 +01:00
|
|
|
const int end = ba.indexOf(':');
|
|
|
|
|
const int begin = ba.lastIndexOf(' ', end) + 1;
|
2011-05-11 15:24:50 +02:00
|
|
|
const QByteArray address = ba.mid(end + 2).trimmed();
|
2011-06-24 16:25:30 +02:00
|
|
|
br.id = BreakpointResponseId(ba.mid(begin, end - begin));
|
2011-05-11 15:24:50 +02:00
|
|
|
if (address.startsWith('*'))
|
2011-06-15 14:02:26 +02:00
|
|
|
br.address = address.mid(1).toULongLong(0, 0);
|
2015-01-10 01:07:01 +01:00
|
|
|
bp.setResponse(br);
|
|
|
|
|
QTC_CHECK(!bp.needsChange());
|
|
|
|
|
bp.notifyBreakpointInsertOk();
|
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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-06 01:26:47 +01:00
|
|
|
void GdbEngine::handleCatchInsert(const DebuggerResponse &response, Breakpoint bp)
|
2011-03-04 19:26:11 +01:00
|
|
|
{
|
2015-02-05 15:47:07 +01:00
|
|
|
if (bp && response.resultClass == ResultDone)
|
2015-01-10 01:07:01 +01:00
|
|
|
bp.notifyBreakpointInsertOk();
|
2011-03-04 19:26:11 +01:00
|
|
|
}
|
|
|
|
|
|
2015-01-10 01:07:01 +01:00
|
|
|
void GdbEngine::handleBkpt(const GdbMi &bkpt, Breakpoint bp)
|
2013-03-07 23:11:42 +01:00
|
|
|
{
|
2015-01-10 01:07:01 +01:00
|
|
|
BreakpointResponse br = bp.response();
|
|
|
|
|
QTC_ASSERT(bp, return);
|
2013-05-03 18:26:10 +02:00
|
|
|
const QByteArray nr = bkpt["number"].data();
|
2013-03-07 23:11:42 +01:00
|
|
|
const BreakpointResponseId rid(nr);
|
|
|
|
|
QTC_ASSERT(rid.isValid(), return);
|
|
|
|
|
if (nr.contains('.')) {
|
|
|
|
|
// A sub-breakpoint.
|
|
|
|
|
BreakpointResponse sub;
|
|
|
|
|
updateResponse(sub, bkpt);
|
|
|
|
|
sub.id = rid;
|
2015-01-10 01:07:01 +01:00
|
|
|
sub.type = bp.type();
|
|
|
|
|
bp.insertSubBreakpoint(sub);
|
2013-03-07 23:11:42 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The MI output format might change, see
|
|
|
|
|
// http://permalink.gmane.org/gmane.comp.gdb.patches/83936
|
2013-05-03 18:26:10 +02:00
|
|
|
const GdbMi locations = bkpt["locations"];
|
2013-03-07 23:11:42 +01:00
|
|
|
if (locations.isValid()) {
|
|
|
|
|
foreach (const GdbMi &loc, locations.children()) {
|
|
|
|
|
// A sub-breakpoint.
|
2013-05-03 18:26:10 +02:00
|
|
|
const QByteArray subnr = loc["number"].data();
|
2013-03-07 23:11:42 +01:00
|
|
|
const BreakpointResponseId subrid(subnr);
|
|
|
|
|
BreakpointResponse sub;
|
|
|
|
|
updateResponse(sub, loc);
|
|
|
|
|
sub.id = subrid;
|
|
|
|
|
sub.type = br.type;
|
2015-01-10 01:07:01 +01:00
|
|
|
bp.insertSubBreakpoint(sub);
|
2013-03-07 23:11:42 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A (the?) primary breakpoint.
|
|
|
|
|
updateResponse(br, bkpt);
|
|
|
|
|
br.id = rid;
|
2015-01-10 01:07:01 +01:00
|
|
|
bp.setResponse(br);
|
2013-03-07 23:11:42 +01:00
|
|
|
}
|
|
|
|
|
|
2015-02-06 01:26:47 +01:00
|
|
|
void GdbEngine::handleBreakInsert1(const DebuggerResponse &response, Breakpoint bp)
|
2010-03-04 15:42:09 +01:00
|
|
|
{
|
2015-01-10 01:07:01 +01:00
|
|
|
if (bp.state() == BreakpointRemoveRequested) {
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultDone) {
|
2014-03-04 00:12:11 +01:00
|
|
|
// This delete was deferred. Act now.
|
2013-05-03 18:26:10 +02:00
|
|
|
const GdbMi mainbkpt = response.data["bkpt"];
|
2015-01-10 01:07:01 +01:00
|
|
|
bp.notifyBreakpointRemoveProceeding();
|
2013-05-03 18:26:10 +02:00
|
|
|
QByteArray nr = mainbkpt["number"].data();
|
2011-10-28 12:26:00 +02:00
|
|
|
postCommand("-break-delete " + nr,
|
|
|
|
|
NeedsStop | RebuildBreakpointModel);
|
2015-01-10 01:07:01 +01:00
|
|
|
bp.notifyBreakpointRemoveOk();
|
2011-10-28 12:26:00 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultDone) {
|
2011-07-04 13:37:01 +02:00
|
|
|
// The result is a list with the first entry marked "bkpt"
|
|
|
|
|
// and "unmarked" rest. The "bkpt" one seems to always be
|
|
|
|
|
// the "main" entry. Use the "main" entry to retrieve the
|
|
|
|
|
// already known data from the BreakpointManager, and then
|
|
|
|
|
// iterate over all items to update main- and sub-data.
|
2013-05-03 18:26:10 +02:00
|
|
|
const GdbMi mainbkpt = response.data["bkpt"];
|
|
|
|
|
const QByteArray mainnr = mainbkpt["number"].data();
|
2013-03-07 23:11:42 +01:00
|
|
|
const BreakpointResponseId mainrid(mainnr);
|
|
|
|
|
if (!isHiddenBreakpoint(mainrid)) {
|
2013-03-12 10:56:52 +01:00
|
|
|
foreach (const GdbMi &bkpt, response.data.children())
|
2015-01-10 01:07:01 +01:00
|
|
|
handleBkpt(bkpt, bp);
|
|
|
|
|
if (bp.needsChange()) {
|
|
|
|
|
bp.notifyBreakpointChangeAfterInsertNeeded();
|
|
|
|
|
changeBreakpoint(bp);
|
2011-06-24 19:02:12 +02:00
|
|
|
} else {
|
2015-01-10 01:07:01 +01:00
|
|
|
bp.notifyBreakpointInsertOk();
|
2011-06-24 19:02:12 +02:00
|
|
|
}
|
2010-11-30 12:47:53 +01:00
|
|
|
}
|
2013-05-03 18:26:10 +02:00
|
|
|
} else if (response.data["msg"].data().contains("Unknown option")) {
|
2010-12-16 17:58:43 +01:00
|
|
|
// Older version of gdb don't know the -a option to set tracepoints
|
|
|
|
|
// ^error,msg="mi_cmd_break_insert: Unknown option ``a''"
|
2015-01-10 01:07:01 +01:00
|
|
|
const QString fileName = bp.fileName();
|
|
|
|
|
const int lineNumber = bp.lineNumber();
|
2010-12-16 17:58:43 +01:00
|
|
|
QByteArray cmd = "trace "
|
2012-08-24 10:25:32 +02:00
|
|
|
"\"" + GdbMi::escapeCString(fileName.toLocal8Bit()) + "\":"
|
2010-12-16 17:58:43 +01:00
|
|
|
+ QByteArray::number(lineNumber);
|
2013-11-27 16:10:14 +01:00
|
|
|
postCommand(cmd, NeedsStop | RebuildBreakpointModel);
|
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.
|
2015-01-10 01:07:01 +01:00
|
|
|
QByteArray cmd = "break " + breakpointLocation2(bp.parameters());
|
2010-03-04 15:42:09 +01:00
|
|
|
postCommand(cmd, NeedsStop | RebuildBreakpointModel,
|
2015-02-06 01:26:47 +01:00
|
|
|
[this, bp](const DebuggerResponse &r) { handleBreakInsert2(r, bp); });
|
2010-03-04 15:42:09 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-06 01:26:47 +01:00
|
|
|
void GdbEngine::handleBreakInsert2(const DebuggerResponse &response, Breakpoint bp)
|
2010-03-04 15:42:09 +01:00
|
|
|
{
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultDone) {
|
2015-01-10 01:07:01 +01:00
|
|
|
QTC_ASSERT(bp, return);
|
|
|
|
|
bp.notifyBreakpointInsertOk();
|
2011-03-04 18:08:56 +01:00
|
|
|
} else {
|
|
|
|
|
// Note: gdb < 60800 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.
|
2010-03-04 15:42:09 +01:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2015-02-06 01:26:47 +01:00
|
|
|
void GdbEngine::handleBreakDisable(const DebuggerResponse &response, Breakpoint bp)
|
2010-03-25 13:33:38 +01:00
|
|
|
{
|
2015-02-05 15:47:07 +01:00
|
|
|
QTC_CHECK(response.resultClass == ResultDone);
|
2010-11-15 16:22:51 +01:00
|
|
|
// This should only be the requested state.
|
2015-01-10 01:07:01 +01:00
|
|
|
QTC_ASSERT(!bp.isEnabled(), /* Prevent later recursion */);
|
|
|
|
|
BreakpointResponse br = bp.response();
|
2010-11-18 16:07:42 +01:00
|
|
|
br.enabled = false;
|
2015-01-10 01:07:01 +01:00
|
|
|
bp.setResponse(br);
|
|
|
|
|
changeBreakpoint(bp); // Maybe there's more to do.
|
2010-11-15 16:22:51 +01:00
|
|
|
}
|
|
|
|
|
|
2015-02-06 01:26:47 +01:00
|
|
|
void GdbEngine::handleBreakEnable(const DebuggerResponse &response, Breakpoint bp)
|
2010-11-15 16:22:51 +01:00
|
|
|
{
|
2015-02-05 15:47:07 +01:00
|
|
|
QTC_CHECK(response.resultClass == ResultDone);
|
2010-11-15 16:58:23 +01:00
|
|
|
// This should only be the requested state.
|
2015-01-10 01:07:01 +01:00
|
|
|
QTC_ASSERT(bp.isEnabled(), /* Prevent later recursion */);
|
|
|
|
|
BreakpointResponse br = bp.response();
|
2010-11-18 16:07:42 +01:00
|
|
|
br.enabled = true;
|
2015-01-10 01:07:01 +01:00
|
|
|
bp.setResponse(br);
|
|
|
|
|
changeBreakpoint(bp); // Maybe there's more to do.
|
2010-11-30 12:47:53 +01:00
|
|
|
}
|
|
|
|
|
|
2015-02-06 01:26:47 +01:00
|
|
|
void GdbEngine::handleBreakThreadSpec(const DebuggerResponse &response, Breakpoint bp)
|
2010-11-30 12:47:53 +01:00
|
|
|
{
|
2015-02-05 15:47:07 +01:00
|
|
|
QTC_CHECK(response.resultClass == ResultDone);
|
2015-01-10 01:07:01 +01:00
|
|
|
BreakpointResponse br = bp.response();
|
|
|
|
|
br.threadSpec = bp.threadSpec();
|
|
|
|
|
bp.setResponse(br);
|
|
|
|
|
bp.notifyBreakpointNeedsReinsertion();
|
|
|
|
|
insertBreakpoint(bp);
|
2010-03-25 13:33:38 +01:00
|
|
|
}
|
|
|
|
|
|
2015-02-06 01:26:47 +01:00
|
|
|
void GdbEngine::handleBreakLineNumber(const DebuggerResponse &response, Breakpoint bp)
|
2011-11-04 14:03:48 +01:00
|
|
|
{
|
2015-02-05 15:47:07 +01:00
|
|
|
QTC_CHECK(response.resultClass == ResultDone);
|
2015-01-10 01:07:01 +01:00
|
|
|
BreakpointResponse br = bp.response();
|
|
|
|
|
br.lineNumber = bp.lineNumber();
|
|
|
|
|
bp.setResponse(br);
|
|
|
|
|
bp.notifyBreakpointNeedsReinsertion();
|
|
|
|
|
insertBreakpoint(bp);
|
2011-11-04 14:03:48 +01:00
|
|
|
}
|
|
|
|
|
|
2015-02-06 01:26:47 +01:00
|
|
|
void GdbEngine::handleBreakIgnore(const DebuggerResponse &response, Breakpoint bp)
|
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
|
2015-02-05 15:47:07 +01:00
|
|
|
QTC_CHECK(response.resultClass == ResultDone);
|
2011-07-05 17:57:57 +02:00
|
|
|
//QString msg = _(response.consoleStreamOutput);
|
2015-01-10 01:07:01 +01:00
|
|
|
BreakpointResponse br = bp.response();
|
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...
|
2015-01-10 01:07:01 +01:00
|
|
|
const BreakpointParameters ¶meters = bp.parameters();
|
2011-02-04 11:33:45 +01:00
|
|
|
br.ignoreCount = parameters.ignoreCount;
|
|
|
|
|
br.command = parameters.command;
|
2015-01-10 01:07:01 +01:00
|
|
|
bp.setResponse(br);
|
|
|
|
|
changeBreakpoint(bp); // Maybe there's more to do.
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2015-02-06 01:26:47 +01:00
|
|
|
void GdbEngine::handleBreakCondition(const DebuggerResponse &, Breakpoint bp)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-11-30 12:47:53 +01:00
|
|
|
// Can happen at invalid condition strings.
|
2015-02-05 15:47:07 +01:00
|
|
|
//QTC_CHECK(response.resultClass == ResultDone)
|
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();
|
2013-11-27 16:10:14 +01:00
|
|
|
// if (msg.startsWith("Error parsing breakpoint condition. "
|
|
|
|
|
// " Will try again when we hit the breakpoint."))
|
2015-01-10 01:07:01 +01:00
|
|
|
BreakpointResponse br = bp.response();
|
|
|
|
|
br.condition = bp.condition();
|
|
|
|
|
bp.setResponse(br);
|
|
|
|
|
changeBreakpoint(bp); // Maybe there's more to do.
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-10 01:07:01 +01:00
|
|
|
bool GdbEngine::acceptsBreakpoint(Breakpoint bp) const
|
2010-10-05 11:01:14 +02:00
|
|
|
{
|
2015-05-27 13:59:56 +02:00
|
|
|
if (runParameters().startMode == AttachCore)
|
2014-12-12 08:26:55 +01:00
|
|
|
return false;
|
|
|
|
|
// We handle QML breakpoint unless specifically
|
2015-05-27 13:59:56 +02:00
|
|
|
if (isNativeMixedEnabled() && !(runParameters().languages & QmlLanguage))
|
2014-12-12 08:26:55 +01:00
|
|
|
return true;
|
2015-01-10 01:07:01 +01:00
|
|
|
return bp.parameters().isCppBreakpoint();
|
2010-11-10 16:33:11 +01:00
|
|
|
}
|
|
|
|
|
|
2015-01-10 01:07:01 +01:00
|
|
|
void GdbEngine::insertBreakpoint(Breakpoint bp)
|
2010-11-10 16:33:11 +01:00
|
|
|
{
|
|
|
|
|
// Set up fallback in case of pending breakpoints which aren't handled
|
|
|
|
|
// by the MI interface.
|
2015-01-10 01:07:01 +01:00
|
|
|
QTC_CHECK(bp.state() == BreakpointInsertRequested);
|
|
|
|
|
bp.notifyBreakpointInsertProceeding();
|
|
|
|
|
|
|
|
|
|
const BreakpointParameters &data = bp.parameters();
|
2014-12-12 08:26:55 +01:00
|
|
|
|
2015-01-10 01:07:01 +01:00
|
|
|
if (!data.isCppBreakpoint()) {
|
2015-02-04 10:48:33 +01:00
|
|
|
DebuggerCommand cmd("insertQmlBreakpoint");
|
2015-02-04 16:27:46 +01:00
|
|
|
bp.addToCommand(&cmd);
|
2015-02-04 10:48:33 +01:00
|
|
|
runCommand(cmd);
|
2015-01-10 01:07:01 +01:00
|
|
|
bp.notifyBreakpointInsertOk();
|
2014-12-12 08:26:55 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-10 01:07:01 +01:00
|
|
|
BreakpointType type = bp.type();
|
2011-05-09 08:35:58 +02:00
|
|
|
if (type == WatchpointAtAddress) {
|
2015-01-10 01:07:01 +01:00
|
|
|
postCommand("watch " + addressSpec(bp.address()),
|
2012-04-26 15:24:47 +02:00
|
|
|
NeedsStop | RebuildBreakpointModel | ConsoleCommand,
|
2015-02-06 01:26:47 +01:00
|
|
|
[this, bp](const DebuggerResponse &r) { handleWatchInsert(r, bp); });
|
2010-11-10 16:33:11 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2011-05-09 08:35:58 +02:00
|
|
|
if (type == WatchpointAtExpression) {
|
2015-01-10 01:07:01 +01:00
|
|
|
postCommand("watch " + bp.expression().toLocal8Bit(),
|
2012-04-26 15:24:47 +02:00
|
|
|
NeedsStop | RebuildBreakpointModel | ConsoleCommand,
|
2015-02-06 01:26:47 +01:00
|
|
|
[this, bp](const DebuggerResponse &r) { handleWatchInsert(r, bp); });
|
2011-05-09 08:35:58 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2011-03-04 19:26:11 +01:00
|
|
|
if (type == BreakpointAtFork) {
|
2012-04-26 15:24:47 +02:00
|
|
|
postCommand("catch fork",
|
|
|
|
|
NeedsStop | RebuildBreakpointModel | ConsoleCommand,
|
2015-02-06 01:26:47 +01:00
|
|
|
[this, bp](const DebuggerResponse &r) { handleCatchInsert(r, bp); });
|
2012-04-26 15:24:47 +02:00
|
|
|
postCommand("catch vfork",
|
|
|
|
|
NeedsStop | RebuildBreakpointModel | ConsoleCommand,
|
2015-02-06 01:26:47 +01:00
|
|
|
[this, bp](const DebuggerResponse &r) { handleCatchInsert(r, bp); });
|
2011-03-04 19:26:11 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2011-03-29 09:58:21 +02:00
|
|
|
//if (type == BreakpointAtVFork) {
|
|
|
|
|
// postCommand("catch vfork", NeedsStop | RebuildBreakpointModel,
|
2011-06-15 14:02:26 +02:00
|
|
|
// CB(handleCatchInsert), vid);
|
2011-03-29 09:58:21 +02:00
|
|
|
// return;
|
|
|
|
|
//}
|
2011-03-04 19:26:11 +01:00
|
|
|
if (type == BreakpointAtExec) {
|
2012-04-26 15:24:47 +02:00
|
|
|
postCommand("catch exec",
|
|
|
|
|
NeedsStop | RebuildBreakpointModel | ConsoleCommand,
|
2015-02-06 01:26:47 +01:00
|
|
|
[this, bp](const DebuggerResponse &r) { handleCatchInsert(r, bp); });
|
2011-03-04 19:26:11 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (type == BreakpointAtSysCall) {
|
2012-04-26 15:24:47 +02:00
|
|
|
postCommand("catch syscall",
|
|
|
|
|
NeedsStop | RebuildBreakpointModel | ConsoleCommand,
|
2015-02-06 01:26:47 +01:00
|
|
|
[this, bp](const DebuggerResponse &r) { handleCatchInsert(r, bp); });
|
2011-03-04 19:26:11 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2010-11-10 16:33:11 +01:00
|
|
|
|
2013-02-14 15:30:31 +01:00
|
|
|
QByteArray cmd;
|
2015-01-10 01:07:01 +01:00
|
|
|
if (bp.isTracepoint()) {
|
2010-12-16 17:58:43 +01:00
|
|
|
cmd = "-break-insert -a -f ";
|
2013-11-27 16:10:14 +01:00
|
|
|
} else {
|
2015-01-10 01:07:01 +01:00
|
|
|
int spec = bp.threadSpec();
|
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 ";
|
|
|
|
|
}
|
2012-08-21 14:16:07 +02:00
|
|
|
|
2015-01-10 01:07:01 +01:00
|
|
|
if (bp.isOneShot())
|
2012-08-21 14:16:07 +02:00
|
|
|
cmd += "-t ";
|
|
|
|
|
|
2015-01-10 01:07:01 +01:00
|
|
|
if (!bp.isEnabled())
|
2013-02-26 13:54:45 +01:00
|
|
|
cmd += "-d ";
|
|
|
|
|
|
2015-01-10 01:07:01 +01:00
|
|
|
if (int ignoreCount = bp.ignoreCount())
|
2013-02-26 11:59:40 +01:00
|
|
|
cmd += "-i " + QByteArray::number(ignoreCount) + ' ';
|
|
|
|
|
|
2015-01-10 01:07:01 +01:00
|
|
|
QByteArray condition = bp.condition();
|
2013-02-14 15:30:31 +01:00
|
|
|
if (!condition.isEmpty())
|
|
|
|
|
cmd += " -c \"" + condition + "\" ";
|
|
|
|
|
|
2015-01-10 01:07:01 +01:00
|
|
|
cmd += breakpointLocation(bp.parameters());
|
2010-11-10 16:33:11 +01:00
|
|
|
postCommand(cmd, NeedsStop | RebuildBreakpointModel,
|
2015-02-06 01:26:47 +01:00
|
|
|
[this, bp](const DebuggerResponse &r) { handleBreakInsert1(r, bp); });
|
2010-11-10 16:33:11 +01:00
|
|
|
}
|
|
|
|
|
|
2015-01-10 01:07:01 +01:00
|
|
|
void GdbEngine::changeBreakpoint(Breakpoint bp)
|
2010-11-10 16:33:11 +01:00
|
|
|
{
|
2015-01-10 01:07:01 +01:00
|
|
|
const BreakpointParameters &data = bp.parameters();
|
2013-04-09 10:59:36 +02:00
|
|
|
QTC_ASSERT(data.type != UnknownBreakpointType, return);
|
2015-01-10 01:07:01 +01:00
|
|
|
const BreakpointResponse &response = bp.response();
|
2011-06-21 16:45:23 +02:00
|
|
|
QTC_ASSERT(response.id.isValid(), return);
|
|
|
|
|
const QByteArray bpnr = response.id.toByteArray();
|
2015-01-10 01:07:01 +01:00
|
|
|
const BreakpointState state = bp.state();
|
2010-11-30 12:47:53 +01:00
|
|
|
if (state == BreakpointChangeRequested)
|
2015-01-10 01:07:01 +01:00
|
|
|
bp.notifyBreakpointChangeProceeding();
|
|
|
|
|
const BreakpointState state2 = bp.state();
|
2010-11-30 12:47:53 +01:00
|
|
|
QTC_ASSERT(state2 == BreakpointChangeProceeding, qDebug() << state2);
|
|
|
|
|
|
2014-05-16 01:46:05 +02:00
|
|
|
if (!response.pending && data.threadSpec != response.threadSpec) {
|
2010-11-30 13:39:01 +01:00
|
|
|
// The only way to change this seems to be to re-set the bp completely.
|
|
|
|
|
postCommand("-break-delete " + bpnr,
|
|
|
|
|
NeedsStop | RebuildBreakpointModel,
|
2015-02-06 01:26:47 +01:00
|
|
|
[this, bp](const DebuggerResponse &r) { handleBreakThreadSpec(r, bp); });
|
2010-11-30 13:39:01 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2014-05-16 01:46:05 +02:00
|
|
|
if (!response.pending && data.lineNumber != response.lineNumber) {
|
2011-11-04 14:03:48 +01:00
|
|
|
// The only way to change this seems to be to re-set the bp completely.
|
|
|
|
|
postCommand("-break-delete " + bpnr,
|
|
|
|
|
NeedsStop | RebuildBreakpointModel,
|
2015-02-06 01:26:47 +01:00
|
|
|
[this, bp](const DebuggerResponse &r) { handleBreakLineNumber(r, bp); });
|
2011-11-04 14:03:48 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2011-02-04 11:33:45 +01:00
|
|
|
if (data.command != response.command) {
|
|
|
|
|
QByteArray breakCommand = "-break-commands " + bpnr;
|
2012-08-20 18:47:51 +02:00
|
|
|
foreach (const QString &command, data.command.split(QLatin1String("\n"))) {
|
2011-02-04 11:33:45 +01:00
|
|
|
if (!command.isEmpty()) {
|
|
|
|
|
breakCommand.append(" \"");
|
|
|
|
|
breakCommand.append(command.toLatin1());
|
|
|
|
|
breakCommand.append('"');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
postCommand(breakCommand, NeedsStop | RebuildBreakpointModel,
|
2015-02-06 01:26:47 +01:00
|
|
|
[this, bp](const DebuggerResponse &r) { handleBreakIgnore(r, bp); });
|
2011-02-04 11:33:45 +01:00
|
|
|
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,
|
2015-02-06 01:26:47 +01:00
|
|
|
[this, bp](const DebuggerResponse &r) { handleBreakCondition(r, bp); });
|
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,
|
2015-02-06 01:26:47 +01:00
|
|
|
[this, bp](const DebuggerResponse &r) { handleBreakIgnore(r, bp); });
|
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,
|
2015-02-06 01:26:47 +01:00
|
|
|
[this, bp](const DebuggerResponse &r) { handleBreakDisable(r, bp); });
|
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,
|
2015-02-06 01:26:47 +01:00
|
|
|
[this, bp](const DebuggerResponse &r) { handleBreakEnable(r, bp); });
|
2010-11-30 12:47:53 +01:00
|
|
|
return;
|
2010-11-15 16:22:51 +01:00
|
|
|
}
|
2015-01-10 01:07:01 +01:00
|
|
|
bp.notifyBreakpointChangeOk();
|
2010-11-10 16:33:11 +01:00
|
|
|
}
|
|
|
|
|
|
2015-01-10 01:07:01 +01:00
|
|
|
void GdbEngine::removeBreakpoint(Breakpoint bp)
|
2010-11-10 16:33:11 +01:00
|
|
|
{
|
2015-01-10 01:07:01 +01:00
|
|
|
QTC_CHECK(bp.state() == BreakpointRemoveRequested);
|
|
|
|
|
BreakpointResponse br = bp.response();
|
2015-01-22 12:05:00 +01:00
|
|
|
|
|
|
|
|
const BreakpointParameters &data = bp.parameters();
|
|
|
|
|
if (!data.isCppBreakpoint()) {
|
2015-02-04 10:48:33 +01:00
|
|
|
DebuggerCommand cmd("removeQmlBreakpoint");
|
2015-02-04 16:27:46 +01:00
|
|
|
bp.addToCommand(&cmd);
|
2015-02-04 10:48:33 +01:00
|
|
|
runCommand(cmd);
|
2015-01-22 12:05:00 +01:00
|
|
|
bp.notifyBreakpointRemoveOk();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-28 12:26:00 +02:00
|
|
|
if (br.id.isValid()) {
|
|
|
|
|
// We already have a fully inserted breakpoint.
|
2015-01-10 01:07:01 +01:00
|
|
|
bp.notifyBreakpointRemoveProceeding();
|
|
|
|
|
showMessage(_("DELETING BP %1 IN %2").arg(br.id.toString()).arg(bp.fileName()));
|
2011-10-28 12:26:00 +02:00
|
|
|
postCommand("-break-delete " + br.id.toByteArray(),
|
2015-02-23 14:30:14 +01:00
|
|
|
NeedsStop | RebuildBreakpointModel);
|
|
|
|
|
|
|
|
|
|
// Pretend it succeeds without waiting for response. Feels better.
|
|
|
|
|
// Otherwise, clicking in the gutter leaves the breakpoint visible
|
|
|
|
|
// for quite some time, so the user assumes a mis-click and clicks
|
|
|
|
|
// again, effectivly re-introducing the breakpoint.
|
|
|
|
|
bp.notifyBreakpointRemoveOk();
|
|
|
|
|
|
2011-10-28 12:26:00 +02:00
|
|
|
} else {
|
|
|
|
|
// Breakpoint was scheduled to be inserted, but we haven't had
|
|
|
|
|
// an answer so far. Postpone activity by doing nothing.
|
|
|
|
|
}
|
2010-10-05 11:01:14 +02:00
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// Modules specific stuff
|
|
|
|
|
//
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2012-03-16 13:34:00 +01:00
|
|
|
void GdbEngine::loadSymbols(const QString &modulePath)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
// FIXME: gdb does not understand quoted names here (tested with 6.8)
|
2012-03-16 13:34:00 +01:00
|
|
|
postCommand("sharedlibrary " + dotEscape(modulePath.toLocal8Bit()));
|
2009-10-28 22:08:58 +01:00
|
|
|
reloadModulesInternal();
|
2015-02-06 01:26:47 +01:00
|
|
|
reloadStack();
|
2010-12-21 12:41:34 +01:00
|
|
|
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();
|
2015-02-06 01:26:47 +01:00
|
|
|
reloadStack();
|
2010-12-21 12:41:34 +01:00
|
|
|
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 "
|
2012-03-16 13:34:00 +01:00
|
|
|
+ dotEscape(module.modulePath.toLocal8Bit()));
|
2010-12-21 13:34:59 +01:00
|
|
|
needUpdate = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (needUpdate) {
|
2012-06-02 03:30:21 +02:00
|
|
|
//reloadModulesInternal();
|
2015-02-06 01:26:47 +01:00
|
|
|
reloadStack();
|
2010-12-21 13:34:59 +01:00
|
|
|
updateLocals();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-06 01:26:47 +01:00
|
|
|
static void handleShowModuleSymbols(const DebuggerResponse &response,
|
|
|
|
|
const QString &modulePath, const QString &fileName)
|
2010-11-26 09:58:34 +01:00
|
|
|
{
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultDone) {
|
2012-11-07 18:31:17 +01:00
|
|
|
Symbols symbols;
|
2010-11-26 09:58:34 +01:00
|
|
|
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);
|
2012-06-13 13:13:26 +02:00
|
|
|
int lenAddress = posName - posAddress;
|
2010-11-26 09:58:34 +01:00
|
|
|
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));
|
2012-11-07 18:31:17 +01:00
|
|
|
symbols.push_back(symbol);
|
2009-04-15 12:01:58 +02:00
|
|
|
}
|
2010-11-26 09:58:34 +01:00
|
|
|
file.close();
|
|
|
|
|
file.remove();
|
2014-10-22 13:04:47 +02:00
|
|
|
Internal::showModuleSymbols(modulePath, symbols);
|
2010-11-26 09:58:34 +01:00
|
|
|
} else {
|
2015-02-06 01:26:47 +01:00
|
|
|
AsynchronousMessageBox::critical(GdbEngine::tr("Cannot Read Symbols"),
|
|
|
|
|
GdbEngine::tr("Cannot read symbols for module \"%1\".").arg(fileName));
|
2010-11-26 09:58:34 +01:00
|
|
|
}
|
2009-04-15 12:01:58 +02:00
|
|
|
}
|
|
|
|
|
|
2015-02-06 01:26:47 +01:00
|
|
|
void GdbEngine::requestModuleSymbols(const QString &modulePath)
|
|
|
|
|
{
|
|
|
|
|
QTemporaryFile tf(QDir::tempPath() + _("/gdbsymbols"));
|
|
|
|
|
if (!tf.open())
|
|
|
|
|
return;
|
|
|
|
|
QString fileName = tf.fileName();
|
|
|
|
|
tf.close();
|
|
|
|
|
postCommand("maint print msymbols \"" + fileName.toLocal8Bit()
|
|
|
|
|
+ "\" " + modulePath.toLocal8Bit(), NeedsStop,
|
|
|
|
|
[modulePath, fileName](const DebuggerResponse &r) {
|
|
|
|
|
handleShowModuleSymbols(r, modulePath, fileName); });
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-07 18:31:17 +01:00
|
|
|
void GdbEngine::requestModuleSections(const QString &moduleName)
|
|
|
|
|
{
|
|
|
|
|
// There seems to be no way to get the symbols from a single .so.
|
2015-02-06 01:26:47 +01:00
|
|
|
postCommand("maint info section ALLOBJ", NeedsStop,
|
|
|
|
|
[this, moduleName](const DebuggerResponse &r) {
|
|
|
|
|
handleShowModuleSections(r, moduleName); });
|
2012-11-07 18:31:17 +01:00
|
|
|
}
|
|
|
|
|
|
2015-02-06 01:26:47 +01:00
|
|
|
void GdbEngine::handleShowModuleSections(const DebuggerResponse &response,
|
|
|
|
|
const QString &moduleName)
|
2012-11-07 18:31:17 +01:00
|
|
|
{
|
|
|
|
|
// ~" Object file: /usr/lib/i386-linux-gnu/libffi.so.6\n"
|
|
|
|
|
// ~" 0xb44a6114->0xb44a6138 at 0x00000114: .note.gnu.build-id ALLOC LOAD READONLY DATA HAS_CONTENTS\n"
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultDone) {
|
2012-11-07 18:31:17 +01:00
|
|
|
const QStringList lines = QString::fromLocal8Bit(response.consoleStreamOutput).split(QLatin1Char('\n'));
|
|
|
|
|
const QString prefix = QLatin1String(" Object file: ");
|
|
|
|
|
const QString needle = prefix + moduleName;
|
|
|
|
|
Sections sections;
|
|
|
|
|
bool active = false;
|
|
|
|
|
foreach (const QString &line, lines) {
|
|
|
|
|
if (line.startsWith(prefix)) {
|
|
|
|
|
if (active)
|
|
|
|
|
break;
|
|
|
|
|
if (line == needle)
|
|
|
|
|
active = true;
|
|
|
|
|
} else {
|
|
|
|
|
if (active) {
|
|
|
|
|
QStringList items = line.split(QLatin1Char(' '), QString::SkipEmptyParts);
|
|
|
|
|
QString fromTo = items.value(0, QString());
|
|
|
|
|
const int pos = fromTo.indexOf(QLatin1Char('-'));
|
|
|
|
|
QTC_ASSERT(pos >= 0, continue);
|
|
|
|
|
Section section;
|
|
|
|
|
section.from = fromTo.left(pos);
|
|
|
|
|
section.to = fromTo.mid(pos + 2);
|
|
|
|
|
section.address = items.value(2, QString());
|
|
|
|
|
section.name = items.value(3, QString());
|
|
|
|
|
section.flags = items.value(4, QString());
|
|
|
|
|
sections.append(section);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!sections.isEmpty())
|
2014-10-22 13:04:47 +02:00
|
|
|
Internal::showModuleSections(moduleName, sections);
|
2012-11-07 18:31:17 +01: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
|
|
|
{
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("info shared", NeedsStop, CB(handleModulesList));
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2012-03-16 13:34:00 +01:00
|
|
|
static QString nameFromPath(const QString &path)
|
|
|
|
|
{
|
|
|
|
|
return QFileInfo(path).baseName();
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleModulesList(const DebuggerResponse &response)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultDone) {
|
2012-06-02 03:30:21 +02:00
|
|
|
ModulesHandler *handler = modulesHandler();
|
|
|
|
|
Module module;
|
2009-10-02 13:24:41 +02:00
|
|
|
// That's console-based output, likely Linux or Windows,
|
2011-03-24 14:21:52 +01:00
|
|
|
// but we can avoid the target dependency here.
|
2011-07-05 17:57:57 +02:00
|
|
|
QString data = QString::fromLocal8Bit(response.consoleStreamOutput);
|
2008-12-02 12:01:29 +01:00
|
|
|
QTextStream ts(&data, QIODevice::ReadOnly);
|
2012-06-02 03:30:21 +02:00
|
|
|
bool found = false;
|
2008-12-02 12:01:29 +01:00
|
|
|
while (!ts.atEnd()) {
|
|
|
|
|
QString line = ts.readLine();
|
|
|
|
|
QString symbolsRead;
|
|
|
|
|
QTextStream ts(&line, QIODevice::ReadOnly);
|
2012-02-16 11:12:02 +01:00
|
|
|
if (line.startsWith(QLatin1String("0x"))) {
|
2009-10-23 15:46:39 +02:00
|
|
|
ts >> module.startAddress >> module.endAddress >> symbolsRead;
|
2012-03-16 13:34:00 +01:00
|
|
|
module.modulePath = ts.readLine().trimmed();
|
|
|
|
|
module.moduleName = nameFromPath(module.modulePath);
|
2010-07-22 16:15:50 +02:00
|
|
|
module.symbolsRead =
|
2012-02-16 11:12:02 +01:00
|
|
|
(symbolsRead == QLatin1String("Yes") ? Module::ReadOk : Module::ReadFailed);
|
2012-06-02 03:30:21 +02:00
|
|
|
handler->updateModule(module);
|
|
|
|
|
found = true;
|
2012-02-16 11:12:02 +01:00
|
|
|
} else if (line.trimmed().startsWith(QLatin1String("No"))) {
|
2009-10-23 15:46:39 +02:00
|
|
|
// gdb 6.4 symbianelf
|
|
|
|
|
ts >> symbolsRead;
|
2012-02-16 11:12:02 +01:00
|
|
|
QTC_ASSERT(symbolsRead == QLatin1String("No"), continue);
|
2011-01-06 09:34:45 +01:00
|
|
|
module.startAddress = 0;
|
|
|
|
|
module.endAddress = 0;
|
2012-03-16 13:34:00 +01:00
|
|
|
module.modulePath = ts.readLine().trimmed();
|
|
|
|
|
module.moduleName = nameFromPath(module.modulePath);
|
2012-06-02 03:30:21 +02:00
|
|
|
handler->updateModule(module);
|
|
|
|
|
found = true;
|
2009-10-23 15:46:39 +02:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2012-06-02 03:30:21 +02:00
|
|
|
if (!found) {
|
2009-03-12 12:00:53 +01:00
|
|
|
// 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()) {
|
2012-03-16 13:34:00 +01:00
|
|
|
module.modulePath =
|
2013-05-03 18:26:10 +02:00
|
|
|
QString::fromLocal8Bit(item["path"].data());
|
2012-03-16 13:34:00 +01:00
|
|
|
module.moduleName = nameFromPath(module.modulePath);
|
2013-05-03 18:26:10 +02:00
|
|
|
module.symbolsRead = (item["state"].data() == "Y")
|
2010-07-22 16:15:50 +02:00
|
|
|
? Module::ReadOk : Module::ReadFailed;
|
2010-12-21 13:34:59 +01:00
|
|
|
module.startAddress =
|
2013-05-03 18:26:10 +02:00
|
|
|
item["loaded_addr"].data().toULongLong(0, 0);
|
2010-12-21 13:34:59 +01:00
|
|
|
module.endAddress = 0; // FIXME: End address not easily available.
|
2012-06-02 03:30:21 +02:00
|
|
|
handler->updateModule(module);
|
2009-03-12 12:00:53 +01:00
|
|
|
}
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2012-06-02 03:30:21 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-22 16:53:01 +02:00
|
|
|
void GdbEngine::examineModules()
|
|
|
|
|
{
|
2012-06-01 19:01:37 +02:00
|
|
|
ModulesHandler *handler = modulesHandler();
|
2013-03-11 17:23:55 +01:00
|
|
|
foreach (const Module &module, handler->modules()) {
|
2012-06-06 16:08:59 +02:00
|
|
|
if (module.elfData.symbolsType == UnknownSymbols)
|
2012-06-04 18:06:59 +02:00
|
|
|
handler->updateModule(module);
|
2010-07-22 16:53:01 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-17 14:08:49 +01:00
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// Source files specific stuff
|
|
|
|
|
//
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
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
|
|
|
{
|
2011-07-29 12:00:11 +02:00
|
|
|
QTC_CHECK(!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-02-17 14:08:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// Stack specific stuff
|
|
|
|
|
//
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2012-10-19 16:37:57 +02:00
|
|
|
void GdbEngine::selectThread(ThreadId threadId)
|
2009-10-05 12:00:47 +02:00
|
|
|
{
|
2012-10-19 16:37:57 +02:00
|
|
|
threadsHandler()->setCurrentThread(threadId);
|
2010-11-23 15:43:05 +01:00
|
|
|
showStatusMessage(tr("Retrieving data for stack view thread 0x%1...")
|
2012-10-19 16:37:57 +02:00
|
|
|
.arg(threadId.raw(), 0, 16), 10000);
|
|
|
|
|
postCommand("-thread-select " + QByteArray::number(threadId.raw()), Discardable,
|
2010-09-15 14:16:03 +02:00
|
|
|
CB(handleStackSelectThread));
|
2009-10-05 12:00:47 +02:00
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleStackSelectThread(const DebuggerResponse &)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2011-07-29 12:00:11 +02:00
|
|
|
QTC_CHECK(state() == InferiorUnrunnable || state() == InferiorStopOk);
|
2009-09-14 11:02:36 +02:00
|
|
|
showStatusMessage(tr("Retrieving data for stack view..."), 3000);
|
2015-02-06 01:26:47 +01:00
|
|
|
reloadStack(); // 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");
|
2013-11-17 21:36:37 +01:00
|
|
|
resetLocation();
|
2015-02-11 16:05:55 +01:00
|
|
|
DebuggerCommand cmd = stackCommand(-1);
|
|
|
|
|
cmd.flags = Discardable;
|
|
|
|
|
cmd.callback = [this](const DebuggerResponse &r) { handleStackListFrames(r, true); };
|
|
|
|
|
runCommand(cmd);
|
2009-10-05 12:00:47 +02:00
|
|
|
}
|
|
|
|
|
|
2014-02-07 18:10:02 +01:00
|
|
|
void GdbEngine::loadAdditionalQmlStack()
|
|
|
|
|
{
|
|
|
|
|
// Scan for QV4::ExecutionContext parameter in the parameter list of a V4 call.
|
|
|
|
|
postCommand("-stack-list-arguments --simple-values", NeedsStop, CB(handleQmlStackFrameArguments));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Scan the arguments of a stack list for the address of a QV4::ExecutionContext.
|
|
|
|
|
static quint64 findJsExecutionContextAddress(const GdbMi &stackArgsResponse, const QByteArray &qtNamespace)
|
|
|
|
|
{
|
|
|
|
|
const GdbMi frameList = stackArgsResponse.childAt(0);
|
|
|
|
|
if (!frameList.childCount())
|
|
|
|
|
return 0;
|
|
|
|
|
QByteArray jsExecutionContextType = qtNamespace;
|
|
|
|
|
if (!jsExecutionContextType.isEmpty())
|
|
|
|
|
jsExecutionContextType.append("::");
|
|
|
|
|
jsExecutionContextType.append("QV4::ExecutionContext *");
|
|
|
|
|
foreach (const GdbMi &frameNode, frameList.children()) {
|
|
|
|
|
foreach (const GdbMi &argNode, frameNode["args"].children()) {
|
|
|
|
|
if (argNode["type"].data() == jsExecutionContextType) {
|
|
|
|
|
bool ok;
|
|
|
|
|
const quint64 address = argNode["value"].data().toULongLong(&ok, 16);
|
|
|
|
|
if (ok && address)
|
|
|
|
|
return address;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static QString msgCannotLoadQmlStack(const QString &why)
|
|
|
|
|
{
|
|
|
|
|
return _("Unable to load QML stack: ") + why;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleQmlStackFrameArguments(const DebuggerResponse &response)
|
2014-02-07 18:10:02 +01:00
|
|
|
{
|
|
|
|
|
if (!response.data.isValid()) {
|
|
|
|
|
showMessage(msgCannotLoadQmlStack(_("No stack obtained.")), LogError);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const quint64 contextAddress = findJsExecutionContextAddress(response.data, qtNamespace());
|
|
|
|
|
if (!contextAddress) {
|
|
|
|
|
showMessage(msgCannotLoadQmlStack(_("The address of the JS execution context could not be found.")), LogError);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Call the debug function of QML with the context address to obtain the QML stack trace.
|
|
|
|
|
QByteArray command = "-data-evaluate-expression \"qt_v4StackTrace((QV4::ExecutionContext *)0x";
|
|
|
|
|
command += QByteArray::number(contextAddress, 16);
|
|
|
|
|
command += ")\"";
|
2015-02-06 01:26:47 +01:00
|
|
|
postCommand(command, NoFlags, CB(handleQmlStackTrace));
|
2014-02-07 18:10:02 +01:00
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleQmlStackTrace(const DebuggerResponse &response)
|
2014-02-07 18:10:02 +01:00
|
|
|
{
|
|
|
|
|
if (!response.data.isValid()) {
|
|
|
|
|
showMessage(msgCannotLoadQmlStack(_("No result obtained.")), LogError);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Prepend QML stack frames to existing C++ stack frames.
|
|
|
|
|
QByteArray stackData = response.data["value"].data();
|
|
|
|
|
const int index = stackData.indexOf("stack=");
|
|
|
|
|
if (index == -1) {
|
|
|
|
|
showMessage(msgCannotLoadQmlStack(_("Malformed result.")), LogError);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
stackData.remove(0, index);
|
|
|
|
|
stackData.replace("\\\"", "\"");
|
|
|
|
|
GdbMi stackMi;
|
|
|
|
|
stackMi.fromString(stackData);
|
|
|
|
|
const int qmlFrameCount = stackMi.childCount();
|
|
|
|
|
if (!qmlFrameCount) {
|
|
|
|
|
showMessage(msgCannotLoadQmlStack(_("No stack frames obtained.")), LogError);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
QList<StackFrame> qmlFrames;
|
|
|
|
|
qmlFrames.reserve(qmlFrameCount);
|
|
|
|
|
for (int i = 0; i < qmlFrameCount; ++i) {
|
|
|
|
|
StackFrame frame = parseStackFrame(stackMi.childAt(i), i);
|
2015-05-27 13:59:56 +02:00
|
|
|
frame.fixQmlFrame(runParameters());
|
2014-02-07 18:10:02 +01:00
|
|
|
qmlFrames.append(frame);
|
|
|
|
|
}
|
|
|
|
|
stackHandler()->prependFrames(qmlFrames);
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-11 16:05:55 +01:00
|
|
|
DebuggerCommand GdbEngine::stackCommand(int depth)
|
2014-12-12 09:22:57 +01:00
|
|
|
{
|
2015-02-11 16:05:55 +01:00
|
|
|
DebuggerCommand cmd("stackListFrames");
|
|
|
|
|
cmd.arg("limit", depth);
|
|
|
|
|
cmd.arg("options", isNativeMixedActive() ? "nativemixed" : "");
|
2014-12-12 09:22:57 +01:00
|
|
|
return cmd;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-06 01:26:47 +01:00
|
|
|
void GdbEngine::reloadStack()
|
2009-10-05 12:00:47 +02:00
|
|
|
{
|
2010-01-14 15:25:53 +01:00
|
|
|
PENDING_DEBUG("RELOAD STACK");
|
2015-02-11 16:05:55 +01:00
|
|
|
DebuggerCommand cmd = stackCommand(action(MaximalStackDepth)->value().toInt());
|
|
|
|
|
cmd.flags = Discardable;
|
|
|
|
|
cmd.callback = [this](const DebuggerResponse &r) { handleStackListFrames(r, false); };
|
|
|
|
|
runCommand(cmd);
|
2009-10-05 12:00:47 +02:00
|
|
|
}
|
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;
|
2013-05-03 18:26:10 +02:00
|
|
|
GdbMi fullName = frameMi["fullname"];
|
2009-10-29 17:50:53 +01:00
|
|
|
if (fullName.isValid())
|
|
|
|
|
frame.file = cleanupFullName(QFile::decodeName(fullName.data()));
|
|
|
|
|
else
|
2013-05-03 18:26:10 +02:00
|
|
|
frame.file = QFile::decodeName(frameMi["file"].data());
|
|
|
|
|
frame.function = _(frameMi["func"].data());
|
|
|
|
|
frame.from = _(frameMi["from"].data());
|
2013-05-07 12:09:54 +02:00
|
|
|
frame.line = frameMi["line"].toInt();
|
2013-05-03 18:26:10 +02:00
|
|
|
frame.address = frameMi["addr"].toAddress();
|
2015-01-22 12:05:00 +01:00
|
|
|
GdbMi usable = frameMi["usable"];
|
|
|
|
|
if (usable.isValid())
|
|
|
|
|
frame.usable = usable.data().toInt();
|
|
|
|
|
else
|
|
|
|
|
frame.usable = QFileInfo(frame.file).isReadable();
|
|
|
|
|
if (frameMi["language"].data() == "js"
|
|
|
|
|
|| frame.file.endsWith(QLatin1String(".js"))
|
|
|
|
|
|| frame.file.endsWith(QLatin1String(".qml"))) {
|
|
|
|
|
frame.file = QFile::decodeName(frameMi["file"].data());
|
2014-02-07 18:10:02 +01:00
|
|
|
frame.language = QmlLanguage;
|
2015-05-27 13:59:56 +02:00
|
|
|
frame.fixQmlFrame(runParameters());
|
2014-12-05 18:45:54 +01:00
|
|
|
}
|
2009-10-02 11:45:19 +02:00
|
|
|
return frame;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-06 01:26:47 +01:00
|
|
|
void GdbEngine::handleStackListFrames(const DebuggerResponse &response, bool isFull)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass != ResultDone) {
|
2009-10-05 12:00:47 +02:00
|
|
|
// That always happens on symbian gdb with
|
|
|
|
|
// ^error,data={msg="Previous frame identical to this frame (corrupt stack?)"
|
2011-07-05 17:57:57 +02:00
|
|
|
// logStreamOutput: "Previous frame identical to this frame (corrupt stack?)\n"
|
2009-10-05 12:00:47 +02:00
|
|
|
//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
|
|
|
QList<StackFrame> stackFrames;
|
2009-09-14 09:46:34 +02:00
|
|
|
|
2014-12-12 09:22:57 +01:00
|
|
|
GdbMi stack = response.data["stack"]; // C++
|
|
|
|
|
if (!stack.isValid() || stack.childCount() == 0) // Mixed.
|
|
|
|
|
stack.fromStringMultiple(response.consoleStreamOutput);
|
2014-12-05 18:45:54 +01:00
|
|
|
|
2009-10-05 12:00:47 +02:00
|
|
|
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
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2015-02-06 01:26:47 +01:00
|
|
|
bool canExpand = !isFull && (n >= action(MaximalStackDepth)->value().toInt());
|
2014-07-28 14:23:52 +02:00
|
|
|
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.
|
2014-07-28 14:23:52 +02:00
|
|
|
if (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);
|
2010-11-22 17:12:02 +01:00
|
|
|
handler->setCurrentIndex(frameIndex);
|
2010-12-16 19:06:33 +01:00
|
|
|
gotoLocation(stackHandler()->currentFrame());
|
2014-12-12 09:22:57 +01:00
|
|
|
|
|
|
|
|
if (handler->frameAt(frameIndex).language != QmlLanguage) {
|
|
|
|
|
// 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.
|
|
|
|
|
QByteArray cmd = "-stack-select-frame";
|
|
|
|
|
//if (!m_currentThread.isEmpty())
|
|
|
|
|
// cmd += " --thread " + m_currentThread;
|
|
|
|
|
cmd += ' ';
|
|
|
|
|
cmd += QByteArray::number(frameIndex);
|
|
|
|
|
postCommand(cmd, Discardable);
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-22 17:12:02 +01:00
|
|
|
updateLocals();
|
|
|
|
|
reloadRegisters();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleThreadInfo(const DebuggerResponse &response)
|
2010-05-17 17:38:31 +02:00
|
|
|
{
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultDone) {
|
2012-10-24 16:48:09 +02:00
|
|
|
ThreadsHandler *handler = threadsHandler();
|
|
|
|
|
handler->updateThreads(response.data);
|
|
|
|
|
// This is necessary as the current thread might not be in the list.
|
|
|
|
|
if (!handler->currentThread().isValid()) {
|
|
|
|
|
ThreadId other = handler->threadAt(0);
|
|
|
|
|
if (other.isValid())
|
|
|
|
|
selectThread(other);
|
|
|
|
|
}
|
2010-10-27 14:21:33 +02:00
|
|
|
updateViews(); // Adjust Threads combobox.
|
2014-07-28 14:23:52 +02:00
|
|
|
if (boolSetting(ShowThreadNames)) {
|
2010-10-27 14:21:33 +02:00
|
|
|
postCommand("threadnames " +
|
2014-07-28 14:23:52 +02:00
|
|
|
action(MaximalStackDepth)->value().toByteArray(),
|
2011-05-24 12:38:54 +02:00
|
|
|
Discardable, CB(handleThreadNames));
|
2010-10-27 14:21:33 +02:00
|
|
|
}
|
2015-02-06 01:26:47 +01:00
|
|
|
reloadStack(); // Will trigger register reload.
|
2010-05-17 17:38:31 +02:00
|
|
|
} else {
|
|
|
|
|
// Fall back for older versions: Try to get at least a list
|
|
|
|
|
// of running threads.
|
2011-05-24 12:38:54 +02:00
|
|
|
postCommand("-thread-list-ids", Discardable, CB(handleThreadListIds));
|
2010-05-17 17:38:31 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleThreadListIds(const DebuggerResponse &response)
|
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"
|
2012-10-19 16:37:57 +02:00
|
|
|
ThreadsHandler *handler = threadsHandler();
|
2015-02-17 17:44:54 +01:00
|
|
|
const std::vector<GdbMi> &items = response.data["thread-ids"].children();
|
|
|
|
|
for (size_t index = 0, n = items.size(); index != n; ++index) {
|
2008-12-02 12:01:29 +01:00
|
|
|
ThreadData thread;
|
2013-05-07 12:09:54 +02:00
|
|
|
thread.id = ThreadId(items.at(index).toInt());
|
2012-10-19 16:37:57 +02:00
|
|
|
handler->updateThread(thread);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2015-02-06 01:26:47 +01:00
|
|
|
reloadStack(); // Will trigger register reload.
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleThreadNames(const DebuggerResponse &response)
|
2010-09-13 12:37:30 +02:00
|
|
|
{
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultDone) {
|
2012-10-19 16:37:57 +02:00
|
|
|
ThreadsHandler *handler = threadsHandler();
|
2010-09-13 12:37:30 +02:00
|
|
|
GdbMi names;
|
2011-07-05 17:57:57 +02:00
|
|
|
names.fromString(response.consoleStreamOutput);
|
2010-09-13 12:37:30 +02:00
|
|
|
foreach (const GdbMi &name, names.children()) {
|
2012-10-19 16:37:57 +02:00
|
|
|
ThreadData thread;
|
2013-05-07 12:09:54 +02:00
|
|
|
thread.id = ThreadId(name["id"].toInt());
|
2013-05-03 18:26:10 +02:00
|
|
|
thread.name = decodeData(name["value"].data(),
|
2015-08-12 11:26:10 +02:00
|
|
|
DebuggerEncoding(name["valueencoded"].toInt()));
|
2012-10-19 16:37:57 +02:00
|
|
|
handler->updateThread(thread);
|
2010-09-13 12:37:30 +02:00
|
|
|
}
|
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();
|
2014-05-27 17:51:55 +02:00
|
|
|
// This must not be quoted, it doesn't work otherwise.
|
2015-02-06 01:26:47 +01:00
|
|
|
postCommand("gcore " + fileName.toLocal8Bit(), NeedsStop|ConsoleCommand,
|
|
|
|
|
[this, fileName](const DebuggerResponse &r) { handleMakeSnapshot(r, fileName); });
|
2010-02-02 17:25:14 +01:00
|
|
|
} else {
|
2015-02-03 23:58:49 +02:00
|
|
|
AsynchronousMessageBox::critical(tr("Snapshot Creation Error"),
|
2010-02-02 17:25:14 +01:00
|
|
|
tr("Cannot create snapshot file."));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-06 01:26:47 +01:00
|
|
|
void GdbEngine::handleMakeSnapshot(const DebuggerResponse &response, const QString &coreFile)
|
2010-02-02 17:25:14 +01:00
|
|
|
{
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultDone) {
|
2015-05-27 13:59:56 +02:00
|
|
|
DebuggerRunParameters rp = runParameters();
|
|
|
|
|
rp.startMode = AttachCore;
|
|
|
|
|
rp.coreFile = coreFile;
|
2010-07-13 15:57:34 +02:00
|
|
|
//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);
|
|
|
|
|
}
|
2015-05-27 13:59:56 +02:00
|
|
|
rp.displayName = function + _(": ") + QDateTime::currentDateTime().toString();
|
|
|
|
|
rp.isSnapshot = true;
|
2015-06-24 08:26:35 +02:00
|
|
|
createAndScheduleRun(rp, 0);
|
2010-02-02 17:25:14 +01:00
|
|
|
} else {
|
2013-05-03 18:26:10 +02:00
|
|
|
QByteArray msg = response.data["msg"].data();
|
2015-02-03 23:58:49 +02:00
|
|
|
AsynchronousMessageBox::critical(tr("Snapshot Creation Error"),
|
2013-10-17 13:48:04 +02:00
|
|
|
tr("Cannot create snapshot:") + QLatin1Char('\n') + QString::fromLocal8Bit(msg));
|
2010-02-02 17:25:14 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// Register specific stuff
|
|
|
|
|
//
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void GdbEngine::reloadRegisters()
|
|
|
|
|
{
|
2014-10-22 13:04:47 +02:00
|
|
|
if (!Internal::isDockVisible(_(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;
|
2014-12-17 13:14:29 +01:00
|
|
|
|
|
|
|
|
if (true) {
|
|
|
|
|
if (!m_registerNamesListed) {
|
2015-07-21 12:52:18 +02:00
|
|
|
// The MI version does not give register size.
|
|
|
|
|
// postCommand("-data-list-register-names", NoFlags, CB(handleRegisterListNames));
|
|
|
|
|
postCommand("maintenance print raw-registers", NoFlags, CB(handleRegisterListing));
|
2014-12-17 13:14:29 +01:00
|
|
|
m_registerNamesListed = true;
|
|
|
|
|
}
|
|
|
|
|
// Can cause i386-linux-nat.c:571: internal-error: Got request
|
|
|
|
|
// for bad register number 41.\nA problem internal to GDB has been detected.
|
|
|
|
|
postCommand("-data-list-register-values r",
|
|
|
|
|
Discardable, CB(handleRegisterListValues));
|
|
|
|
|
} else {
|
2015-02-06 01:26:47 +01:00
|
|
|
postCommand("maintenance print cooked-registers", NoFlags, CB(handleMaintPrintRegisters));
|
2009-10-30 16:42:33 +01:00
|
|
|
}
|
2014-12-17 13:14:29 +01:00
|
|
|
}
|
2009-10-30 16:42:33 +01:00
|
|
|
|
2014-12-17 13:14:29 +01:00
|
|
|
static QByteArray readWord(const QByteArray &ba, int *pos)
|
|
|
|
|
{
|
|
|
|
|
const int n = ba.size();
|
|
|
|
|
while (*pos < n && ba.at(*pos) == ' ')
|
|
|
|
|
++*pos;
|
|
|
|
|
const int start = *pos;
|
|
|
|
|
while (*pos < n && ba.at(*pos) != ' ' && ba.at(*pos) != '\n')
|
|
|
|
|
++*pos;
|
|
|
|
|
return ba.mid(start, *pos - start);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleMaintPrintRegisters(const DebuggerResponse &response)
|
2009-08-18 08:34:48 +02:00
|
|
|
{
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass != ResultDone)
|
2014-12-17 13:14:29 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const QByteArray &ba = response.consoleStreamOutput;
|
|
|
|
|
RegisterHandler *handler = registerHandler();
|
|
|
|
|
//0 1 2 3 4 5 6
|
|
|
|
|
//0123456789012345678901234567890123456789012345678901234567890
|
|
|
|
|
// Name Nr Rel Offset Size Type Raw value
|
|
|
|
|
// rax 0 0 0 8 int64_t 0x0000000000000000
|
|
|
|
|
// rip 16 16 128 8 *1 0x0000000000400dc9
|
|
|
|
|
// eflags 17 17 136 4 i386_eflags 0x00000246
|
|
|
|
|
// cs 18 18 140 4 int32_t 0x00000033
|
|
|
|
|
// xmm15 55 55 516 16 vec128 0x00000000000000000000000000000000
|
|
|
|
|
// mxcsr 56 56 532 4 i386_mxcsr 0x00001fa0
|
|
|
|
|
// ''
|
|
|
|
|
// st6 30 30 224 10 _i387_ext 0x00000000000000000000
|
|
|
|
|
// st7 31 31 234 10 _i387_ext 0x00000000000000000000
|
|
|
|
|
// fctrl 32 32 244 4 int 0x0000037f
|
|
|
|
|
|
|
|
|
|
const int n = ba.size();
|
|
|
|
|
int pos = 0;
|
|
|
|
|
while (true) {
|
|
|
|
|
// Skip first line, and until '\n' after each line finished.
|
|
|
|
|
while (pos < n && ba.at(pos) != '\n')
|
|
|
|
|
++pos;
|
|
|
|
|
if (pos >= n)
|
|
|
|
|
break;
|
|
|
|
|
++pos; // skip \n
|
|
|
|
|
Register reg;
|
|
|
|
|
reg.name = readWord(ba, &pos);
|
|
|
|
|
if (reg.name == "''" || reg.name == "*1:" || reg.name.isEmpty())
|
|
|
|
|
continue;
|
|
|
|
|
readWord(ba, &pos); // Nr
|
|
|
|
|
readWord(ba, &pos); // Rel
|
|
|
|
|
readWord(ba, &pos); // Offset
|
|
|
|
|
reg.size = readWord(ba, &pos).toInt();
|
|
|
|
|
reg.reportedType = readWord(ba, &pos);
|
2015-08-25 13:48:55 +02:00
|
|
|
reg.value.fromByteArray(readWord(ba, &pos), HexadecimalFormat);
|
2014-12-17 13:14:29 +01:00
|
|
|
handler->updateRegister(reg);
|
|
|
|
|
}
|
|
|
|
|
handler->commitUpdates();
|
|
|
|
|
}
|
2015-08-25 13:48:55 +02:00
|
|
|
|
2014-12-17 13:14:29 +01:00
|
|
|
void GdbEngine::setRegisterValue(const QByteArray &name, const QString &value)
|
|
|
|
|
{
|
2015-08-25 13:48:55 +02:00
|
|
|
QByteArray fullName = name;
|
|
|
|
|
if (name.startsWith("xmm"))
|
|
|
|
|
fullName += ".uint128";
|
|
|
|
|
postCommand("set $" + fullName + "=" + value.toLatin1());
|
2009-08-18 08:34:48 +02:00
|
|
|
reloadRegisters();
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleRegisterListNames(const DebuggerResponse &response)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass != ResultDone) {
|
2009-10-30 16:42:33 +01:00
|
|
|
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
|
|
|
|
2013-05-03 18:26:10 +02:00
|
|
|
GdbMi names = response.data["register-names"];
|
2015-07-21 12:52:18 +02:00
|
|
|
m_registers.clear();
|
2014-12-17 13:14:29 +01:00
|
|
|
int gdbRegisterNumber = 0;
|
2012-08-28 12:39:57 -05:00
|
|
|
foreach (const GdbMi &item, names.children()) {
|
2015-07-21 12:52:18 +02:00
|
|
|
if (!item.data().isEmpty()) {
|
|
|
|
|
Register reg;
|
|
|
|
|
reg.name = item.data();
|
|
|
|
|
m_registers[gdbRegisterNumber] = reg;
|
|
|
|
|
}
|
2014-12-17 13:14:29 +01:00
|
|
|
++gdbRegisterNumber;
|
2012-08-28 12:39:57 -05:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2015-07-21 12:52:18 +02:00
|
|
|
void GdbEngine::handleRegisterListing(const DebuggerResponse &response)
|
|
|
|
|
{
|
|
|
|
|
if (response.resultClass != ResultDone) {
|
|
|
|
|
m_registerNamesListed = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// &"maintenance print raw-registers\n"
|
|
|
|
|
// >~" Name Nr Rel Offset Size Type Raw value\n"
|
|
|
|
|
// >~" rax 0 0 0 8 int64_t 0x0000000000000005\n"
|
|
|
|
|
// >~" rip 16 16 128 8 *1 0x000000000040232a\n"
|
|
|
|
|
// >~" '' 145 145 536 0 int0_t <invalid>\n"
|
|
|
|
|
|
|
|
|
|
m_registers.clear();
|
|
|
|
|
QList<QByteArray> lines = response.consoleStreamOutput.split('\n');
|
|
|
|
|
for (int i = 1; i < lines.size(); ++i) {
|
|
|
|
|
QStringList parts = QString::fromLatin1(lines.at(i))
|
|
|
|
|
.split(QLatin1Char(' '), QString::SkipEmptyParts);
|
|
|
|
|
if (parts.size() < 7)
|
|
|
|
|
continue;
|
|
|
|
|
int gdbRegisterNumber = parts.at(1).toInt();
|
|
|
|
|
Register reg;
|
|
|
|
|
reg.name = parts.at(0).toLatin1();
|
|
|
|
|
reg.size = parts.at(4).toInt();
|
|
|
|
|
reg.reportedType = parts.at(5).toLatin1();
|
|
|
|
|
m_registers[gdbRegisterNumber] = reg;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleRegisterListValues(const DebuggerResponse &response)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass != ResultDone)
|
2008-12-02 12:01:29 +01:00
|
|
|
return;
|
|
|
|
|
|
2014-12-17 13:14:29 +01:00
|
|
|
RegisterHandler *handler = registerHandler();
|
2008-12-02 12:01:29 +01:00
|
|
|
// 24^done,register-values=[{number="0",value="0xf423f"},...]
|
2013-05-03 18:26:10 +02:00
|
|
|
const GdbMi values = response.data["register-values"];
|
2011-09-14 12:53:17 +02:00
|
|
|
foreach (const GdbMi &item, values.children()) {
|
2013-05-07 12:09:54 +02:00
|
|
|
const int number = item["number"].toInt();
|
2015-07-21 12:52:18 +02:00
|
|
|
Register reg = m_registers[number];
|
2014-12-17 13:14:29 +01:00
|
|
|
QByteArray data = item["value"].data();
|
|
|
|
|
if (data.startsWith("0x")) {
|
2015-08-25 13:48:55 +02:00
|
|
|
reg.value.fromByteArray(data, HexadecimalFormat);
|
2015-03-03 17:10:50 +01:00
|
|
|
} else if (data == "<error reading variable>") {
|
|
|
|
|
// Nothing. See QTCREATORBUG-14029.
|
2014-12-17 13:14:29 +01:00
|
|
|
} else {
|
|
|
|
|
// This is what GDB considers machine readable output:
|
|
|
|
|
// value="{v4_float = {0x00000000, 0x00000000, 0x00000000, 0x00000000},
|
|
|
|
|
// v2_double = {0x0000000000000000, 0x0000000000000000},
|
|
|
|
|
// v16_int8 = {0x00 <repeats 16 times>},
|
|
|
|
|
// v8_int16 = {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
|
|
|
|
|
// v4_int32 = {0x00000000, 0x00000000, 0x00000000, 0x00000000},
|
|
|
|
|
// v2_int64 = {0x0000000000000000, 0x0000000000000000},
|
|
|
|
|
// uint128 = <error reading variable>}"}
|
|
|
|
|
// Try to make sense of it using the int32 chunks:
|
2015-08-25 13:48:55 +02:00
|
|
|
QByteArray result;
|
2014-12-17 13:14:29 +01:00
|
|
|
const int pos1 = data.indexOf("_int32");
|
|
|
|
|
const int pos2 = data.indexOf('{', pos1) + 1;
|
|
|
|
|
const int pos3 = data.indexOf('}', pos2);
|
|
|
|
|
QByteArray inner = data.mid(pos2, pos3 - pos2);
|
|
|
|
|
QList<QByteArray> list = inner.split(',');
|
|
|
|
|
for (int i = list.size(); --i >= 0; ) {
|
|
|
|
|
QByteArray chunk = list.at(i);
|
|
|
|
|
if (chunk.startsWith(' '))
|
|
|
|
|
chunk.remove(0, 1);
|
|
|
|
|
if (chunk.startsWith("0x"))
|
|
|
|
|
chunk.remove(0, 2);
|
|
|
|
|
QTC_ASSERT(chunk.size() == 8, continue);
|
|
|
|
|
result.append(chunk);
|
|
|
|
|
}
|
2015-08-25 13:48:55 +02:00
|
|
|
reg.value.fromByteArray(result, HexadecimalFormat);
|
2014-12-17 13:14:29 +01:00
|
|
|
}
|
|
|
|
|
handler->updateRegister(reg);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2014-12-17 13:14:29 +01:00
|
|
|
handler->commitUpdates();
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleVarAssign(const DebuggerResponse &)
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-19 12:42:53 +01:00
|
|
|
void GdbEngine::assignValueInDebugger(WatchItem *item,
|
2010-11-10 16:33:11 +01:00
|
|
|
const QString &expression, const QVariant &value)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2015-02-10 13:40:26 +01:00
|
|
|
DebuggerCommand cmd("assignValue");
|
2015-03-19 12:42:53 +01:00
|
|
|
cmd.arg("type", item->type.toHex());
|
2015-02-10 13:40:26 +01:00
|
|
|
cmd.arg("expr", expression.toLatin1().toHex());
|
|
|
|
|
cmd.arg("value", value.toString().toLatin1().toHex());
|
2015-03-19 12:42:53 +01:00
|
|
|
cmd.arg("simpleType", isIntOrFloatType(item->type));
|
2015-02-10 13:40:26 +01:00
|
|
|
cmd.callback = CB(handleVarAssign);
|
|
|
|
|
runCommand(cmd);
|
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());
|
2014-05-27 13:36:24 +02:00
|
|
|
postCommand("print " + qtNamespace() + "QApplication::widgetAt("
|
2011-01-17 15:11:11 +01:00
|
|
|
+ x + ',' + y + ')',
|
2009-07-01 12:49:41 +02:00
|
|
|
NeedsStop, CB(handleWatchPoint));
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleWatchPoint(const DebuggerResponse &response)
|
2009-07-01 12:49:41 +02:00
|
|
|
{
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultDone) {
|
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);
|
2014-05-27 13:36:24 +02:00
|
|
|
const int pos0x = ba.indexOf("0x");
|
|
|
|
|
if (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
|
2014-05-27 13:36:24 +02:00
|
|
|
const QByteArray type = "::" + qtNamespace() + "QWidget";
|
|
|
|
|
const QString exp = _("{%1}%2").arg(_(type)).arg(_(addr));
|
2011-01-17 16:33:31 +01:00
|
|
|
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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-24 11:55:09 +02:00
|
|
|
class MemoryAgentCookie
|
2009-08-14 13:04:05 +02:00
|
|
|
{
|
2011-05-24 11:55:09 +02:00
|
|
|
public:
|
2013-11-05 18:47:03 +01:00
|
|
|
MemoryAgentCookie()
|
|
|
|
|
: accumulator(0), pendingRequests(0), agent(0), token(0), base(0), offset(0), length(0)
|
2009-08-18 08:34:48 +02:00
|
|
|
{}
|
2011-05-24 11:55:09 +02:00
|
|
|
|
|
|
|
|
public:
|
2013-11-05 18:47:03 +01:00
|
|
|
QByteArray *accumulator; // Shared between split request. Last one cleans up.
|
|
|
|
|
uint *pendingRequests; // Shared between split request. Last one cleans up.
|
|
|
|
|
|
2010-12-14 12:29:32 +01:00
|
|
|
QPointer<MemoryAgent> agent;
|
2010-02-17 17:33:42 +01:00
|
|
|
QPointer<QObject> token;
|
2013-11-05 18:47:03 +01:00
|
|
|
quint64 base; // base address.
|
|
|
|
|
uint offset; // offset to base, and in accumulator
|
|
|
|
|
uint length; //
|
2009-08-18 08:34:48 +02:00
|
|
|
};
|
2009-08-14 13:04:05 +02:00
|
|
|
|
2013-11-05 18:47:03 +01:00
|
|
|
|
2011-02-25 13:21:54 +01:00
|
|
|
void GdbEngine::changeMemory(MemoryAgent *agent, QObject *token,
|
|
|
|
|
quint64 addr, const QByteArray &data)
|
|
|
|
|
{
|
2013-11-05 18:47:03 +01:00
|
|
|
QByteArray cmd = "-data-write-memory 0x" + QByteArray::number(addr, 16) + " d 1";
|
2011-05-24 11:55:09 +02:00
|
|
|
foreach (unsigned char c, data) {
|
2011-02-25 13:21:54 +01:00
|
|
|
cmd.append(' ');
|
2011-05-24 11:55:09 +02:00
|
|
|
cmd.append(QByteArray::number(uint(c)));
|
2011-02-25 13:21:54 +01:00
|
|
|
}
|
2013-11-05 18:47:03 +01:00
|
|
|
MemoryAgentCookie ac;
|
|
|
|
|
ac.agent = agent;
|
|
|
|
|
ac.token = token;
|
|
|
|
|
ac.base = addr;
|
|
|
|
|
ac.length = data.size();
|
2015-02-06 01:26:47 +01:00
|
|
|
postCommand(cmd, NeedsStop, CB(handleChangeMemory));
|
2011-02-25 13:21:54 +01:00
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleChangeMemory(const DebuggerResponse &response)
|
2011-02-25 13:21:54 +01:00
|
|
|
{
|
|
|
|
|
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
|
|
|
{
|
2013-11-05 18:47:03 +01:00
|
|
|
MemoryAgentCookie ac;
|
|
|
|
|
ac.accumulator = new QByteArray(length, char());
|
|
|
|
|
ac.pendingRequests = new uint(1);
|
|
|
|
|
ac.agent = agent;
|
|
|
|
|
ac.token = token;
|
|
|
|
|
ac.base = addr;
|
|
|
|
|
ac.length = length;
|
|
|
|
|
fetchMemoryHelper(ac);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::fetchMemoryHelper(const MemoryAgentCookie &ac)
|
|
|
|
|
{
|
|
|
|
|
postCommand("-data-read-memory 0x" + QByteArray::number(ac.base + ac.offset, 16) + " x 1 1 "
|
2015-02-06 01:26:47 +01:00
|
|
|
+ QByteArray::number(ac.length), NeedsStop,
|
|
|
|
|
[this, ac](const DebuggerResponse &r) { handleFetchMemory(r, ac); });
|
2009-08-14 13:04:05 +02:00
|
|
|
}
|
|
|
|
|
|
2015-02-06 01:26:47 +01:00
|
|
|
void GdbEngine::handleFetchMemory(const DebuggerResponse &response, MemoryAgentCookie ac)
|
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"]}]
|
2013-11-05 18:47:03 +01:00
|
|
|
--*ac.pendingRequests;
|
|
|
|
|
showMessage(QString::fromLatin1("PENDING: %1").arg(*ac.pendingRequests));
|
2009-08-18 08:34:48 +02:00
|
|
|
QTC_ASSERT(ac.agent, return);
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultDone) {
|
2013-11-05 18:47:03 +01:00
|
|
|
GdbMi memory = response.data["memory"];
|
|
|
|
|
QTC_ASSERT(memory.children().size() <= 1, return);
|
2015-02-17 17:44:54 +01:00
|
|
|
if (memory.children().empty())
|
2013-11-05 18:47:03 +01:00
|
|
|
return;
|
|
|
|
|
GdbMi memory0 = memory.children().at(0); // we asked for only one 'row'
|
|
|
|
|
GdbMi data = memory0["data"];
|
2015-02-19 16:44:58 +01:00
|
|
|
for (int i = 0, n = int(data.children().size()); i != n; ++i) {
|
2013-11-05 18:47:03 +01:00
|
|
|
const GdbMi &child = data.children().at(i);
|
|
|
|
|
bool ok = true;
|
|
|
|
|
unsigned char c = '?';
|
|
|
|
|
c = child.data().toUInt(&ok, 0);
|
|
|
|
|
QTC_ASSERT(ok, return);
|
|
|
|
|
(*ac.accumulator)[ac.offset + i] = c;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// We have an error
|
|
|
|
|
if (ac.length > 1) {
|
|
|
|
|
// ... and size > 1, split the load and re-try.
|
|
|
|
|
*ac.pendingRequests += 2;
|
|
|
|
|
uint hunk = ac.length / 2;
|
|
|
|
|
MemoryAgentCookie ac1 = ac;
|
|
|
|
|
ac1.length = hunk;
|
|
|
|
|
ac1.offset = ac.offset;
|
|
|
|
|
MemoryAgentCookie ac2 = ac;
|
|
|
|
|
ac2.length = ac.length - hunk;
|
|
|
|
|
ac2.offset = ac.offset + hunk;
|
|
|
|
|
fetchMemoryHelper(ac1);
|
|
|
|
|
fetchMemoryHelper(ac2);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (*ac.pendingRequests <= 0) {
|
|
|
|
|
ac.agent->addLazyData(ac.token, ac.base, *ac.accumulator);
|
|
|
|
|
delete ac.pendingRequests;
|
|
|
|
|
delete ac.accumulator;
|
|
|
|
|
}
|
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
|
|
|
{
|
2014-07-28 14:23:52 +02:00
|
|
|
if (boolSetting(IntelFlavor))
|
2014-05-26 15:24:27 +02:00
|
|
|
postCommand("set disassembly-flavor intel");
|
|
|
|
|
else
|
|
|
|
|
postCommand("set disassembly-flavor att");
|
|
|
|
|
|
2014-04-11 18:35:37 +02:00
|
|
|
fetchDisassemblerByCliPointMixed(agent);
|
2009-08-14 13:04:05 +02:00
|
|
|
}
|
2011-02-10 13:43:02 +01:00
|
|
|
|
2012-02-01 17:44:07 +01:00
|
|
|
static inline QByteArray disassemblerCommand(const Location &location, bool mixed)
|
|
|
|
|
{
|
2014-05-02 17:01:39 +02:00
|
|
|
QByteArray command = "disassemble /r";
|
2012-02-01 17:44:07 +01:00
|
|
|
if (mixed)
|
2014-05-02 17:01:39 +02:00
|
|
|
command += 'm';
|
|
|
|
|
command += ' ';
|
2012-02-01 17:44:07 +01:00
|
|
|
if (const quint64 address = location.address()) {
|
|
|
|
|
command += "0x";
|
|
|
|
|
command += QByteArray::number(address, 16);
|
|
|
|
|
} else if (!location.functionName().isEmpty()) {
|
|
|
|
|
command += location.functionName().toLatin1();
|
|
|
|
|
} else {
|
|
|
|
|
QTC_ASSERT(false, return QByteArray(); );
|
|
|
|
|
}
|
|
|
|
|
return command;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-06 01:26:47 +01:00
|
|
|
void GdbEngine::fetchDisassemblerByCliPointMixed(const DisassemblerAgentCookie &ac)
|
2011-02-10 13:43:02 +01:00
|
|
|
{
|
|
|
|
|
QTC_ASSERT(ac.agent, return);
|
2012-04-13 14:44:39 +02:00
|
|
|
postCommand(disassemblerCommand(ac.agent->location(), true), Discardable|ConsoleCommand,
|
2015-02-06 01:26:47 +01:00
|
|
|
[this, ac](const DebuggerResponse &response) {
|
|
|
|
|
if (response.resultClass == ResultDone)
|
|
|
|
|
if (handleCliDisassemblerResult(response.consoleStreamOutput, ac.agent))
|
|
|
|
|
return;
|
|
|
|
|
// 'point, plain' can take far too long.
|
|
|
|
|
// Skip this feature and immediately fall back to the 'range' version:
|
|
|
|
|
fetchDisassemblerByCliRangeMixed(ac);
|
|
|
|
|
});
|
2011-02-10 13:43:02 +01:00
|
|
|
}
|
|
|
|
|
|
2015-02-06 01:26:47 +01:00
|
|
|
void GdbEngine::fetchDisassemblerByCliRangeMixed(const DisassemblerAgentCookie &ac)
|
2011-02-10 13:43:02 +01:00
|
|
|
{
|
|
|
|
|
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);
|
2014-05-02 17:01:39 +02:00
|
|
|
QByteArray cmd = "disassemble /rm 0x" + start + ",0x" + end;
|
2012-04-13 14:44:39 +02:00
|
|
|
postCommand(cmd, Discardable|ConsoleCommand,
|
2015-02-06 01:26:47 +01:00
|
|
|
[this, ac](const DebuggerResponse &response) {
|
|
|
|
|
if (response.resultClass == ResultDone)
|
|
|
|
|
if (handleCliDisassemblerResult(response.consoleStreamOutput, ac.agent))
|
|
|
|
|
return;
|
|
|
|
|
fetchDisassemblerByCliRangePlain(ac);
|
|
|
|
|
});
|
2010-02-09 15:02:55 +01:00
|
|
|
}
|
|
|
|
|
|
2014-04-11 18:35:37 +02: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);
|
2014-04-30 14:56:51 +02:00
|
|
|
QByteArray cmd = "disassemble /r 0x" + start + ",0x" + end;
|
2012-04-13 14:44:39 +02:00
|
|
|
postCommand(cmd, Discardable,
|
2015-02-06 01:26:47 +01:00
|
|
|
[this, ac](const DebuggerResponse &response) {
|
|
|
|
|
if (response.resultClass == ResultDone)
|
|
|
|
|
if (handleCliDisassemblerResult(response.consoleStreamOutput, ac.agent))
|
|
|
|
|
return;
|
|
|
|
|
// 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["msg"].data();
|
|
|
|
|
showStatusMessage(tr("Disassembler failed: %1")
|
|
|
|
|
.arg(QString::fromLocal8Bit(msg)), 5000);
|
|
|
|
|
});
|
2010-02-09 15:02:55 +01:00
|
|
|
}
|
|
|
|
|
|
2014-04-11 18:35:37 +02:00
|
|
|
struct LineData
|
2009-08-14 13:04:05 +02:00
|
|
|
{
|
2014-04-11 18:35:37 +02:00
|
|
|
LineData() {}
|
|
|
|
|
LineData(int i, int f) : index(i), function(f) {}
|
|
|
|
|
int index;
|
|
|
|
|
int function;
|
|
|
|
|
};
|
2009-08-12 10:51:25 +02:00
|
|
|
|
2014-04-11 18:35:37 +02:00
|
|
|
bool GdbEngine::handleCliDisassemblerResult(const QByteArray &output, DisassemblerAgent *agent)
|
2011-02-10 13:43:02 +01:00
|
|
|
{
|
2015-02-06 01:26:47 +01:00
|
|
|
QTC_ASSERT(agent, return true);
|
2011-02-10 13:43:02 +01:00
|
|
|
// First line is something like
|
|
|
|
|
// "Dump of assembler code from 0xb7ff598f to 0xb7ff5a07:"
|
|
|
|
|
DisassemblerLines dlines;
|
2011-07-05 17:57:57 +02:00
|
|
|
foreach (const QByteArray &line, output.split('\n'))
|
2011-04-12 17:32:41 +02:00
|
|
|
dlines.appendUnparsed(_(line));
|
2011-02-10 13:43:02 +01:00
|
|
|
|
2014-04-11 18:35:37 +02:00
|
|
|
QVector<DisassemblerLine> lines = dlines.data();
|
|
|
|
|
|
|
|
|
|
typedef QMap<quint64, LineData> LineMap;
|
|
|
|
|
LineMap lineMap;
|
|
|
|
|
int currentFunction = -1;
|
|
|
|
|
for (int i = 0, n = lines.size(); i != n; ++i) {
|
|
|
|
|
const DisassemblerLine &line = lines.at(i);
|
|
|
|
|
if (line.address)
|
|
|
|
|
lineMap.insert(line.address, LineData(i, currentFunction));
|
|
|
|
|
else
|
|
|
|
|
currentFunction = i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
currentFunction = -1;
|
|
|
|
|
DisassemblerLines result;
|
2014-04-30 14:56:51 +02:00
|
|
|
result.setBytesLength(dlines.bytesLength());
|
2015-04-01 11:19:32 +02:00
|
|
|
for (LineMap::const_iterator it = lineMap.constBegin(), et = lineMap.constEnd(); it != et; ++it) {
|
2014-04-11 18:35:37 +02:00
|
|
|
LineData d = *it;
|
|
|
|
|
if (d.function != currentFunction) {
|
|
|
|
|
if (d.function != -1) {
|
|
|
|
|
DisassemblerLine &line = lines[d.function];
|
|
|
|
|
++line.hunk;
|
|
|
|
|
result.appendLine(line);
|
|
|
|
|
currentFunction = d.function;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
result.appendLine(lines.at(d.index));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (result.coversAddress(agent->address())) {
|
|
|
|
|
agent->setContents(result);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
2011-02-10 13:43:02 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-25 13:43:06 +01:00
|
|
|
// Binary/configuration check logic.
|
|
|
|
|
|
2015-05-27 13:59:56 +02:00
|
|
|
static QString gdbBinary(const DebuggerRunParameters &sp)
|
2011-02-25 13:43:06 +01:00
|
|
|
{
|
|
|
|
|
// 1) Environment.
|
|
|
|
|
const QByteArray envBinary = qgetenv("QTC_DEBUGGER_PATH");
|
|
|
|
|
if (!envBinary.isEmpty())
|
|
|
|
|
return QString::fromLocal8Bit(envBinary);
|
2012-06-28 10:00:04 +02:00
|
|
|
// 2) Command from profile.
|
|
|
|
|
return sp.debuggerCommand;
|
2011-02-25 13:43:06 +01:00
|
|
|
}
|
|
|
|
|
|
2015-05-27 13:59:56 +02:00
|
|
|
static SourcePathMap mergeStartParametersSourcePathMap(const DebuggerRunParameters &sp,
|
2014-09-11 22:22:51 +03:00
|
|
|
const SourcePathMap &in)
|
2013-02-06 16:17:30 +01:00
|
|
|
{
|
|
|
|
|
// Do not overwrite user settings.
|
2014-09-11 22:22:51 +03:00
|
|
|
SourcePathMap rc = sp.sourcePathMap;
|
|
|
|
|
for (auto it = in.constBegin(), end = in.constEnd(); it != end; ++it)
|
2013-02-06 16:17:30 +01:00
|
|
|
rc.insert(it.key(), it.value());
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-21 11:09:38 +02:00
|
|
|
//
|
|
|
|
|
// Starting up & shutting down
|
|
|
|
|
//
|
|
|
|
|
|
2012-06-05 19:55:32 +02:00
|
|
|
void GdbEngine::startGdb(const QStringList &args)
|
2009-09-21 11:09:38 +02:00
|
|
|
{
|
2011-06-06 18:17:51 +02:00
|
|
|
const QByteArray tests = qgetenv("QTC_DEBUGGER_TESTS");
|
|
|
|
|
foreach (const QByteArray &test, tests.split(','))
|
|
|
|
|
m_testCases.insert(test.toInt());
|
|
|
|
|
foreach (int test, m_testCases)
|
|
|
|
|
showMessage(_("ENABLING TEST CASE: " + QByteArray::number(test)));
|
|
|
|
|
|
2015-06-08 12:10:11 +02:00
|
|
|
m_gdbProc.disconnect(); // From any previous runs
|
2009-10-20 11:02:16 +02:00
|
|
|
|
2015-05-27 13:59:56 +02:00
|
|
|
const DebuggerRunParameters &rp = runParameters();
|
|
|
|
|
m_gdb = gdbBinary(rp);
|
2010-04-08 16:55:25 +02:00
|
|
|
if (m_gdb.isEmpty()) {
|
2012-06-13 10:15:56 +02:00
|
|
|
handleGdbStartFailed();
|
2010-06-16 11:08:54 +02:00
|
|
|
handleAdapterStartFailed(
|
2015-05-27 13:59:56 +02:00
|
|
|
msgNoGdbBinaryForToolChain(rp.toolChainAbi),
|
2012-12-29 01:31:08 +01:00
|
|
|
Constants::DEBUGGER_COMMON_SETTINGS_ID);
|
2012-06-05 11:05:58 +02:00
|
|
|
return;
|
2010-04-08 16:55:25 +02:00
|
|
|
}
|
2009-10-20 11:02:16 +02:00
|
|
|
QStringList gdbArgs;
|
|
|
|
|
gdbArgs << _("-i");
|
|
|
|
|
gdbArgs << _("mi");
|
2014-07-28 14:23:52 +02:00
|
|
|
if (!boolSetting(LoadGdbInit))
|
2010-12-21 11:52:21 +01:00
|
|
|
gdbArgs << _("-n");
|
2009-10-20 11:02:16 +02:00
|
|
|
gdbArgs += args;
|
2010-12-02 17:43:14 +01:00
|
|
|
|
2015-06-08 12:10:11 +02:00
|
|
|
connect(&m_gdbProc, static_cast<void (QProcess::*)(QProcess::ProcessError)>(&QProcess::error),
|
|
|
|
|
this, &GdbEngine::handleGdbError);
|
|
|
|
|
connect(&m_gdbProc, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
|
|
|
|
|
this, &GdbEngine::handleGdbFinished);
|
|
|
|
|
connect(&m_gdbProc, &QtcProcess::readyReadStandardOutput, this, &GdbEngine::readGdbStandardOutput);
|
|
|
|
|
connect(&m_gdbProc, &QtcProcess::readyReadStandardError, this, &GdbEngine::readGdbStandardError);
|
2009-10-20 11:02:16 +02:00
|
|
|
|
2014-08-23 01:19:53 +02:00
|
|
|
showMessage(_("STARTING ") + m_gdb + _(" ") + gdbArgs.join(QLatin1Char(' ')));
|
2015-06-08 12:10:11 +02:00
|
|
|
m_gdbProc.setCommand(m_gdb, QtcProcess::joinArgs(gdbArgs));
|
|
|
|
|
m_gdbProc.start();
|
2010-03-10 15:51:31 +01:00
|
|
|
|
2015-06-08 12:10:11 +02:00
|
|
|
if (!m_gdbProc.waitForStarted()) {
|
2012-06-13 10:15:56 +02:00
|
|
|
handleGdbStartFailed();
|
2010-09-06 14:26:19 +02:00
|
|
|
const QString msg = errorMessage(QProcess::FailedToStart);
|
2012-06-05 19:55:32 +02:00
|
|
|
handleAdapterStartFailed(msg);
|
2012-06-05 11:05:58 +02:00
|
|
|
return;
|
2010-03-10 15:51:31 +01:00
|
|
|
}
|
|
|
|
|
|
2010-06-14 18:19:02 +02:00
|
|
|
showMessage(_("GDB STARTED, INITIALIZING IT"));
|
2015-02-06 01:26:47 +01:00
|
|
|
postCommand("show version", NoFlags, CB(handleShowVersion));
|
2012-04-17 10:00:40 +02:00
|
|
|
//postCommand("-list-features", CB(handleListFeatures));
|
2015-02-06 01:26:47 +01:00
|
|
|
postCommand("show debug-file-directory", NoFlags, CB(handleDebugInfoLocation));
|
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");
|
2011-08-17 14:55:11 +02:00
|
|
|
postCommand("set print object on");
|
2010-01-05 16:51:55 +01:00
|
|
|
//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");
|
2012-04-17 10:00:40 +02:00
|
|
|
|
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
|
|
|
|
|
|
|
|
// 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>'()
|
2013-10-22 18:58:19 +02:00
|
|
|
// /build/buildd/gdb-6.8/gdb/valops.c:2069: internal-error
|
|
|
|
|
// This seems to be fixed, however, with 'on' it seems to _require_
|
|
|
|
|
// explicit casting of function pointers:
|
|
|
|
|
// GNU gdb (GDB) 7.5.91.20130417-cvs-ubuntu
|
|
|
|
|
// (gdb) p &Myns::QMetaType::typeName -> $1 = (const char *(*)(int)) 0xb7cf73b0 <Myns::QMetaType::typeName(int)>
|
|
|
|
|
// (gdb) p Myns::QMetaType::typeName(1024) -> 31^error,msg="Couldn't find method Myns::QMetaType::typeName"
|
|
|
|
|
// But we can work around on the dumper side. So let's use the default (i.e. 'on')
|
|
|
|
|
//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
|
|
|
|
2010-01-05 16:51:55 +01:00
|
|
|
postCommand("set unwindonsignal on");
|
|
|
|
|
postCommand("set width 0");
|
|
|
|
|
postCommand("set height 0");
|
2012-01-24 20:03:50 +01:00
|
|
|
|
2013-08-30 14:27:00 +02:00
|
|
|
// FIXME: Provide proper Gui settings for these:
|
2012-11-08 17:23:47 +01:00
|
|
|
//postCommand("set breakpoint always-inserted on", ConsoleCommand);
|
2012-01-24 20:03:50 +01:00
|
|
|
// displaced-stepping does not work in Thumb mode.
|
|
|
|
|
//postCommand("set displaced-stepping on");
|
2012-11-08 17:23:47 +01:00
|
|
|
//postCommand("set trust-readonly-sections on", ConsoleCommand);
|
2013-08-30 14:27:00 +02:00
|
|
|
//postCommand("set remotecache on", ConsoleCommand);
|
2012-03-09 15:04:59 +01:00
|
|
|
//postCommand("set non-stop on", ConsoleCommand);
|
2011-02-03 16:49:28 +01:00
|
|
|
|
2012-06-13 10:15:56 +02:00
|
|
|
showStatusMessage(tr("Setting up inferior..."));
|
|
|
|
|
|
|
|
|
|
// Addint executable to modules list.
|
|
|
|
|
Module module;
|
|
|
|
|
module.startAddress = 0;
|
|
|
|
|
module.endAddress = 0;
|
2015-05-27 13:59:56 +02:00
|
|
|
module.modulePath = rp.executable;
|
2012-06-13 10:15:56 +02:00
|
|
|
module.moduleName = QLatin1String("<executable>");
|
|
|
|
|
modulesHandler()->updateModule(module);
|
|
|
|
|
|
|
|
|
|
// Apply source path mappings from global options.
|
2012-08-09 04:25:13 +02:00
|
|
|
//showMessage(_("Assuming Qt is installed at %1").arg(qtInstallPath));
|
2012-06-13 10:15:56 +02:00
|
|
|
const SourcePathMap sourcePathMap =
|
2015-05-27 13:59:56 +02:00
|
|
|
DebuggerSourcePathMappingWidget::mergePlatformQtPath(rp,
|
2014-10-22 13:04:47 +02:00
|
|
|
Internal::globalDebuggerOptions()->sourcePathMap);
|
2013-02-06 16:17:30 +01:00
|
|
|
const SourcePathMap completeSourcePathMap =
|
2015-05-27 13:59:56 +02:00
|
|
|
mergeStartParametersSourcePathMap(rp, sourcePathMap);
|
2014-09-11 22:22:51 +03:00
|
|
|
for (auto it = completeSourcePathMap.constBegin(), cend = completeSourcePathMap.constEnd();
|
|
|
|
|
it != cend;
|
|
|
|
|
++it) {
|
2012-06-13 10:15:56 +02:00
|
|
|
postCommand("set substitute-path " + it.key().toLocal8Bit()
|
|
|
|
|
+ " " + it.value().toLocal8Bit());
|
2014-09-11 22:22:51 +03:00
|
|
|
}
|
2012-06-13 10:15:56 +02:00
|
|
|
|
|
|
|
|
// Spaces just will not work.
|
2015-05-27 13:59:56 +02:00
|
|
|
foreach (const QString &src, rp.debugSourceLocation) {
|
2013-04-19 15:01:55 +02:00
|
|
|
if (QDir(src).exists())
|
|
|
|
|
postCommand("directory " + src.toLocal8Bit());
|
|
|
|
|
else
|
|
|
|
|
showMessage(_("# directory does not exist: ") + src, LogInput);
|
|
|
|
|
}
|
2012-06-13 10:15:56 +02:00
|
|
|
|
2015-05-27 13:59:56 +02:00
|
|
|
const QByteArray sysroot = rp.sysRoot.toLocal8Bit();
|
2012-06-13 10:15:56 +02:00
|
|
|
if (!sysroot.isEmpty()) {
|
|
|
|
|
postCommand("set sysroot " + sysroot);
|
|
|
|
|
// sysroot is not enough to correctly locate the sources, so explicitly
|
|
|
|
|
// relocate the most likely place for the debug source
|
|
|
|
|
postCommand("set substitute-path /usr/src " + sysroot + "/usr/src");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//QByteArray ba = QFileInfo(sp.dumperLibrary).path().toLocal8Bit();
|
|
|
|
|
//if (!ba.isEmpty())
|
|
|
|
|
// postCommand("set solib-search-path " + ba);
|
2012-04-17 10:00:40 +02:00
|
|
|
if (attemptQuickStart()) {
|
|
|
|
|
postCommand("set auto-solib-add off", ConsoleCommand);
|
|
|
|
|
} else {
|
|
|
|
|
m_fullStartDone = true;
|
|
|
|
|
postCommand("set auto-solib-add on", ConsoleCommand);
|
|
|
|
|
}
|
2009-09-21 11:09:38 +02:00
|
|
|
|
2014-07-28 14:23:52 +02:00
|
|
|
if (boolSetting(MultiInferior)) {
|
2012-11-06 15:21:42 +01:00
|
|
|
//postCommand("set follow-exec-mode new");
|
|
|
|
|
postCommand("set detach-on-fork off");
|
|
|
|
|
}
|
2013-01-09 14:51:49 +01:00
|
|
|
|
2014-01-28 11:27:13 +01:00
|
|
|
// Finally, set up Python.
|
|
|
|
|
// We need to guarantee a roundtrip before the adapter proceeds.
|
2013-01-09 14:51:49 +01:00
|
|
|
// Make sure this stays the last command in startGdb().
|
2013-05-28 12:12:42 +02:00
|
|
|
// Don't use ConsoleCommand, otherwise Mac won't markup the output.
|
2014-01-28 11:27:13 +01:00
|
|
|
const QByteArray dumperSourcePath =
|
2014-06-05 14:15:54 +02:00
|
|
|
ICore::resourcePath().toLocal8Bit() + "/debugger/";
|
2012-06-05 11:05:58 +02:00
|
|
|
|
2015-01-14 16:58:10 +01:00
|
|
|
if (terminal()->isUsable())
|
|
|
|
|
postCommand("set inferior-tty " + terminal()->slaveDevice());
|
|
|
|
|
|
2014-01-28 11:27:13 +01:00
|
|
|
const QFileInfo gdbBinaryFile(m_gdb);
|
|
|
|
|
const QByteArray uninstalledData = gdbBinaryFile.absolutePath().toLocal8Bit()
|
|
|
|
|
+ "/data-directory/python";
|
|
|
|
|
|
2015-08-14 17:15:32 +02:00
|
|
|
const GdbCommandFlags flags = ConsoleCommand;
|
2014-01-28 11:27:13 +01:00
|
|
|
postCommand("python sys.path.insert(1, '" + dumperSourcePath + "')", flags);
|
|
|
|
|
postCommand("python sys.path.append('" + uninstalledData + "')", flags);
|
2015-01-20 20:19:55 +01:00
|
|
|
postCommand("python from gdbbridge import *", flags);
|
|
|
|
|
|
|
|
|
|
const QString path = stringSetting(ExtraDumperFile);
|
2015-06-22 08:00:47 +03:00
|
|
|
if (!path.isEmpty() && QFileInfo(path).isReadable()) {
|
2015-02-11 12:20:21 +01:00
|
|
|
DebuggerCommand cmd("addDumperModule");
|
|
|
|
|
cmd.arg("path", path.toUtf8());
|
|
|
|
|
runCommand(cmd);
|
|
|
|
|
}
|
2015-01-20 20:19:55 +01:00
|
|
|
|
|
|
|
|
const QString commands = stringSetting(ExtraDumperCommands);
|
|
|
|
|
if (!commands.isEmpty())
|
|
|
|
|
postCommand(commands.toLocal8Bit(), flags);
|
|
|
|
|
|
2015-03-18 16:48:57 +01:00
|
|
|
runCommand(DebuggerCommand("loadDumpers", flags, CB(handlePythonSetup)));
|
2012-06-13 10:15:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::handleGdbStartFailed()
|
|
|
|
|
{
|
2011-12-05 08:53:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::loadInitScript()
|
|
|
|
|
{
|
2015-05-27 13:59:56 +02:00
|
|
|
const QString script = runParameters().overrideStartScript;
|
2011-11-01 17:56:32 +01:00
|
|
|
if (!script.isEmpty()) {
|
|
|
|
|
if (QFileInfo(script).isReadable()) {
|
|
|
|
|
postCommand("source " + script.toLocal8Bit());
|
2009-09-21 11:09:38 +02:00
|
|
|
} else {
|
2015-02-03 23:58:49 +02:00
|
|
|
AsynchronousMessageBox::warning(
|
2009-09-21 11:09:38 +02:00
|
|
|
tr("Cannot find debugger initialization script"),
|
2014-04-17 14:09:47 +02:00
|
|
|
tr("The debugger settings point to a script file at \"%1\" "
|
2009-09-21 11:09:38 +02:00
|
|
|
"which is not accessible. If a script file is not needed, "
|
|
|
|
|
"consider clearing that entry to avoid this warning. "
|
2011-11-01 17:56:32 +01:00
|
|
|
).arg(script));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2014-07-28 14:23:52 +02:00
|
|
|
const QString commands = stringSetting(GdbStartupCommands);
|
2011-12-05 08:53:51 +02:00
|
|
|
if (!commands.isEmpty())
|
2011-11-01 17:56:32 +01:00
|
|
|
postCommand(commands.toLocal8Bit());
|
2009-09-21 11:09:38 +02:00
|
|
|
}
|
2010-07-15 13:23:44 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-15 10:38:15 +03:00
|
|
|
void GdbEngine::setEnvironmentVariables()
|
|
|
|
|
{
|
2015-07-16 11:12:43 +02:00
|
|
|
Environment sysEnv = Environment::systemEnvironment();
|
|
|
|
|
Environment runEnv = runParameters().environment;
|
|
|
|
|
foreach (const EnvironmentItem &item, sysEnv.diff(runEnv)) {
|
|
|
|
|
if (item.unset)
|
|
|
|
|
postCommand("unset environment " + item.name.toUtf8());
|
|
|
|
|
else
|
|
|
|
|
postCommand("-gdb-set environment " + item.name.toUtf8() + '=' + item.value.toUtf8());
|
2015-07-15 10:38:15 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-10 12:40:50 +01:00
|
|
|
void GdbEngine::reloadDebuggingHelpers()
|
|
|
|
|
{
|
2015-03-18 16:48:57 +01:00
|
|
|
runCommand("reloadDumpers");
|
2015-01-22 13:06:38 +01:00
|
|
|
reloadLocals();
|
2013-01-10 12:40:50 +01: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:
|
2013-10-26 21:57:20 +03:00
|
|
|
//m_gdbProc->kill();
|
2010-10-27 15:39:25 +02:00
|
|
|
//notifyEngineIll();
|
2015-02-03 23:58:49 +02:00
|
|
|
AsynchronousMessageBox::critical(tr("GDB I/O Error"), msg);
|
2009-10-20 11:02:16 +02:00
|
|
|
break;
|
|
|
|
|
}
|
2009-09-21 11:09:38 +02:00
|
|
|
}
|
|
|
|
|
|
2015-03-05 11:11:32 +01:00
|
|
|
void GdbEngine::handleGdbFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
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
|
|
|
|
2015-03-05 11:11:32 +01:00
|
|
|
notifyDebuggerProcessFinished(exitCode, exitStatus, QLatin1String("GDB"));
|
2009-09-21 11:09:38 +02:00
|
|
|
}
|
|
|
|
|
|
2011-10-31 16:15:48 +01:00
|
|
|
void GdbEngine::abortDebugger()
|
|
|
|
|
{
|
|
|
|
|
if (targetState() == DebuggerFinished) {
|
|
|
|
|
// We already tried. Try harder.
|
|
|
|
|
showMessage(_("ABORTING DEBUGGER. SECOND TIME."));
|
2015-06-08 12:10:11 +02:00
|
|
|
m_gdbProc.kill();
|
2011-10-31 16:15:48 +01:00
|
|
|
} else {
|
|
|
|
|
// Be friendly the first time. This will change targetState().
|
|
|
|
|
showMessage(_("ABORTING DEBUGGER. FIRST TIME."));
|
|
|
|
|
quitDebugger();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-05 17:22:51 +02:00
|
|
|
void GdbEngine::resetInferior()
|
|
|
|
|
{
|
2015-05-27 13:59:56 +02:00
|
|
|
if (!runParameters().commandsForReset.isEmpty()) {
|
|
|
|
|
QByteArray commands = globalMacroExpander()->expand(runParameters().commandsForReset);
|
2014-10-15 14:45:31 +02:00
|
|
|
foreach (QByteArray command, commands.split('\n')) {
|
2014-06-05 17:22:51 +02:00
|
|
|
command = command.trimmed();
|
2015-08-14 17:15:32 +02:00
|
|
|
if (!command.isEmpty())
|
|
|
|
|
postCommand(command, ConsoleCommand|NeedsStop);
|
2014-06-05 17:22:51 +02:00
|
|
|
}
|
|
|
|
|
}
|
2015-07-07 15:13:10 +02:00
|
|
|
m_rerunPending = true;
|
2014-06-05 17:22:51 +02:00
|
|
|
requestInterruptInferior();
|
|
|
|
|
runEngine();
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-05 14:15:54 +02:00
|
|
|
void GdbEngine::handleAdapterStartFailed(const QString &msg, Id settingsIdHint)
|
2009-09-21 11:09:38 +02:00
|
|
|
{
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(EngineSetupOk);
|
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");
|
2012-12-29 01:31:08 +01:00
|
|
|
if (!settingsIdHint.isValid()) {
|
2014-06-05 14:15:54 +02:00
|
|
|
ICore::showWarningWithOptions(title, msg);
|
2010-02-25 16:05:45 +01:00
|
|
|
} else {
|
2015-02-23 11:07:38 +01:00
|
|
|
ICore::showWarningWithOptions(title, msg, QString(), 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
|
|
|
}
|
|
|
|
|
|
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
|
|
|
{
|
2015-05-27 13:59:56 +02:00
|
|
|
const DebuggerRunParameters &rp = runParameters();
|
2011-03-18 10:02:07 +01:00
|
|
|
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(InferiorSetupRequested);
|
2011-03-04 16:21:57 +01:00
|
|
|
|
2015-05-27 13:59:56 +02:00
|
|
|
if (!rp.commandsAfterConnect.isEmpty()) {
|
|
|
|
|
QByteArray commands = globalMacroExpander()->expand(rp.commandsAfterConnect);
|
2014-10-15 14:45:31 +02:00
|
|
|
foreach (QByteArray command, commands.split('\n')) {
|
2013-09-12 18:46:35 +02:00
|
|
|
postCommand(command);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-26 15:24:27 +02:00
|
|
|
//postCommand("set follow-exec-mode new");
|
2015-05-27 13:59:56 +02:00
|
|
|
if (rp.breakOnMain) {
|
2012-05-18 02:28:41 +02:00
|
|
|
QByteArray cmd = "tbreak ";
|
2015-05-27 13:59:56 +02:00
|
|
|
cmd += rp.toolChainAbi.os() == Abi::WindowsOS ? "qMain" : "main";
|
2012-05-18 02:28:41 +02:00
|
|
|
postCommand(cmd);
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-21 14:41:17 +01:00
|
|
|
// Initial attempt to set breakpoints.
|
2015-05-27 13:59:56 +02:00
|
|
|
if (rp.startMode != AttachCore) {
|
2010-12-21 14:41:17 +01:00
|
|
|
showStatusMessage(tr("Setting breakpoints..."));
|
|
|
|
|
showMessage(tr("Setting breakpoints..."));
|
|
|
|
|
attemptBreakpointSynchronization();
|
|
|
|
|
}
|
2009-10-13 09:53:15 +02:00
|
|
|
|
2015-02-06 10:30:25 +01:00
|
|
|
if (m_commandForToken.isEmpty()) {
|
2010-07-08 18:10:50 +02:00
|
|
|
finishInferiorSetup();
|
2009-10-20 11:02:16 +02:00
|
|
|
} else {
|
2011-07-29 12:00:11 +02:00
|
|
|
QTC_CHECK(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
|
|
|
{
|
2015-02-24 15:30:42 +01:00
|
|
|
CHECK_STATE(InferiorSetupRequested);
|
2011-04-18 14:30:52 +02:00
|
|
|
|
2015-05-27 13:59:56 +02:00
|
|
|
if (runParameters().startMode == AttachCore) {
|
2011-04-18 14:30:52 +02:00
|
|
|
notifyInferiorSetupOk(); // No breakpoints in core files.
|
2011-05-30 16:01:50 +02:00
|
|
|
} else {
|
2014-07-28 14:23:52 +02:00
|
|
|
if (boolSetting(BreakOnAbort))
|
2012-05-08 18:01:28 +03:00
|
|
|
postCommand("-break-insert -f abort");
|
2014-07-28 14:23:52 +02:00
|
|
|
if (boolSetting(BreakOnWarning)) {
|
2011-05-30 16:01:50 +02:00
|
|
|
postCommand("-break-insert -f '" + qtNamespace() + "qWarning'");
|
2012-12-20 15:26:51 +01:00
|
|
|
postCommand("-break-insert -f '" + qtNamespace() + "QMessageLogger::warning'");
|
|
|
|
|
}
|
2014-07-28 14:23:52 +02:00
|
|
|
if (boolSetting(BreakOnFatal)) {
|
2015-02-06 01:26:47 +01:00
|
|
|
postCommand("-break-insert -f '" + qtNamespace() + "qFatal'", NoFlags,
|
|
|
|
|
[this](const DebuggerResponse &r) { handleBreakOnQFatal(r, false); });
|
|
|
|
|
postCommand("-break-insert -f '" + qtNamespace() + "QMessageLogger::fatal'", NoFlags,
|
|
|
|
|
[this](const DebuggerResponse &r) { handleBreakOnQFatal(r, true); });
|
2012-12-20 15:26:51 +01:00
|
|
|
} else {
|
2011-07-13 16:52:28 +02:00
|
|
|
notifyInferiorSetupOk();
|
2012-12-20 15:26:51 +01:00
|
|
|
}
|
2011-05-30 16:01:50 +02:00
|
|
|
}
|
2011-03-21 15:04:31 +01:00
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleDebugInfoLocation(const DebuggerResponse &response)
|
2014-02-27 12:54:20 +01:00
|
|
|
{
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultDone) {
|
2015-05-27 13:59:56 +02:00
|
|
|
const QByteArray debugInfoLocation = runParameters().debugInfoLocation.toLocal8Bit();
|
2014-02-27 12:54:20 +01:00
|
|
|
if (QFile::exists(QString::fromLocal8Bit(debugInfoLocation))) {
|
|
|
|
|
const QByteArray curDebugInfoLocations = response.consoleStreamOutput.split('"').value(1);
|
|
|
|
|
if (curDebugInfoLocations.isEmpty()) {
|
|
|
|
|
postCommand("set debug-file-directory " + debugInfoLocation);
|
|
|
|
|
} else {
|
|
|
|
|
postCommand("set debug-file-directory " + debugInfoLocation
|
|
|
|
|
+ HostOsInfo::pathListSeparator().toLatin1()
|
|
|
|
|
+ curDebugInfoLocations);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-06 01:26:47 +01:00
|
|
|
void GdbEngine::handleBreakOnQFatal(const DebuggerResponse &response, bool continueSetup)
|
2011-03-21 15:04:31 +01:00
|
|
|
{
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultDone) {
|
2013-05-03 18:26:10 +02:00
|
|
|
GdbMi bkpt = response.data["bkpt"];
|
|
|
|
|
GdbMi number = bkpt["number"];
|
2011-06-24 16:25:30 +02:00
|
|
|
BreakpointResponseId rid(number.data());
|
|
|
|
|
if (rid.isValid()) {
|
|
|
|
|
m_qFatalBreakpointResponseId = rid;
|
2011-03-21 15:04:31 +01:00
|
|
|
postCommand("-break-commands " + number.data() + " return");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Continue setup.
|
2015-02-06 01:26:47 +01:00
|
|
|
if (continueSetup)
|
2012-12-20 15:26:51 +01:00
|
|
|
notifyInferiorSetupOk();
|
2010-07-08 18:10:50 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-09 17:07:59 +02:00
|
|
|
void GdbEngine::notifyInferiorSetupFailed(const QString &msg)
|
2009-09-21 17:35:19 +02:00
|
|
|
{
|
2013-10-17 14:52:10 +02:00
|
|
|
showStatusMessage(tr("Failed to start application:") + QLatin1Char(' ') + 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"));
|
2015-02-03 23:58:49 +02:00
|
|
|
AsynchronousMessageBox::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.
|
2011-03-21 16:34:33 +01:00
|
|
|
if (state() == EngineSetupRequested)
|
|
|
|
|
notifyEngineSetupFailed();
|
|
|
|
|
else
|
|
|
|
|
notifyEngineIll();
|
2009-09-21 17:35:19 +02:00
|
|
|
|
2009-10-20 11:02:16 +02:00
|
|
|
// No point in being friendly here ...
|
2015-06-08 12:10:11 +02:00
|
|
|
m_gdbProc.kill();
|
2009-10-20 11:02:16 +02:00
|
|
|
|
2009-10-07 10:25:26 +02:00
|
|
|
if (!msg.isEmpty())
|
2015-02-03 23:58:49 +02:00
|
|
|
AsynchronousMessageBox::critical(tr("Adapter crashed"), msg);
|
2009-09-21 17:35:19 +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
|
|
|
}
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
void GdbEngine::handleCreateFullBacktrace(const DebuggerResponse &response)
|
2010-03-29 18:44:02 +02:00
|
|
|
{
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultDone) {
|
2014-10-22 13:04:47 +02:00
|
|
|
Internal::openTextEditor(_("Backtrace $"),
|
2011-07-05 17:57:57 +02:00
|
|
|
_(response.consoleStreamOutput + response.logStreamOutput));
|
2010-03-29 18:44:02 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-11 09:46:39 +02:00
|
|
|
void GdbEngine::resetCommandQueue()
|
|
|
|
|
{
|
2010-11-16 12:42:57 +01:00
|
|
|
m_commandTimer.stop();
|
2015-02-06 10:30:25 +01:00
|
|
|
if (!m_commandForToken.isEmpty()) {
|
2010-08-11 09:46:39 +02:00
|
|
|
QString msg;
|
|
|
|
|
QTextStream ts(&msg);
|
|
|
|
|
ts << "RESETING COMMAND QUEUE. LEFT OVER TOKENS: ";
|
2015-02-06 10:30:25 +01:00
|
|
|
foreach (const DebuggerCommand &cmd, m_commandForToken)
|
|
|
|
|
ts << "CMD:" << cmd.function;
|
|
|
|
|
m_commandForToken.clear();
|
2010-08-11 09:46:39 +02:00
|
|
|
showMessage(msg);
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-03-29 18:44:02 +02:00
|
|
|
|
2011-06-24 16:25:30 +02:00
|
|
|
bool GdbEngine::isQFatalBreakpoint(const BreakpointResponseId &id) const
|
2011-01-24 13:37:07 +01:00
|
|
|
{
|
2011-06-24 16:25:30 +02:00
|
|
|
return id.isValid() && m_qFatalBreakpointResponseId == id;
|
2011-01-24 13:37:07 +01:00
|
|
|
}
|
2011-01-14 17:08:59 +01:00
|
|
|
|
2011-06-24 16:25:30 +02:00
|
|
|
bool GdbEngine::isHiddenBreakpoint(const BreakpointResponseId &id) const
|
2011-03-21 15:04:31 +01:00
|
|
|
{
|
2014-12-12 10:20:07 +01:00
|
|
|
return isQFatalBreakpoint(id);
|
2011-03-21 15:04:31 +01:00
|
|
|
}
|
|
|
|
|
|
2012-03-09 15:04:59 +01:00
|
|
|
bool GdbEngine::usesExecInterrupt() const
|
|
|
|
|
{
|
2015-05-27 13:59:56 +02:00
|
|
|
DebuggerStartMode mode = runParameters().startMode;
|
2012-06-05 10:25:48 +02:00
|
|
|
return (mode == AttachToRemoteServer || mode == AttachToRemoteProcess)
|
2014-07-28 14:23:52 +02:00
|
|
|
&& boolSetting(TargetAsync);
|
2012-03-09 15:04:59 +01:00
|
|
|
}
|
|
|
|
|
|
2011-06-06 18:17:51 +02:00
|
|
|
void GdbEngine::scheduleTestResponse(int testCase, const QByteArray &response)
|
|
|
|
|
{
|
2015-05-27 13:59:56 +02:00
|
|
|
if (!m_testCases.contains(testCase) && runParameters().testCase != testCase)
|
2011-06-06 18:17:51 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
int token = currentToken() + 1;
|
2014-04-17 14:09:47 +02:00
|
|
|
showMessage(_("SCHEDULING TEST RESPONSE (CASE: %1, TOKEN: %2, RESPONSE: %3)")
|
2011-06-06 18:17:51 +02:00
|
|
|
.arg(testCase).arg(token).arg(_(response)));
|
|
|
|
|
m_scheduledTestResponses[token] = response;
|
|
|
|
|
}
|
|
|
|
|
|
2012-03-30 17:36:44 +02:00
|
|
|
void GdbEngine::requestDebugInformation(const DebugInfoTask &task)
|
|
|
|
|
{
|
|
|
|
|
QProcess::startDetached(task.command);
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-17 10:00:40 +02:00
|
|
|
bool GdbEngine::attemptQuickStart() const
|
|
|
|
|
{
|
|
|
|
|
// Don't try if the user does not ask for it.
|
2014-07-28 14:23:52 +02:00
|
|
|
if (!boolSetting(AttemptQuickStart))
|
2012-04-17 10:00:40 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// Don't try if there are breakpoints we might be able to handle.
|
|
|
|
|
BreakHandler *handler = breakHandler();
|
2015-01-10 01:07:01 +01:00
|
|
|
foreach (Breakpoint bp, handler->unclaimedBreakpoints()) {
|
|
|
|
|
if (acceptsBreakpoint(bp))
|
2012-04-17 10:00:40 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-13 10:15:56 +02:00
|
|
|
void GdbEngine::write(const QByteArray &data)
|
|
|
|
|
{
|
2015-06-08 12:10:11 +02:00
|
|
|
m_gdbProc.write(data);
|
2012-06-13 10:15:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GdbEngine::prepareCommand()
|
|
|
|
|
{
|
2014-02-05 10:43:21 +01:00
|
|
|
if (HostOsInfo::isWindowsHost()) {
|
2015-05-27 13:59:56 +02:00
|
|
|
DebuggerRunParameters &rp = runParameters();
|
2014-02-05 10:43:21 +01:00
|
|
|
QtcProcess::SplitError perr;
|
2015-05-27 13:59:56 +02:00
|
|
|
rp.processArgs = QtcProcess::prepareArgs(rp.processArgs, &perr,
|
2014-06-05 14:15:54 +02:00
|
|
|
HostOsInfo::hostOs(),
|
2015-07-15 10:38:15 +03:00
|
|
|
nullptr, &rp.workingDirectory).toWindowsArgs();
|
2014-06-05 14:15:54 +02:00
|
|
|
if (perr != QtcProcess::SplitOk) {
|
2014-02-05 10:43:21 +01:00
|
|
|
// perr == BadQuoting is never returned on Windows
|
|
|
|
|
// FIXME? QTCREATORBUG-2809
|
|
|
|
|
handleAdapterStartFailed(QCoreApplication::translate("DebuggerEngine", // Same message in CdbEngine
|
2014-06-05 14:15:54 +02:00
|
|
|
"Debugging complex command lines is currently not supported on Windows."), Id());
|
2014-02-05 10:43:21 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
2012-06-13 10:15:56 +02:00
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString GdbEngine::msgGdbStopFailed(const QString &why)
|
|
|
|
|
{
|
|
|
|
|
return tr("The gdb process could not be stopped:\n%1").arg(why);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString GdbEngine::msgInferiorStopFailed(const QString &why)
|
|
|
|
|
{
|
|
|
|
|
return tr("Application process could not be stopped:\n%1").arg(why);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString GdbEngine::msgInferiorSetupOk()
|
|
|
|
|
{
|
|
|
|
|
return tr("Application started");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString GdbEngine::msgInferiorRunOk()
|
|
|
|
|
{
|
|
|
|
|
return tr("Application running");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString GdbEngine::msgAttachedToStoppedInferior()
|
|
|
|
|
{
|
|
|
|
|
return tr("Attached to stopped application");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString GdbEngine::msgConnectRemoteServerFailed(const QString &why)
|
|
|
|
|
{
|
|
|
|
|
return tr("Connecting to remote server failed:\n%1").arg(why);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GdbEngine::interruptLocalInferior(qint64 pid)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(state() == InferiorStopRequested, qDebug() << state(); return);
|
|
|
|
|
if (pid <= 0) {
|
|
|
|
|
showMessage(QLatin1String("TRYING TO INTERRUPT INFERIOR BEFORE PID WAS OBTAINED"), LogError);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
QString errorMessage;
|
|
|
|
|
if (interruptProcess(pid, GdbEngineType, &errorMessage)) {
|
|
|
|
|
showMessage(QLatin1String("Interrupted ") + QString::number(pid));
|
|
|
|
|
} else {
|
|
|
|
|
showMessage(errorMessage, LogError);
|
|
|
|
|
notifyInferiorStopFailed();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-24 16:33:17 +01:00
|
|
|
QByteArray GdbEngine::dotEscape(QByteArray str)
|
|
|
|
|
{
|
|
|
|
|
str.replace(' ', '.');
|
|
|
|
|
str.replace('\\', '.');
|
|
|
|
|
str.replace('/', '.');
|
|
|
|
|
return str;
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-04 01:34:51 +01:00
|
|
|
void GdbEngine::debugLastCommand()
|
|
|
|
|
{
|
2015-02-11 17:51:15 +01:00
|
|
|
runCommand(m_lastDebuggableCommand);
|
2014-01-04 01:34:51 +01:00
|
|
|
}
|
|
|
|
|
|
2009-09-23 09:54:21 +02:00
|
|
|
//
|
|
|
|
|
// Factory
|
|
|
|
|
//
|
|
|
|
|
|
2015-05-27 13:59:56 +02:00
|
|
|
DebuggerEngine *createGdbEngine(const DebuggerRunParameters &rp)
|
2010-06-16 11:08:54 +02:00
|
|
|
{
|
2015-05-27 13:59:56 +02:00
|
|
|
switch (rp.startMode) {
|
2012-06-13 10:15:56 +02:00
|
|
|
case AttachCore:
|
2015-05-27 13:59:56 +02:00
|
|
|
return new GdbCoreEngine(rp);
|
2012-11-05 15:16:12 +01:00
|
|
|
case StartRemoteProcess:
|
2012-06-13 10:15:56 +02:00
|
|
|
case AttachToRemoteServer:
|
2015-05-27 13:59:56 +02:00
|
|
|
return new GdbRemoteServerEngine(rp);
|
2012-06-13 10:15:56 +02:00
|
|
|
case AttachExternal:
|
2015-05-27 13:59:56 +02:00
|
|
|
return new GdbAttachEngine(rp);
|
2012-06-13 10:15:56 +02:00
|
|
|
default:
|
2015-05-27 13:59:56 +02:00
|
|
|
if (rp.useTerminal)
|
|
|
|
|
return new GdbTermEngine(rp);
|
|
|
|
|
return new GdbPlainEngine(rp);
|
2012-06-13 10:15:56 +02:00
|
|
|
}
|
2010-06-16 11:08:54 +02:00
|
|
|
}
|
|
|
|
|
|
2015-03-26 13:03:38 +01:00
|
|
|
void GdbEngine::doUpdateLocals(const UpdateParameters ¶ms)
|
2013-11-27 16:10:14 +01:00
|
|
|
{
|
|
|
|
|
m_pendingBreakpointRequests = 0;
|
|
|
|
|
|
2015-07-06 09:46:08 +02:00
|
|
|
watchHandler()->notifyUpdateStarted(params.partialVariables());
|
2015-06-16 16:38:14 +02:00
|
|
|
|
2015-02-11 17:51:15 +01:00
|
|
|
DebuggerCommand cmd("showData");
|
2015-02-12 11:31:02 +01:00
|
|
|
watchHandler()->appendFormatRequests(&cmd);
|
2015-02-11 17:51:15 +01:00
|
|
|
|
|
|
|
|
cmd.arg("stringcutoff", action(MaximalStringLength)->value().toByteArray());
|
|
|
|
|
cmd.arg("displaystringlimit", action(DisplayStringLimit)->value().toByteArray());
|
2014-05-16 00:18:17 +02:00
|
|
|
|
2014-11-11 11:00:32 +01:00
|
|
|
// Re-create tooltip items that are not filters on existing local variables in
|
|
|
|
|
// the tooltip model.
|
2015-02-11 17:51:15 +01:00
|
|
|
cmd.beginList("watchers");
|
2014-11-11 11:00:32 +01:00
|
|
|
DebuggerToolTipContexts toolTips = DebuggerToolTipManager::pendingTooltips(this);
|
|
|
|
|
foreach (const DebuggerToolTipContext &p, toolTips) {
|
2015-02-11 17:51:15 +01:00
|
|
|
cmd.beginGroup();
|
|
|
|
|
cmd.arg("iname", p.iname);
|
|
|
|
|
cmd.arg("exp", p.expression.toLatin1().toHex());
|
|
|
|
|
cmd.endGroup();
|
2013-11-27 16:10:14 +01:00
|
|
|
}
|
|
|
|
|
|
2015-02-11 17:51:15 +01:00
|
|
|
QHashIterator<QByteArray, int> it(WatchHandler::watcherNames());
|
2013-11-27 16:10:14 +01:00
|
|
|
while (it.hasNext()) {
|
|
|
|
|
it.next();
|
2015-02-11 17:51:15 +01:00
|
|
|
cmd.beginGroup();
|
|
|
|
|
cmd.arg("iname", "watch." + QByteArray::number(it.value()));
|
|
|
|
|
cmd.arg("exp", it.key().toHex());
|
|
|
|
|
cmd.endGroup();
|
2013-11-27 16:10:14 +01:00
|
|
|
}
|
2015-02-11 17:51:15 +01:00
|
|
|
cmd.endList();
|
2013-11-27 16:10:14 +01:00
|
|
|
|
|
|
|
|
const static bool alwaysVerbose = !qgetenv("QTC_DEBUGGER_PYTHON_VERBOSE").isEmpty();
|
2015-02-11 17:51:15 +01:00
|
|
|
|
|
|
|
|
cmd.arg("passExceptions", alwaysVerbose);
|
|
|
|
|
cmd.arg("fancy", boolSetting(UseDebuggingHelpers));
|
|
|
|
|
cmd.arg("autoderef", boolSetting(AutoDerefPointers));
|
|
|
|
|
cmd.arg("dyntype", boolSetting(UseDynamicType));
|
|
|
|
|
cmd.arg("nativemixed", isNativeMixedActive());
|
|
|
|
|
|
2015-01-22 12:05:00 +01:00
|
|
|
if (isNativeMixedActive()) {
|
|
|
|
|
StackFrame frame = stackHandler()->currentFrame();
|
|
|
|
|
if (frame.language == QmlLanguage)
|
2015-02-11 17:51:15 +01:00
|
|
|
cmd.arg("qmlcontext", "0x" + QByteArray::number(frame.address, 16));
|
2015-01-22 12:05:00 +01:00
|
|
|
}
|
|
|
|
|
|
2015-02-11 17:51:15 +01:00
|
|
|
cmd.arg("resultvarname", m_resultVarName);
|
2015-03-26 13:03:38 +01:00
|
|
|
cmd.arg("partialVariable", params.partialVariable);
|
2015-05-29 08:23:52 +02:00
|
|
|
cmd.arg("sortStructMembers", boolSetting(SortStructMembers));
|
2015-07-17 13:30:51 +02:00
|
|
|
cmd.flags = Discardable | InUpdateLocals;
|
2015-09-09 16:34:09 +02:00
|
|
|
cmd.callback = CB(handleStackFrame);
|
2015-02-11 17:51:15 +01:00
|
|
|
runCommand(cmd);
|
2014-01-07 16:15:08 +01:00
|
|
|
|
2015-02-11 17:51:15 +01:00
|
|
|
cmd.arg("passExceptions", true);
|
|
|
|
|
m_lastDebuggableCommand = cmd;
|
2013-11-27 16:10:14 +01:00
|
|
|
}
|
|
|
|
|
|
2015-06-08 18:07:11 +02:00
|
|
|
void GdbEngine::handleStackFrame(const DebuggerResponse &response)
|
2013-11-27 16:10:14 +01:00
|
|
|
{
|
2015-07-17 13:30:51 +02:00
|
|
|
m_inUpdateLocals = false;
|
|
|
|
|
|
2015-02-05 15:47:07 +01:00
|
|
|
if (response.resultClass == ResultDone) {
|
2013-11-27 16:10:14 +01:00
|
|
|
QByteArray out = response.consoleStreamOutput;
|
|
|
|
|
while (out.endsWith(' ') || out.endsWith('\n'))
|
|
|
|
|
out.chop(1);
|
|
|
|
|
int pos = out.indexOf("data=");
|
|
|
|
|
if (pos != 0) {
|
|
|
|
|
showMessage(_("DISCARDING JUNK AT BEGIN OF RESPONSE: "
|
|
|
|
|
+ out.left(pos)));
|
|
|
|
|
out = out.mid(pos);
|
|
|
|
|
}
|
|
|
|
|
GdbMi all;
|
|
|
|
|
all.fromStringMultiple(out);
|
|
|
|
|
|
2015-03-26 13:03:38 +01:00
|
|
|
updateLocalsView(all);
|
2015-03-19 14:21:20 +01:00
|
|
|
|
2013-11-27 16:10:14 +01:00
|
|
|
} else {
|
|
|
|
|
showMessage(_("DUMPER FAILED: " + response.toString()));
|
|
|
|
|
}
|
2015-07-06 09:46:08 +02:00
|
|
|
watchHandler()->notifyUpdateFinished();
|
2013-11-27 16:10:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString GdbEngine::msgPtraceError(DebuggerStartMode sm)
|
|
|
|
|
{
|
|
|
|
|
if (sm == StartInternal) {
|
|
|
|
|
return QCoreApplication::translate("QtDumperHelper",
|
|
|
|
|
"ptrace: Operation not permitted.\n\n"
|
|
|
|
|
"Could not attach to the process. "
|
|
|
|
|
"Make sure no other debugger traces this process.\n"
|
|
|
|
|
"Check the settings of\n"
|
|
|
|
|
"/proc/sys/kernel/yama/ptrace_scope\n"
|
|
|
|
|
"For more details, see /etc/sysctl.d/10-ptrace.conf\n");
|
|
|
|
|
}
|
|
|
|
|
return QCoreApplication::translate("QtDumperHelper",
|
|
|
|
|
"ptrace: Operation not permitted.\n\n"
|
|
|
|
|
"Could not attach to the process. "
|
|
|
|
|
"Make sure no other debugger traces this process.\n"
|
|
|
|
|
"If your uid matches the uid\n"
|
|
|
|
|
"of the target process, check the settings of\n"
|
|
|
|
|
"/proc/sys/kernel/yama/ptrace_scope\n"
|
|
|
|
|
"For more details, see /etc/sysctl.d/10-ptrace.conf\n");
|
|
|
|
|
}
|
|
|
|
|
|
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::GdbMi)
|