debugger: Replace adapter classes by GdbEngine subclasses

Change-Id: Iee6195b1eed5251545b9d688221ac2edf19325c1
Reviewed-by: Christian Kandeler <christian.kandeler@nokia.com>
This commit is contained in:
hjk
2012-06-13 10:15:56 +02:00
committed by hjk
parent c68cf6dcd9
commit 5d782df33f
27 changed files with 605 additions and 986 deletions

View File

@@ -69,6 +69,7 @@
#include "debuggersourcepathmappingwidget.h"
#include "hostutils.h"
#include "logwindow.h"
#include "procinterrupt.h"
#include <coreplugin/icore.h>
#include <coreplugin/idocument.h>
@@ -78,8 +79,9 @@
#include <projectexplorer/taskhub.h>
#include <projectexplorer/itaskhandler.h>
#include <texteditor/itexteditor.h>
#include <utils/qtcassert.h>
#include <utils/elfreader.h>
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
#include <QCoreApplication>
#include <QDebug>
@@ -243,7 +245,6 @@ GdbEngine::GdbEngine(const DebuggerStartParameters &startParameters,
setObjectName(_("GdbEngine"));
m_busy = false;
m_gdbAdapter = 0;
m_debuggingHelperState = DebuggingHelperUninitialized;
m_gdbVersion = 100;
m_gdbBuildVersion = -1;
@@ -268,8 +269,6 @@ GdbEngine::GdbEngine(const DebuggerStartParameters &startParameters,
invalidateSourcesList();
m_gdbAdapter = createAdapter();
m_debugInfoTaskHandler = new DebugInfoTaskHandler(this);
ExtensionSystem::PluginManager::instance()->addObject(m_debugInfoTaskHandler);
@@ -280,16 +279,10 @@ GdbEngine::GdbEngine(const DebuggerStartParameters &startParameters,
SLOT(reloadLocals()));
connect(debuggerCore()->action(CreateFullBacktrace), SIGNAL(triggered()),
SLOT(createFullBacktrace()));
}
DebuggerStartMode GdbEngine::startMode() const
{
return startParameters().startMode;
}
AbstractGdbProcess *GdbEngine::gdbProc() const
{
return m_gdbAdapter->gdbProc();
connect(debuggerCore()->action(UseDebuggingHelpers), SIGNAL(valueChanged(QVariant)),
SLOT(reloadLocals()));
connect(debuggerCore()->action(UseDynamicType), SIGNAL(valueChanged(QVariant)),
SLOT(reloadLocals()));
}
GdbEngine::~GdbEngine()
@@ -299,10 +292,12 @@ GdbEngine::~GdbEngine()
m_debugInfoTaskHandler = 0;
// Prevent sending error messages afterwards.
if (m_gdbAdapter)
disconnect(gdbProc(), 0, this, 0);
delete m_gdbAdapter;
m_gdbAdapter = 0;
disconnect();
}
DebuggerStartMode GdbEngine::startMode() const
{
return startParameters().startMode;
}
QString GdbEngine::errorMessage(QProcess::ProcessError error)
@@ -396,11 +391,9 @@ static bool contains(const QByteArray &message, const char *pattern, int size)
const int pos = message.indexOf(pattern);
if (pos == -1)
return false;
if (pos != 0 && message.at(pos - 1) != '\n')
return false;
if (pos + size != s && message.at(pos + size + 1) != '\n')
return false;
return true;
const bool beginFits = pos == 0 || message.at(pos - 1) == '\n';
const bool endFits = pos + size == s || message.at(pos + size) == '\n';
return beginFits && endFits;
}
static bool isGdbConnectionError(const QByteArray &message)
@@ -856,7 +849,7 @@ void GdbEngine::interruptInferior()
} else {
showStatusMessage(tr("Stop requested..."), 5000);
showMessage(_("TRYING TO INTERRUPT INFERIOR"));
m_gdbAdapter->interruptInferior();
interruptInferior2();
}
}
@@ -885,25 +878,6 @@ void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
notifyInferiorPid(pid);
}
void GdbEngine::postCommand(const QByteArray &command, AdapterCallback callback,
const char *callbackName, const QVariant &cookie)
{
postCommand(command, NoFlags, callback, callbackName, cookie);
}
void GdbEngine::postCommand(const QByteArray &command, GdbCommandFlags flags,
AdapterCallback callback,
const char *callbackName, const QVariant &cookie)
{
GdbCommand cmd;
cmd.command = command;
cmd.flags = flags;
cmd.adapterCallback = callback;
cmd.callbackName = callbackName;
cmd.cookie = cookie;
postCommandHelper(cmd);
}
void GdbEngine::postCommand(const QByteArray &command, GdbCommandCallback callback,
const char *callbackName, const QVariant &cookie)
{
@@ -1021,7 +995,7 @@ void GdbEngine::flushCommand(const GdbCommand &cmd0)
QMetaObject::invokeMethod(this, "handleResponse",
Q_ARG(QByteArray, buffer));
} else {
m_gdbAdapter->write(cmd.command + "\r\n");
write(cmd.command + "\r\n");
// Start Watchdog.
if (m_commandTimer.interval() <= 20000)
@@ -1226,8 +1200,6 @@ void GdbEngine::handleResultRecord(GdbResponse *response)
if (cmd.callback)
(this->*cmd.callback)(*response);
else if (cmd.adapterCallback)
(m_gdbAdapter->*cmd.adapterCallback)(*response);
if (cmd.flags & RebuildBreakpointModel) {
--m_pendingBreakpointRequests;
@@ -1595,7 +1567,7 @@ void GdbEngine::handleStop1(const GdbMi &data)
// Don't load helpers on stops triggered by signals unless it's
// an intentional trap.
if (initHelpers
&& m_gdbAdapter->dumperHandling() != AbstractGdbAdapter::DumperLoadedByGdbPreload
&& dumperHandling() != DumperLoadedByGdbPreload
&& reason == "signal-received") {
const QByteArray name = data.findChild("signal-name").data();
const DebuggerStartParameters &sp = startParameters();
@@ -1775,8 +1747,8 @@ void GdbEngine::handleStop2()
reloadStack(false); // Will trigger register reload.
if (supportsThreads()) {
if (m_gdbAdapter->isCodaAdapter()) {
m_gdbAdapter->codaReloadThreads();
if (isCodaAdapter()) {
codaReloadThreads();
} else if (m_isMacGdb || m_gdbVersion < 70100) {
postCommand("-thread-list-ids", Discardable, CB(handleThreadListIds));
} else {
@@ -1875,8 +1847,7 @@ void GdbEngine::pythonDumpersFailed()
{
m_hasPython = false;
const DebuggerStartParameters &sp = startParameters();
if (m_gdbAdapter->dumperHandling() == AbstractGdbAdapter::DumperLoadedByGdbPreload
&& checkDebuggingHelpersClassic()) {
if (dumperHandling() == DumperLoadedByGdbPreload && checkDebuggingHelpersClassic()) {
QByteArray cmd = "set environment ";
if (sp.toolChainAbi.os() == Abi::MacOS)
cmd += "DYLD_INSERT_LIBRARIES";
@@ -2031,15 +2002,10 @@ void GdbEngine::handleInferiorShutdown(const GdbResponse &response)
}
showMessageBox(QMessageBox::Critical,
tr("Failed to shut down application"),
AbstractGdbAdapter::msgInferiorStopFailed(QString::fromLocal8Bit(ba)));
msgInferiorStopFailed(QString::fromLocal8Bit(ba)));
notifyInferiorShutdownFailed();
}
void GdbEngine::shutdownEngine()
{
m_gdbAdapter->shutdownAdapter();
}
void GdbEngine::notifyAdapterShutdownFailed()
{
showMessage(_("ADAPTER SHUTDOWN FAILED"));
@@ -2076,7 +2042,7 @@ void GdbEngine::handleGdbExit(const GdbResponse &response)
// Don't set state here, this will be handled in handleGdbFinished()
//notifyEngineShutdownOk();
} else {
QString msg = m_gdbAdapter->msgGdbStopFailed(
QString msg = msgGdbStopFailed(
QString::fromLocal8Bit(response.data.findChild("msg").data()));
qDebug() << (_("GDB WON'T EXIT (%1); KILLING IT").arg(msg));
showMessage(_("GDB WON'T EXIT (%1); KILLING IT").arg(msg));
@@ -2121,50 +2087,6 @@ QString msgNoGdbBinaryForToolChain(const Abi &tc)
.arg(tc.toString());
}
AbstractGdbAdapter *GdbEngine::createAdapter()
{
const DebuggerStartParameters &sp = startParameters();
if (sp.toolChainAbi.os() == Abi::SymbianOS) {
// FIXME: 1 of 3 testing hacks.
return new CodaGdbAdapter(this);
}
switch (sp.startMode) {
case AttachCore:
return new CoreGdbAdapter(this);
case AttachToRemoteServer:
return new RemoteGdbServerAdapter(this);
case StartRemoteGdb:
return new RemotePlainGdbAdapter(this);
case AttachExternal:
return new AttachGdbAdapter(this);
default:
if (sp.useTerminal)
return new TermGdbAdapter(this);
return new LocalPlainGdbAdapter(this);
}
}
void GdbEngine::setupEngine()
{
QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state());
QTC_CHECK(m_debuggingHelperState == DebuggingHelperUninitialized);
if (m_gdbAdapter->dumperHandling() != AbstractGdbAdapter::DumperNotAvailable) {
connect(debuggerCore()->action(UseDebuggingHelpers),
SIGNAL(valueChanged(QVariant)),
SLOT(reloadLocals()));
connect(debuggerCore()->action(UseDynamicType),
SIGNAL(valueChanged(QVariant)),
SLOT(reloadLocals()));
}
QTC_CHECK(state() == EngineSetupRequested);
if (debuggerCore()->boolSetting(WarnOnReleaseBuilds))
checkForReleaseBuild();
m_gdbAdapter->startAdapter();
}
bool GdbEngine::hasCapability(unsigned cap) const
{
if (cap & (ReverseSteppingCapability
@@ -2237,7 +2159,7 @@ void GdbEngine::executeStep()
setTokenBarrier();
notifyInferiorRunRequested();
showStatusMessage(tr("Step requested..."), 5000);
if (m_gdbAdapter->isCodaAdapter() && stackHandler()->stackSize() > 0)
if (isCodaAdapter() && stackHandler()->stackSize() > 0)
postCommand("sal step,0x" + QByteArray::number(stackHandler()->topAddress(), 16));
if (isReverseDebugging()) {
postCommand("reverse-step", RunRequest, CB(handleExecuteStep));
@@ -2311,7 +2233,7 @@ void GdbEngine::executeNext()
setTokenBarrier();
notifyInferiorRunRequested();
showStatusMessage(tr("Step next requested..."), 5000);
if (m_gdbAdapter->isCodaAdapter() && stackHandler()->stackSize() > 0)
if (isCodaAdapter() && stackHandler()->stackSize() > 0)
postCommand("sal next,0x" + QByteArray::number(stackHandler()->topAddress(), 16));
if (isReverseDebugging()) {
postCommand("reverse-next", RunRequest, CB(handleExecuteNext));
@@ -3206,7 +3128,7 @@ void GdbEngine::insertBreakpoint(BreakpointModelId id)
cmd = "-break-insert -a -f ";
} else if (m_isMacGdb) {
cmd = "-break-insert -l -1 -f ";
} else if (m_gdbAdapter->isCodaAdapter()) {
} else if (isCodaAdapter()) {
cmd = "-break-insert -h -f ";
} else if (m_gdbVersion >= 70000) {
int spec = handler->threadSpec(id);
@@ -3591,7 +3513,7 @@ void GdbEngine::reloadStack(bool forceGotoLocation)
PENDING_DEBUG("RELOAD STACK");
QByteArray cmd = "-stack-list-frames";
int stackDepth = debuggerCore()->action(MaximalStackDepth)->value().toInt();
if (stackDepth && !m_gdbAdapter->isCodaAdapter())
if (stackDepth && !isCodaAdapter())
cmd += " 0 " + QByteArray::number(stackDepth);
postCommand(cmd, Discardable, CB(handleStackListFrames),
QVariant::fromValue<StackCookie>(StackCookie(false, forceGotoLocation)));
@@ -3832,12 +3754,12 @@ void GdbEngine::reloadRegisters()
postCommand("-data-list-register-names", CB(handleRegisterListNames));
m_registerNamesListed = true;
// FIXME: Maybe better completely re-do this logic in CODA adapter.
if (m_gdbAdapter->isCodaAdapter())
if (isCodaAdapter())
return;
}
if (m_gdbAdapter->isCodaAdapter()) {
m_gdbAdapter->codaReloadRegisters();
if (isCodaAdapter()) {
codaReloadRegisters();
} else {
postCommand("-data-list-register-values r",
Discardable, CB(handleRegisterListValues));
@@ -3865,8 +3787,8 @@ void GdbEngine::handleRegisterListNames(const GdbResponse &response)
registerHandler()->setRegisters(registers);
if (m_gdbAdapter->isCodaAdapter())
m_gdbAdapter->codaReloadRegisters();
if (isCodaAdapter())
codaReloadRegisters();
}
void GdbEngine::handleRegisterListValues(const GdbResponse &response)
@@ -4047,7 +3969,7 @@ bool GdbEngine::hasDebuggingHelperForType(const QByteArray &type) const
if (!debuggerCore()->boolSetting(UseDebuggingHelpers))
return false;
if (m_gdbAdapter->dumperHandling() == AbstractGdbAdapter::DumperNotAvailable) {
if (dumperHandling() == DumperNotAvailable) {
// Inferior calls are not possible in gdb when looking at core files.
return type == "QString" || type.endsWith("::QString")
|| type == "QStringList" || type.endsWith("::QStringList");
@@ -4760,7 +4682,7 @@ void GdbEngine::startGdb(const QStringList &args)
const DebuggerStartParameters &sp = startParameters();
m_gdb = gdbBinary(sp);
if (m_gdb.isEmpty()) {
m_gdbAdapter->handleGdbStartFailed();
handleGdbStartFailed();
handleAdapterStartFailed(
msgNoGdbBinaryForToolChain(sp.toolChainAbi),
_(Constants::DEBUGGER_COMMON_SETTINGS_ID));
@@ -4786,7 +4708,7 @@ void GdbEngine::startGdb(const QStringList &args)
gdbProc()->start(m_gdb, gdbArgs);
if (!gdbProc()->waitForStarted()) {
m_gdbAdapter->handleGdbStartFailed();
handleGdbStartFailed();
const QString msg = errorMessage(QProcess::FailedToStart);
handleAdapterStartFailed(msg);
return;
@@ -4866,6 +4788,47 @@ void GdbEngine::startGdb(const QStringList &args)
"AN ERROR IS EXPECTED."));
postCommand("disassemble 0 0", ConsoleCommand, CB(handleDisassemblerCheck));
typedef GlobalDebuggerOptions::SourcePathMap SourcePathMap;
typedef SourcePathMap::const_iterator SourcePathMapIterator;
if (debuggerCore()->boolSetting(WarnOnReleaseBuilds))
checkForReleaseBuild();
showStatusMessage(tr("Setting up inferior..."));
// Addint executable to modules list.
Module module;
module.startAddress = 0;
module.endAddress = 0;
module.modulePath = sp.executable;
module.moduleName = QLatin1String("<executable>");
modulesHandler()->updateModule(module);
// Apply source path mappings from global options.
const SourcePathMap sourcePathMap =
DebuggerSourcePathMappingWidget::mergePlatformQtPath(sp.qtInstallPath,
debuggerCore()->globalDebuggerOptions()->sourcePathMap);
const SourcePathMapIterator cend = sourcePathMap.constEnd();
SourcePathMapIterator it = sourcePathMap.constBegin();
for ( ; it != cend; ++it)
postCommand("set substitute-path " + it.key().toLocal8Bit()
+ " " + it.value().toLocal8Bit());
// Spaces just will not work.
foreach (const QString &src, sp.debugSourceLocation)
postCommand("directory " + src.toLocal8Bit());
const QByteArray sysroot = sp.sysroot.toLocal8Bit();
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);
if (attemptQuickStart()) {
postCommand("set auto-solib-add off", ConsoleCommand);
} else {
@@ -4875,15 +4838,22 @@ void GdbEngine::startGdb(const QStringList &args)
}
// Dummy command to guarantee a roundtrip before the adapter proceed.
postCommand("pwd", ConsoleCommand, CB(handleGdbStart));
postCommand("pwd", ConsoleCommand, CB(reportEngineSetupOk));
}
void GdbEngine::handleGdbStart(const GdbResponse &response)
void GdbEngine::reportEngineSetupOk(const GdbResponse &response)
{
Q_UNUSED(response);
m_gdbAdapter->handleGdbStartDone();
QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state());
showMessage(_("ENGINE SUCCESSFULLY STARTED"));
notifyEngineSetupOk();
}
void GdbEngine::handleGdbStartFailed()
{
}
void GdbEngine::loadInitScript()
{
const QString script = startParameters().overrideStartScript;
@@ -4982,9 +4952,8 @@ void GdbEngine::abortDebugger()
if (targetState() == DebuggerFinished) {
// We already tried. Try harder.
showMessage(_("ABORTING DEBUGGER. SECOND TIME."));
QTC_ASSERT(m_gdbAdapter, return);
QTC_ASSERT(m_gdbAdapter->gdbProc(), return);
m_gdbAdapter->gdbProc()->kill();
QTC_ASSERT(gdbProc(), return);
gdbProc()->kill();
} else {
// Be friendly the first time. This will change targetState().
showMessage(_("ABORTING DEBUGGER. FIRST TIME."));
@@ -5009,50 +4978,6 @@ void GdbEngine::handleAdapterStartFailed(const QString &msg,
notifyEngineSetupFailed();
}
void GdbEngine::handleAdapterStarted()
{
QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state());
showMessage(_("ADAPTER SUCCESSFULLY STARTED"));
notifyEngineSetupOk();
Module module;
module.startAddress = 0;
module.endAddress = 0;
module.modulePath = startParameters().executable;
module.moduleName = QLatin1String("<executable>");
modulesHandler()->updateModule(module);
}
void GdbEngine::setupInferior()
{
QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
typedef GlobalDebuggerOptions::SourcePathMap SourcePathMap;
typedef SourcePathMap::const_iterator SourcePathMapIterator;
showStatusMessage(tr("Setting up inferior..."));
const DebuggerStartParameters &sp = startParameters();
// Apply source path mappings from global options.
const SourcePathMap sourcePathMap =
DebuggerSourcePathMappingWidget::mergePlatformQtPath(sp.qtInstallPath,
debuggerCore()->globalDebuggerOptions()->sourcePathMap);
const SourcePathMapIterator cend = sourcePathMap.constEnd();
SourcePathMapIterator it = sourcePathMap.constBegin();
for ( ; it != cend; ++it)
postCommand("set substitute-path " + it.key().toLocal8Bit()
+ " " + it.value().toLocal8Bit());
// Spaces just will not work.
foreach (const QString &src, sp.debugSourceLocation)
postCommand("directory " + src.toLocal8Bit());
//QByteArray ba = QFileInfo(sp.dumperLibrary).path().toLocal8Bit();
//if (!ba.isEmpty())
// postCommand("set solib-search-path " + ba);
m_gdbAdapter->setupInferior();
}
void GdbEngine::notifyInferiorSetupFailed()
{
// FIXME: that's not enough to stop gdb from getting confused
@@ -5172,12 +5097,6 @@ void GdbEngine::handleBreakOnQFatal(const GdbResponse &response)
notifyInferiorSetupOk();
}
void GdbEngine::runEngine()
{
QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
m_gdbAdapter->runEngine();
}
void GdbEngine::notifyInferiorSetupFailed(const QString &msg)
{
showStatusMessage(tr("Failed to start application: ") + msg);
@@ -5246,14 +5165,15 @@ void GdbEngine::resetCommandQueue()
void GdbEngine::handleRemoteSetupDone(int gdbServerPort, int qmlPort)
{
Q_UNUSED(gdbServerPort);
Q_UNUSED(qmlPort);
notifyEngineRemoteSetupDone();
m_gdbAdapter->handleRemoteSetupDone(gdbServerPort, qmlPort);
}
void GdbEngine::handleRemoteSetupFailed(const QString &message)
{
Q_UNUSED(message);
notifyEngineRemoteSetupFailed();
m_gdbAdapter->handleRemoteSetupFailed(message);
}
bool GdbEngine::setupQmlStep(bool on)
@@ -5421,14 +5341,106 @@ void GdbEngine::checkForReleaseBuild()
showMessageBox(QMessageBox::Information, tr("Warning"), warning);
}
void GdbEngine::write(const QByteArray &data)
{
gdbProc()->write(data);
}
bool GdbEngine::isCodaAdapter() const
{
return false;
}
bool GdbEngine::prepareCommand()
{
#ifdef Q_OS_WIN
Utils::QtcProcess::SplitError perr;
startParameters().processArgs = Utils::QtcProcess::prepareArgs(
startParameters().processArgs, &perr,
&startParameters().environment, &startParameters().workingDirectory);
if (perr != Utils::QtcProcess::SplitOk) {
// perr == BadQuoting is never returned on Windows
// FIXME? QTCREATORBUG-2809
handleAdapterStartFailed(QCoreApplication::translate("DebuggerEngine", // Same message in CdbEngine
"Debugging complex command lines is currently not supported on Windows."), QString());
return false;
}
#endif
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();
}
}
//
// Factory
//
DebuggerEngine *createGdbEngine(const DebuggerStartParameters &startParameters,
DebuggerEngine *createGdbEngine(const DebuggerStartParameters &sp,
DebuggerEngine *masterEngine)
{
return new GdbEngine(startParameters, masterEngine);
if (sp.toolChainAbi.os() == Abi::SymbianOS) {
// FIXME: 1 of 3 testing hacks.
return new GdbCodaEngine(sp, masterEngine);
}
switch (sp.startMode) {
case AttachCore:
return new GdbCoreEngine(sp, masterEngine);
case AttachToRemoteServer:
return new GdbRemoteServerEngine(sp, masterEngine);
case StartRemoteGdb:
return new GdbRemotePlainEngine(sp, masterEngine);
case AttachExternal:
return new GdbAttachEngine(sp, masterEngine);
default:
if (sp.useTerminal)
return new GdbTermEngine(sp, masterEngine);
return new GdbLocalPlainEngine(sp, masterEngine);
}
}
void addGdbOptionPages(QList<Core::IOptionsPage *> *opts)