Commit 52915776 authored by Friedemann Kleint's avatar Friedemann Kleint
Browse files

Make CDB load custom dumpers.

Load in a 'well-defined' (temporary) breakpoint
at main().
parent ef8e69d9
......@@ -2529,7 +2529,11 @@ void *qDumpObjectData440(
int protocolVersion,
int token,
void *data,
#ifdef Q_CC_MSVC // CDB cannot handle boolean parameters
int dumpChildren,
#else
bool dumpChildren,
#endif
int extraInt0,
int extraInt1,
int extraInt2,
......
......@@ -48,7 +48,8 @@ static const char sourceFileQuoteC = '`';
CDBBreakPoint::CDBBreakPoint() :
ignoreCount(0),
lineNumber(-1)
lineNumber(-1),
oneShot(false)
{
}
......@@ -57,7 +58,8 @@ CDBBreakPoint::CDBBreakPoint(const BreakpointData &bpd) :
condition(bpd.condition),
ignoreCount(0),
funcName(bpd.funcName),
lineNumber(-1)
lineNumber(-1),
oneShot(false)
{
if (!bpd.ignoreCount.isEmpty())
ignoreCount = bpd.ignoreCount.toInt();
......@@ -75,6 +77,10 @@ int CDBBreakPoint::compare(const CDBBreakPoint& rhs) const
return 1;
if (lineNumber < rhs.lineNumber)
return -1;
if (oneShot && !rhs.oneShot)
return 1;
if (!oneShot && rhs.oneShot)
return -1;
if (const int fileCmp = fileName.compare(rhs.fileName))
return fileCmp;
if (const int funcCmp = funcName.compare(rhs.funcName))
......@@ -87,7 +93,8 @@ int CDBBreakPoint::compare(const CDBBreakPoint& rhs) const
void CDBBreakPoint::clear()
{
ignoreCount = 0;
clearExpressionData();
oneShot = false;
clearExpressionData();
}
void CDBBreakPoint::clearExpressionData()
......@@ -110,6 +117,8 @@ QDebug operator<<(QDebug dbg, const CDBBreakPoint &bp)
nsp << " condition='" << bp.condition << '\'';
if (bp.ignoreCount)
nsp << " ignoreCount=" << bp.ignoreCount;
if (bp.oneShot)
nsp << " oneShot";
return dbg;
}
......@@ -144,7 +153,10 @@ bool CDBBreakPoint::apply(CIDebugBreakpoint *ibp, QString *errorMessage) const
}
// Pass Count is ignoreCount + 1
ibp->SetPassCount(ignoreCount + 1u);
ibp->AddFlags(DEBUG_BREAKPOINT_ENABLED);
ULONG flags = DEBUG_BREAKPOINT_ENABLED;
if (oneShot)
flags |= DEBUG_BREAKPOINT_ONE_SHOT;
ibp->AddFlags(flags);
return true;
}
......@@ -190,6 +202,10 @@ bool CDBBreakPoint::retrieve(CIDebugBreakpoint *ibp, QString *errorMessage)
ibp->GetPassCount(&ignoreCount);
if (ignoreCount)
ignoreCount--;
ULONG flags = 0;
ibp->GetFlags(&flags);
if (flags & DEBUG_BREAKPOINT_ONE_SHOT)
oneShot = true;
const QString expr = QString::fromUtf16(wszBuf);
if (!parseExpression(expr)) {
*errorMessage = QString::fromLatin1("Parsing of '%1' failed.").arg(expr);
......
......@@ -83,6 +83,7 @@ struct CDBBreakPoint
unsigned long ignoreCount; // ignore count associated with breakpoint
int lineNumber; // line in source file
QString funcName; // name of containing function
bool oneShot;
};
QDebug operator<<(QDebug, const CDBBreakPoint &bp);
......
......@@ -53,6 +53,7 @@
#include <utils/consoleprocess.h>
#include <QtCore/QDebug>
#include <QtCore/QTimer>
#include <QtCore/QTimerEvent>
#include <QtCore/QFileInfo>
#include <QtCore/QDir>
......@@ -96,13 +97,13 @@ QString msgDebugEngineComResult(HRESULT hr)
case E_UNEXPECTED:
return QLatin1String("E_UNEXPECTED");
case E_NOTIMPL:
return QLatin1String("E_NOTIMPL");
return QLatin1String("E_NOTIMPL");
}
if (hr == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED))
return QLatin1String("ERROR_ACCESS_DENIED");;
if (hr == HRESULT_FROM_NT(STATUS_CONTROL_C_EXIT))
return QLatin1String("STATUS_CONTROL_C_EXIT");
return Core::Utils::winErrorMessage(HRESULT_CODE(hr));
return QLatin1String("E_FAIL ") + Core::Utils::winErrorMessage(HRESULT_CODE(hr));
}
static QString msgStackIndexOutOfRange(int idx, int size)
......@@ -450,14 +451,16 @@ void CdbDebugEnginePrivate::clearDisplay()
bool CdbDebugEngine::startDebugger()
{
m_d->clearDisplay();
const DebuggerStartMode mode = m_d->m_debuggerManager->startMode();
// Figure out dumper. @TODO: same in gdb...
bool dumperEnabled = false && m_d->m_debuggerManager->qtDumperLibraryEnabled();
const QString dumperLibName = QDir::toNativeSeparators(m_d->m_debuggerManager->qtDumperLibraryName());
const QString dumperLibName = QDir::toNativeSeparators(m_d->m_debuggerManagerAccess->qtDumperLibraryName());
bool dumperEnabled = mode != AttachCore && !dumperLibName.isEmpty()
&& m_d->m_debuggerManagerAccess->qtDumperLibraryEnabled();
if (dumperEnabled) {
const QFileInfo fi(dumperLibName);
if (!fi.isFile()) {
const QString msg = tr("The dumper library '%1' does not exist.").arg(dumperLibName);
m_d->m_debuggerManager->showQtDumperLibraryWarning(msg);
m_d->m_debuggerManagerAccess->showQtDumperLibraryWarning(msg);
dumperEnabled = false;
}
}
......@@ -466,7 +469,6 @@ bool CdbDebugEngine::startDebugger()
QString errorMessage;
bool rc = false;
m_d->clearForRun();
const DebuggerStartMode mode = m_d->m_debuggerManager->startMode();
switch (mode) {
case AttachExternal:
rc = startAttachDebugger(m_d->m_debuggerManager->m_attachedPID, &errorMessage);
......@@ -561,6 +563,52 @@ bool CdbDebugEngine::startDebuggerWithExecutable(DebuggerStartMode sm, QString *
return true;
}
// check for a breakpoint at 'main()'
static inline bool hasBreakPointAtMain(const BreakHandler *bp)
{
if (const int count = bp->size()) {
// check all variations, resolved or not
const QString main = QLatin1String("main");
const QString qMain = QLatin1String("qMain");
const QString moduleMainPattern = QLatin1String("!main");
for (int i = 0; i < count ; i++) {
const QString &function = bp->at(i)->funcName;
if (function == main || function == qMain || function.endsWith(moduleMainPattern))
return true;
}
}
return false;
}
void CdbDebugEnginePrivate::processCreatedAttached(ULONG64 processHandle, ULONG64 initialThreadHandle)
{
setDebuggeeHandles(reinterpret_cast<HANDLE>(processHandle), reinterpret_cast<HANDLE>(initialThreadHandle));
m_debuggerManagerAccess->notifyInferiorRunning();
ULONG currentThreadId;
if (SUCCEEDED(m_cif.debugSystemObjects->GetThreadIdByHandle(initialThreadHandle, &currentThreadId))) {
m_currentThreadId = currentThreadId;
} else {
m_currentThreadId = 0;
}
// Set initial breakpoints
if (m_debuggerManagerAccess->breakHandler()->hasPendingBreakpoints())
m_engine->attemptBreakpointSynchronization();
// At any event, we want a temporary breakpoint at main() to load
// the dumpers.
if (m_dumper.state() == CdbDumperHelper::NotLoaded) {
if (!hasBreakPointAtMain(m_debuggerManagerAccess->breakHandler())) {
CDBBreakPoint mainBP;
// Do not resolve at this point in the rare event someone
// has main in a module
mainBP.funcName = QLatin1String("main");
mainBP.oneShot = true;
QString errorMessage;
if (!mainBP.add(m_cif.debugControl, &errorMessage))
m_debuggerManagerAccess->showQtDumperLibraryWarning(errorMessage);
}
}
}
void CdbDebugEngine::processTerminated(unsigned long exitCode)
{
if (debugCDB)
......@@ -859,13 +907,18 @@ void CdbDebugEngine::continueInferior()
}
// Continue process without notifications
bool CdbDebugEnginePrivate::continueInferiorProcess(QString *errorMessage)
bool CdbDebugEnginePrivate::continueInferiorProcess(QString *errorMessagePtr /* = 0 */)
{
if (debugCDB)
qDebug() << Q_FUNC_INFO;
const HRESULT hr = m_cif.debugControl->SetExecutionStatus(DEBUG_STATUS_GO);
if (FAILED(hr)) {
*errorMessage = msgComFailed("SetExecutionStatus", hr);
const QString errorMessage = msgComFailed("SetExecutionStatus", hr);
if (errorMessagePtr) {
*errorMessagePtr = errorMessage;
} else {
qWarning("continueInferiorProcess: %s\n", qPrintable(errorMessage));
}
return false;
}
return true;
......@@ -992,10 +1045,10 @@ void CdbDebugEngine::executeDebuggerCommand(const QString &command)
bool CdbDebugEnginePrivate::executeDebuggerCommand(CIDebugControl *ctrl, const QString &command, QString *errorMessage)
{
if (debugCDB)
qDebug() << Q_FUNC_INFO << command;
// output to all clients, else we do not see anything
const HRESULT hr = ctrl->ExecuteWide(DEBUG_OUTCTL_ALL_CLIENTS, command.utf16(), 0);
if (debugCDB)
qDebug() << "executeDebuggerCommand" << command << SUCCEEDED(hr);
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Unable to execute '%1': %2").
arg(command, msgDebugEngineComResult(hr));
......@@ -1300,10 +1353,19 @@ void CdbDebugEnginePrivate::handleDebugEvent()
switch (mode) {
case BreakEventHandle:
case BreakEventMain:
if (mode == BreakEventMain)
m_dumper.load(m_debuggerManager, m_debuggerManagerAccess);
m_debuggerManagerAccess->notifyInferiorStopped();
updateThreadList();
updateStackTrace();
break;
case BreakEventMainLoadDumpers:
// Temp stop to load dumpers
m_dumper.load(m_debuggerManager, m_debuggerManagerAccess);
m_engine->startWatchTimer();
continueInferiorProcess();
break;
case BreakEventIgnoreOnce:
m_engine->startWatchTimer();
break;
......@@ -1417,17 +1479,6 @@ void CdbDebugEnginePrivate::handleModuleLoad(const QString &name)
if (debugCDB>2)
qDebug() << Q_FUNC_INFO << "\n " << name;
updateModules();
// Call the dumper helper hook and notify about progress.
bool ignoreNextBreakPoint;
if (m_dumper.moduleLoadHook(name, &ignoreNextBreakPoint)) {
if (m_dumper.state() == CdbDumperHelper::Loaded)
m_debuggerManagerAccess->showDebuggerOutput(QLatin1String(dumperPrefixC), QString::fromLatin1("Dumpers loaded: %1").arg(m_dumper.library()));
} else {
m_debuggerManager->showQtDumperLibraryWarning(m_dumper.errorMessage());
m_debuggerManagerAccess->showDebuggerOutput(QLatin1String(dumperPrefixC), QString::fromLatin1("Unable to load dumpers: %1").arg(m_dumper.errorMessage()));
}
if (ignoreNextBreakPoint)
m_breakEventMode = BreakEventIgnoreOnce;
}
void CdbDebugEnginePrivate::handleBreakpointEvent(PDEBUG_BREAKPOINT2 pBP)
......@@ -1435,6 +1486,17 @@ void CdbDebugEnginePrivate::handleBreakpointEvent(PDEBUG_BREAKPOINT2 pBP)
Q_UNUSED(pBP)
if (debugCDB)
qDebug() << Q_FUNC_INFO;
// Did we hit main() and did the user want that or is that just
// our internal BP to load the dumpers?
QString errorMessage;
CDBBreakPoint bp;
if (bp.retrieve(pBP, &errorMessage) && !bp.funcName.isEmpty()) {
if (bp.funcName == QLatin1String("main") || bp.funcName.endsWith(QLatin1String("!main"))) {
m_breakEventMode = bp.oneShot ? BreakEventMainLoadDumpers : BreakEventMain;
}
if (debugCDB)
qDebug() << bp << " b-mode=" << m_breakEventMode;
}
}
void CdbDebugEngine::reloadSourceFiles()
......
......@@ -98,6 +98,10 @@ struct CdbDebugEnginePrivate
enum HandleBreakEventMode { // Special modes for break event handler.
BreakEventHandle,
BreakEventIgnoreOnce,
// We hit main (and the user intended it)
BreakEventMain,
// We hit main (and the user did not intend it, just load dumpers)
BreakEventMainLoadDumpers,
BreakEventSyncBreakPoints,
};
......@@ -107,6 +111,7 @@ struct CdbDebugEnginePrivate
bool init(QString *errorMessage);
~CdbDebugEnginePrivate();
void processCreatedAttached(ULONG64 processHandle, ULONG64 initialThreadHandle);
void setDebuggeeHandles(HANDLE hDebuggeeProcess, HANDLE hDebuggeeThread);
bool isDebuggeeRunning() const { return m_watchTimer != -1; }
......@@ -125,7 +130,7 @@ struct CdbDebugEnginePrivate
bool interruptInterferiorProcess(QString *errorMessage);
bool continueInferiorProcess(QString *errorMessage);
bool continueInferiorProcess(QString *errorMessage = 0);
bool continueInferior(QString *errorMessage);
bool attemptBreakpointSynchronization(QString *errorMessage);
......
......@@ -247,7 +247,8 @@ void formatException(const EXCEPTION_RECORD64 *e, QTextStream &str)
break;
case EXCEPTION_ACCESS_VIOLATION: {
const bool writeOperation = e->ExceptionInformation[0];
str << (writeOperation ? "write access violation" : "read access violation");
str << (writeOperation ? "write" : "read")
<< " access violation at: 0x" << e->ExceptionInformation[1];
}
break;
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
......@@ -342,8 +343,10 @@ STDMETHODIMP CdbDebugEventCallback::Exception(
QString msg;
{
QTextStream str(&msg);
formatException(Exception, m_pEngine->m_d->m_cif, str);
formatException(Exception, m_pEngine->m_d->m_cif, str);
}
if (debugCDB)
qDebug() << Q_FUNC_INFO << '\n' << msg;
m_pEngine->m_d->m_debuggerManagerAccess->showApplicationOutput(msg);
return S_OK;
}
......@@ -402,18 +405,7 @@ STDMETHODIMP CdbDebugEventCallback::CreateProcess(
Q_UNUSED(StartOffset)
if (debugCDB)
qDebug() << Q_FUNC_INFO << ModuleName;
m_pEngine->m_d->setDebuggeeHandles(reinterpret_cast<HANDLE>(Handle), reinterpret_cast<HANDLE>(InitialThreadHandle));
m_pEngine->m_d->m_debuggerManagerAccess->notifyInferiorRunning();
ULONG currentThreadId;
if (SUCCEEDED(m_pEngine->m_d->m_cif.debugSystemObjects->GetThreadIdByHandle(InitialThreadHandle, &currentThreadId)))
m_pEngine->m_d->m_currentThreadId = currentThreadId;
else
m_pEngine->m_d->m_currentThreadId = 0;
// Set initial breakpoints
if (m_pEngine->m_d->m_debuggerManagerAccess->breakHandler()->hasPendingBreakpoints())
m_pEngine->attemptBreakpointSynchronization();
m_pEngine->m_d->processCreatedAttached(Handle, InitialThreadHandle);
return S_OK;
}
......
......@@ -32,11 +32,18 @@
#include "cdbdebugengine_p.h"
#include "cdbdebugoutput.h"
#include "cdbdebugeventcallback.h"
#include "watchutils.h"
#include <QtCore/QRegExp>
#include <QtCore/QCoreApplication>
#include <QtCore/QTextStream>
enum { loadDebug = 0 };
static const char *dumperModuleNameC = "gdbmacros";
static const char *qtCoreModuleNameC = "QtCore";
static const ULONG waitTimeOutMS = 30000;
namespace Debugger {
namespace Internal {
......@@ -52,7 +59,7 @@ static bool allocDebuggeeMemory(CIDebugControl *ctl,
OutputRedirector redir(client, &stringHandler);
if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, allocCmd, errorMessage))
return false;
// "Allocated 1000 bytes starting at 003a0000" .. hopefully never localized
// "Allocated 1000 bytes starting at 003a0000" .. hopefully never localized
bool ok = false;
const QString output = stringHandler.result();
const int lastBlank = output.lastIndexOf(QLatin1Char(' '));
......@@ -61,6 +68,8 @@ static bool allocDebuggeeMemory(CIDebugControl *ctl,
if (ok)
*address = addri;
}
if (loadDebug)
qDebug() << Q_FUNC_INFO << '\n' << output << *address << ok;
if (!ok) {
*errorMessage = QString::fromLatin1("Failed to parse output '%1'").arg(output);
return false;
......@@ -88,25 +97,6 @@ static bool createDebuggeeAscIIString(CIDebugControl *ctl,
return true;
}
// Locate 'qstrdup' in the (potentially namespaced) corelib. For some
// reason, the symbol is present in QtGui as well without type information.
static inline QString resolveStrdup(CIDebugSymbols *syms, QString *errorMessage)
{
QStringList matches;
const QString pattern = QLatin1String("*qstrdup");
const QRegExp corelibPattern(QLatin1String("QtCore[d]*4!"));
Q_ASSERT(corelibPattern.isValid());
if (!searchSymbols(syms, pattern, &matches, errorMessage))
return QString();
QStringList corelibStrdup = matches.filter(corelibPattern);
if (corelibStrdup.isEmpty()) {
*errorMessage = QString::fromLatin1("Unable to locate '%1' in '%2' (%3)").
arg(pattern, corelibPattern.pattern(), matches.join(QString(QLatin1Char(','))));
return QString();
}
return corelibStrdup.front();
}
// Load a library into the debuggee. Currently requires
// the QtCored4.pdb file to be present as we need "qstrdup"
// as dummy symbol. This is ok ATM since dumpers only
......@@ -132,9 +122,10 @@ static bool debuggeeLoadLibrary(CIDebugControl *ctl,
// server, the debugger refuses to recognize it as a function.
// Set up the call stack with a function of same signature (qstrdup)
// and change the call register to LoadLibraryA() before executing "g".
// Prepare call.
const QString dummyFunc = resolveStrdup(syms, errorMessage);
if (dummyFunc.isEmpty())
// Prepare call: Locate 'qstrdup' in the (potentially namespaced) corelib. For some
// reason, the symbol is present in QtGui as well without type information.
QString dummyFunc = QLatin1String("*qstrdup");
if (resolveSymbol(syms, QLatin1String("QtCore[d]*4!"), &dummyFunc, errorMessage) != ResolveSymbolOk)
return false;
QString callCmd = QLatin1String(".call ");
callCmd += dummyFunc;
......@@ -145,80 +136,293 @@ static bool debuggeeLoadLibrary(CIDebugControl *ctl,
return false;
if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, QLatin1String("r eip=Kernel32!LoadLibraryA"), errorMessage))
return false;
// This will hit a breakpoint
if (loadDebug)
qDebug() << " executing 'g'";
// This will hit a breakpoint.
if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, QString(QLatin1Char('g')), errorMessage))
return false;
const HRESULT hr = ctl->WaitForEvent(0, waitTimeOutMS);
if (FAILED(hr)) {
*errorMessage = msgComFailed("WaitForEvent", hr);
return false;
// @Todo: We cannot evaluate output here as it is asynchronous
}
return true;
}
CdbDumperHelper::CdbDumperHelper(CdbComInterfaces *cif) :
// ------------------- CdbDumperHelper::DumperInputParameters
struct CdbDumperHelper::DumperInputParameters {
DumperInputParameters(int protocolVersion_ = 1,
int token_ = 1,
quint64 Address_ = 0,
bool dumpChildren_ = false,
int extraInt0_ = 0,
int extraInt1_ = 0,
int extraInt2_ = 0,
int extraInt3_ = 0);
QString command(const QString &dumpFunction) const;
int protocolVersion;
int token;
quint64 address;
bool dumpChildren;
int extraInt0;
int extraInt1;
int extraInt2;
int extraInt3;
};
CdbDumperHelper::DumperInputParameters::DumperInputParameters(int protocolVersion_,
int token_,
quint64 address_,
bool dumpChildren_,
int extraInt0_,
int extraInt1_,
int extraInt2_,
int extraInt3_) :
protocolVersion(protocolVersion_),
token(token_),
address(address_),
dumpChildren(dumpChildren_),
extraInt0(extraInt0_),
extraInt1(extraInt1_),
extraInt2(extraInt2_),
extraInt3(extraInt3_)
{
}
QString CdbDumperHelper::DumperInputParameters::command(const QString &dumpFunction) const
{
QString rc;
QTextStream str(&rc);
str.setIntegerBase(16);
str << ".call " << dumpFunction << '(' << protocolVersion << ',' << token << ',';
str.setIntegerBase(16);
str << "0x" << address;
str.setIntegerBase(10);
str << ',' << (dumpChildren ? 1 : 0) << ',' << extraInt0 << ',' << extraInt1
<< ',' << extraInt2 << ',' << extraInt3 << ')';
return rc;
}
// ------------------- CdbDumperHelper
CdbDumperHelper::CdbDumperHelper(CdbComInterfaces *cif) :
m_state(NotLoaded),
m_cif(cif)
m_cif(cif),
m_inBufferAddress(0),
m_inBufferSize(0),
m_outBufferAddress(0),
m_outBufferSize(0),
m_buffer(0)
{
}
CdbDumperHelper::~CdbDumperHelper()
{
clearBuffer();
}
void CdbDumperHelper::clearBuffer()
{
if (m_buffer) {
delete [] m_buffer;
m_buffer = 0;
}
}
void CdbDumperHelper::reset(const QString &library, bool enabled)
{
m_library = library;
m_state = enabled ? NotLoaded : Disabled;
m_dumpObjectSymbol = QLatin1String("qDumpObjectData440");
m_errorMessage.clear();
m_knownTypes.clear();
m_qtVersion.clear();
m_qtNamespace.clear();
m_inBufferAddress = m_outBufferAddress = 0;
m_inBufferSize = m_outBufferSize = 0;
clearBuffer();
}
bool CdbDumperHelper::moduleLoadHook(const QString &name, bool *ignoreNextBreakPoint)
// Attempt to load and initialize dumpers, give feedback
// to user.
void CdbDumperHelper::load(DebuggerManager *manager, IDebuggerManagerAccessForEngines *access)
{
*ignoreNextBreakPoint = false;
bool ok = true; // report failure only once
switch (m_state) {
case Disabled:
break;
case NotLoaded:
// Load once QtCore is there.
if (name.contains(QLatin1String("QtCore"))) {
if (loadDebug)
qDebug() << Q_FUNC_INFO << '\n' << name << m_state << executionStatusString(m_cif->debugControl);
ok = debuggeeLoadLibrary(m_cif->debugControl, m_cif->debugClient, m_cif->debugSymbols, m_cif->debugDataSpaces,
m_library, &m_errorMessage);
if (ok) {
m_state = Loading;
*ignoreNextBreakPoint = true;
} else {
m_state = Failed;
}
if (loadDebug)
qDebug() << m_state << executionStatusString(m_cif->debugControl);
enum Result { Failed, Succeeded, NoQtApp };
if (m_state != NotLoaded)
return;
manager->showStatusMessage(QCoreApplication::translate("CdbDumperHelper", "Loading dumpers..."), 10000);
const QString messagePrefix = QLatin1String("dumper:");
QString message;
Result result = Failed;
do {
// Do we have Qt and are we already loaded by accident?
QStringList modules;
if (!getModuleNameList(m_cif->debugSymbols, &modules, &message))
break;
if (modules.filter(QLatin1String(qtCoreModuleNameC)).isEmpty()) {
message = QCoreApplication::translate("CdbDumperHelper", "The debugger does not appear to be Qt application.");
result = NoQtApp;
}
break;
case Loading:
// Hurray, loaded. Now resolve the symbols we need
if (name.contains(QLatin1String("gdbmacros"))) {
ok = resolveSymbols(&m_errorMessage);
if (ok) {
m_state = Loaded;
} else {
m_state = Failed;
// Make sure the dumper lib is loaded.