Start on dumper support for CDB, bug fixes.

Make command execution output shown, use Wide version
of Debug Callbacks and output, provide bases classes
for other output and event handlers, move common
dumper functionality into debugger manager.
This commit is contained in:
Friedemann Kleint
2009-04-20 16:40:50 +02:00
parent 892042636d
commit 3695ee3646
14 changed files with 836 additions and 191 deletions

View File

@@ -37,7 +37,8 @@ HEADERS += \
$$PWD/cdbmodules.h \
$$PWD/cdbassembler.h \
$$PWD/cdboptions.h \
$$PWD/cdboptionspage.h
$$PWD/cdboptionspage.h \
$$PWD/cdbdumperhelper.h
SOURCES += \
$$PWD/cdbdebugengine.cpp \
@@ -49,7 +50,8 @@ SOURCES += \
$$PWD/cdbmodules.cpp \
$$PWD/cdbassembler.cpp \
$$PWD/cdboptions.cpp \
$$PWD/cdboptionspage.cpp
$$PWD/cdboptionspage.cpp \
$$PWD/cdbdumperhelper.cpp
FORMS += $$PWD/cdboptionspagewidget.ui

View File

@@ -212,14 +212,11 @@ bool dissassemble(IDebugClient5 *client,
// in assembler code). We build a complete string first as line breaks
// may occur in-between messages.
StringOutputHandler stringHandler;
IDebugOutputCallbacksWide *oldHandler = CdbDebugOutputBase::getOutputCallback(client);
client->SetOutputCallbacksWide(&stringHandler);
OutputRedirector redir(client, &stringHandler);
// For some reason, we need to output to "all clients"
const HRESULT hr = ctl->OutputDisassemblyLines(DEBUG_OUTCTL_ALL_CLIENTS,
beforeLines, beforeLines + afterLines,
offset, flags, 0, 0, 0, 0);
client->SetOutputCallbacksWide(oldHandler);
if (FAILED(hr)) {
*errorMessage= QString::fromLatin1("Unable to dissamble at 0x%1: %2").
arg(QString::number(offset, 16), msgComFailed("OutputDisassemblyLines", hr));

View File

@@ -188,19 +188,7 @@ bool DebuggerEngineLibrary::init(const QString &path, QString *errorMessage)
return true;
}
// A class that sets an expression syntax on the debug control while in scope.
// Can be nested as it checks for the old value.
class SyntaxSetter {
Q_DISABLE_COPY(SyntaxSetter)
public:
explicit inline SyntaxSetter(IDebugControl4 *ctl, ULONG desiredSyntax);
inline ~SyntaxSetter();
private:
const ULONG m_desiredSyntax;
IDebugControl4 *m_ctl;
ULONG m_oldSyntax;
};
// ----- SyntaxSetter
SyntaxSetter::SyntaxSetter(IDebugControl4 *ctl, ULONG desiredSyntax) :
m_desiredSyntax(desiredSyntax),
m_ctl(ctl)
@@ -216,6 +204,17 @@ SyntaxSetter::~SyntaxSetter()
m_ctl->SetExpressionSyntax(m_oldSyntax);
}
// CdbComInterfaces
CdbComInterfaces::CdbComInterfaces() :
debugClient(0),
debugControl(0),
debugSystemObjects(0),
debugSymbols(0),
debugRegisters(0),
debugDataSpaces(0)
{
}
// --- CdbDebugEnginePrivate
CdbDebugEnginePrivate::CdbDebugEnginePrivate(DebuggerManager *parent,
@@ -225,13 +224,9 @@ CdbDebugEnginePrivate::CdbDebugEnginePrivate(DebuggerManager *parent,
m_hDebuggeeProcess(0),
m_hDebuggeeThread(0),
m_breakEventMode(BreakEventHandle),
m_dumper(&m_cif),
m_watchTimer(-1),
m_debugEventCallBack(engine),
m_pDebugClient(0),
m_pDebugControl(0),
m_pDebugSystemObjects(0),
m_pDebugSymbols(0),
m_pDebugRegisters(0),
m_engine(engine),
m_debuggerManager(parent),
m_debuggerManagerAccess(parent->engineInterface()),
@@ -250,42 +245,49 @@ bool CdbDebugEnginePrivate::init(QString *errorMessage)
// Initialize the COM interfaces
HRESULT hr;
hr = lib.debugCreate( __uuidof(IDebugClient5), reinterpret_cast<void**>(&m_pDebugClient));
hr = lib.debugCreate( __uuidof(IDebugClient5), reinterpret_cast<void**>(&m_cif.debugClient));
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Creation of IDebugClient5 failed: %1").arg(msgDebugEngineComResult(hr));
return false;
}
m_pDebugClient->SetOutputCallbacksWide(&m_debugOutputCallBack);
m_pDebugClient->SetEventCallbacks(&m_debugEventCallBack);
m_cif.debugClient->SetOutputCallbacksWide(&m_debugOutputCallBack);
m_cif.debugClient->SetEventCallbacksWide(&m_debugEventCallBack);
hr = lib.debugCreate( __uuidof(IDebugControl4), reinterpret_cast<void**>(&m_pDebugControl));
hr = lib.debugCreate( __uuidof(IDebugControl4), reinterpret_cast<void**>(&m_cif.debugControl));
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Creation of IDebugControl4 failed: %1").arg(msgDebugEngineComResult(hr));
return false;
}
m_pDebugControl->SetCodeLevel(DEBUG_LEVEL_SOURCE);
m_cif.debugControl->SetCodeLevel(DEBUG_LEVEL_SOURCE);
hr = lib.debugCreate( __uuidof(IDebugSystemObjects4), reinterpret_cast<void**>(&m_pDebugSystemObjects));
hr = lib.debugCreate( __uuidof(IDebugSystemObjects4), reinterpret_cast<void**>(&m_cif.debugSystemObjects));
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Creation of IDebugSystemObjects4 failed: %1").arg(msgDebugEngineComResult(hr));
return false;
}
hr = lib.debugCreate( __uuidof(IDebugSymbols3), reinterpret_cast<void**>(&m_pDebugSymbols));
hr = lib.debugCreate( __uuidof(IDebugSymbols3), reinterpret_cast<void**>(&m_cif.debugSymbols));
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Creation of IDebugSymbols3 failed: %1").arg(msgDebugEngineComResult(hr));
return false;
}
hr = lib.debugCreate( __uuidof(IDebugRegisters2), reinterpret_cast<void**>(&m_pDebugRegisters));
hr = lib.debugCreate( __uuidof(IDebugRegisters2), reinterpret_cast<void**>(&m_cif.debugRegisters));
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Creation of IDebugRegisters2 failed: %1").arg(msgDebugEngineComResult(hr));
return false;
}
hr = lib.debugCreate( __uuidof(IDebugDataSpaces4), reinterpret_cast<void**>(&m_cif.debugDataSpaces));
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Creation of IDebugDataSpaces4 failed: %1").arg(msgDebugEngineComResult(hr));
return false;
}
if (debugCDB)
qDebug() << QString::fromLatin1("CDB Initialization succeeded, interrupt time out %1s.").arg(getInterruptTimeOutSecs(m_pDebugControl));
qDebug() << QString::fromLatin1("CDB Initialization succeeded, interrupt time out %1s.").arg(getInterruptTimeOutSecs(m_cif.debugControl));
return true;
}
@@ -303,16 +305,18 @@ IDebuggerEngine *CdbDebugEngine::create(DebuggerManager *parent,
CdbDebugEnginePrivate::~CdbDebugEnginePrivate()
{
cleanStackTrace();
if (m_pDebugClient)
m_pDebugClient->Release();
if (m_pDebugControl)
m_pDebugControl->Release();
if (m_pDebugSystemObjects)
m_pDebugSystemObjects->Release();
if (m_pDebugSymbols)
m_pDebugSymbols->Release();
if (m_pDebugRegisters)
m_pDebugRegisters->Release();
if (m_cif.debugClient)
m_cif.debugClient->Release();
if (m_cif.debugControl)
m_cif.debugControl->Release();
if (m_cif.debugSystemObjects)
m_cif.debugSystemObjects->Release();
if (m_cif.debugSymbols)
m_cif.debugSymbols->Release();
if (m_cif.debugRegisters)
m_cif.debugRegisters->Release();
if (m_cif.debugDataSpaces)
m_cif.debugDataSpaces->Release();
}
void CdbDebugEnginePrivate::clearForRun()
@@ -395,6 +399,18 @@ void CdbDebugEnginePrivate::clearDisplay()
bool CdbDebugEngine::startDebugger()
{
m_d->clearDisplay();
// 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());
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);
dumperEnabled = false;
}
}
m_d->m_dumper.reset(dumperLibName, dumperEnabled);
m_d->m_debuggerManager->showStatusMessage("Starting Debugger", -1);
QString errorMessage;
bool rc = false;
@@ -435,7 +451,7 @@ bool CdbDebugEngine::startAttachDebugger(qint64 pid, QString *errorMessage)
{
// Need to aatrach invasively, otherwise, no notification signals
// for for CreateProcess/ExitProcess occur.
const HRESULT hr = m_d->m_pDebugClient->AttachProcess(NULL, pid,
const HRESULT hr = m_d->m_cif.debugClient->AttachProcess(NULL, pid,
DEBUG_ATTACH_INVASIVE_RESUME_PROCESS);
if (debugCDB)
qDebug() << "Attaching to " << pid << " returns " << hr;
@@ -461,10 +477,10 @@ bool CdbDebugEngine::startDebuggerWithExecutable(DebuggerStartMode sm, QString *
qDebug() << Q_FUNC_INFO <<filename;
const QFileInfo fi(filename);
m_d->m_pDebugSymbols->AppendImagePathWide(QDir::toNativeSeparators(fi.absolutePath()).utf16());
//m_pDebugSymbols->SetSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_DEBUG | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS);
m_d->m_pDebugSymbols->SetSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS);
//m_pDebugSymbols->AddSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_DEBUG | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS | SYMOPT_NO_IMAGE_SEARCH);
m_d->m_cif.debugSymbols->AppendImagePathWide(QDir::toNativeSeparators(fi.absolutePath()).utf16());
//m_cif.debugSymbols->SetSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_DEBUG | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS);
m_d->m_cif.debugSymbols->SetSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS);
//m_cif.debugSymbols->AddSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_DEBUG | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS | SYMOPT_NO_IMAGE_SEARCH);
// TODO console
const QString cmd = Core::Utils::AbstractProcess::createWinCommandline(filename, m_d->m_debuggerManager->m_processArgs);
@@ -476,7 +492,7 @@ bool CdbDebugEngine::startDebuggerWithExecutable(DebuggerStartMode sm, QString *
envData = Core::Utils::AbstractProcess::createWinEnvironment(Core::Utils::AbstractProcess::fixWinEnvironment(m_d->m_debuggerManager->m_environment));
env = reinterpret_cast<PCWSTR>(envData.data());
}
const HRESULT hr = m_d->m_pDebugClient->CreateProcess2Wide(NULL,
const HRESULT hr = m_d->m_cif.debugClient->CreateProcess2Wide(NULL,
const_cast<PWSTR>(cmd.utf16()),
&dbgopts,
sizeof(dbgopts),
@@ -490,6 +506,7 @@ bool CdbDebugEngine::startDebuggerWithExecutable(DebuggerStartMode sm, QString *
m_d->m_mode = sm;
}
m_d->m_debuggerManagerAccess->notifyInferiorRunning();
return true;
}
@@ -522,7 +539,7 @@ void CdbDebugEngine::exitDebugger()
m_d->interruptInterferiorProcess(&errorMessage);
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
}
hr = m_d->m_pDebugClient->DetachCurrentProcess();
hr = m_d->m_cif.debugClient->DetachCurrentProcess();
if (FAILED(hr))
errorMessage += msgComFailed("DetachCurrentProcess", hr);
if (debugCDB)
@@ -536,11 +553,11 @@ void CdbDebugEngine::exitDebugger()
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
}
// Terminate and wait for stop events.
hr = m_d->m_pDebugClient->TerminateCurrentProcess();
hr = m_d->m_cif.debugClient->TerminateCurrentProcess();
if (FAILED(hr))
errorMessage += msgComFailed("TerminateCurrentProcess", hr);
if (!wasRunning) {
hr = m_d->m_pDebugClient->TerminateProcesses();
hr = m_d->m_cif.debugClient->TerminateProcesses();
if (FAILED(hr))
errorMessage += msgComFailed("TerminateProcesses", hr);
}
@@ -633,7 +650,7 @@ void CdbDebugEngine::filterEvaluateWatchers(QList<WatchData> *wd, WatchHandler *
return;
// Filter out actual watchers. Ignore the "<Edit>" top level place holders
SyntaxSetter syntaxSetter(m_d->m_pDebugControl, DEBUG_EXPR_CPLUSPLUS);
SyntaxSetter syntaxSetter(m_d->m_cif.debugControl, DEBUG_EXPR_CPLUSPLUS);
const QString watcherPrefix = QLatin1String("watch.");
const QChar lessThan = QLatin1Char('<');
const QChar greaterThan = QLatin1Char('>');
@@ -701,7 +718,7 @@ void CdbDebugEngine::stepExec()
qDebug() << Q_FUNC_INFO;
m_d->clearForRun();
const HRESULT hr = m_d->m_pDebugControl->SetExecutionStatus(DEBUG_STATUS_STEP_INTO);
const HRESULT hr = m_d->m_cif.debugControl->SetExecutionStatus(DEBUG_STATUS_STEP_INTO);
Q_UNUSED(hr)
m_d->m_breakEventMode = CdbDebugEnginePrivate::BreakEventIgnoreOnce;
@@ -733,7 +750,7 @@ void CdbDebugEngine::stepOutExec()
}
IDebugBreakpoint2* pBP;
HRESULT hr = m_d->m_pDebugControl->AddBreakpoint2(DEBUG_BREAKPOINT_CODE, DEBUG_ANY_ID, &pBP);
HRESULT hr = m_d->m_cif.debugControl->AddBreakpoint2(DEBUG_BREAKPOINT_CODE, DEBUG_ANY_ID, &pBP);
if (FAILED(hr) || !pBP) {
errorMessage = QString::fromLatin1("Cannot create temporary breakpoint: %1").arg(msgDebugEngineComResult(hr));
break;
@@ -756,7 +773,7 @@ void CdbDebugEngine::nextExec()
qDebug() << Q_FUNC_INFO;
m_d->clearForRun();
const HRESULT hr = m_d->m_pDebugControl->SetExecutionStatus(DEBUG_STATUS_STEP_OVER);
const HRESULT hr = m_d->m_cif.debugControl->SetExecutionStatus(DEBUG_STATUS_STEP_OVER);
if (SUCCEEDED(hr)) {
startWatchTimer();
} else {
@@ -775,7 +792,7 @@ void CdbDebugEngine::nextIExec()
qDebug() << Q_FUNC_INFO;
m_d->clearForRun();
const HRESULT hr = m_d->m_pDebugControl->Execute(DEBUG_OUTCTL_THIS_CLIENT, "p", 0);
const HRESULT hr = m_d->m_cif.debugControl->Execute(DEBUG_OUTCTL_THIS_CLIENT, "p", 0);
if (SUCCEEDED(hr)) {
startWatchTimer();
} else {
@@ -795,7 +812,7 @@ bool CdbDebugEnginePrivate::continueInferiorProcess(QString *errorMessage)
{
if (debugCDB)
qDebug() << Q_FUNC_INFO;
const HRESULT hr = m_pDebugControl->SetExecutionStatus(DEBUG_STATUS_GO);
const HRESULT hr = m_cif.debugControl->SetExecutionStatus(DEBUG_STATUS_GO);
if (FAILED(hr)) {
*errorMessage = msgComFailed("SetExecutionStatus", hr);
return false;
@@ -807,7 +824,7 @@ bool CdbDebugEnginePrivate::continueInferiorProcess(QString *errorMessage)
bool CdbDebugEnginePrivate::continueInferior(QString *errorMessage)
{
ULONG executionStatus;
if (!getExecutionStatus(m_pDebugControl, &executionStatus, errorMessage))
if (!getExecutionStatus(m_cif.debugControl, &executionStatus, errorMessage))
return false;
if (debugCDB)
@@ -836,7 +853,7 @@ bool CdbDebugEnginePrivate::interruptInterferiorProcess(QString *errorMessage)
// Interrupt the interferior process without notifications
if (debugCDB) {
ULONG executionStatus;
getExecutionStatus(m_pDebugControl, &executionStatus, errorMessage);
getExecutionStatus(m_cif.debugControl, &executionStatus, errorMessage);
qDebug() << Q_FUNC_INFO << "\n ex=" << executionStatus;
}
@@ -845,10 +862,10 @@ bool CdbDebugEnginePrivate::interruptInterferiorProcess(QString *errorMessage)
return false;
}
#if 0
const HRESULT hr = m_pDebugControl->SetInterrupt(DEBUG_INTERRUPT_ACTIVE|DEBUG_INTERRUPT_EXIT);
const HRESULT hr = m_cif.debugControl->SetInterrupt(DEBUG_INTERRUPT_ACTIVE|DEBUG_INTERRUPT_EXIT);
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Unable to interrupt debuggee after %1s: %2").
arg(getInterruptTimeOutSecs(m_pDebugControl)).arg(msgComFailed("SetInterrupt", hr));
arg(getInterruptTimeOutSecs(m_cif.debugControl)).arg(msgComFailed("SetInterrupt", hr));
return false;
}
#endif
@@ -918,15 +935,16 @@ void CdbDebugEngine::assignValueInDebugger(const QString &expr, const QString &v
void CdbDebugEngine::executeDebuggerCommand(const QString &command)
{
QString errorMessage;
if (!executeDebuggerCommand(command, &errorMessage))
if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_d->m_cif.debugControl, command, &errorMessage))
qWarning("%s\n", qPrintable(errorMessage));
}
bool CdbDebugEngine::executeDebuggerCommand(const QString &command, QString *errorMessage)
bool CdbDebugEnginePrivate::executeDebuggerCommand(IDebugControl4 *ctrl, const QString &command, QString *errorMessage)
{
if (debugCDB)
qDebug() << Q_FUNC_INFO << command;
const HRESULT hr = m_d->m_pDebugControl->ExecuteWide(DEBUG_OUTCTL_THIS_CLIENT, command.utf16(), 0);
// output to all clients, else we do not see anything
const HRESULT hr = ctrl->ExecuteWide(DEBUG_OUTCTL_ALL_CLIENTS, command.utf16(), 0);
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Unable to execute '%1': %2").
arg(command, msgDebugEngineComResult(hr));
@@ -945,9 +963,9 @@ bool CdbDebugEngine::evaluateExpression(const QString &expression,
DEBUG_VALUE debugValue;
memset(&debugValue, 0, sizeof(DEBUG_VALUE));
// Original syntax must be restored, else setting breakpoints will fail.
SyntaxSetter syntaxSetter(m_d->m_pDebugControl, DEBUG_EXPR_CPLUSPLUS);
SyntaxSetter syntaxSetter(m_d->m_cif.debugControl, DEBUG_EXPR_CPLUSPLUS);
ULONG errorPosition = 0;
const HRESULT hr = m_d->m_pDebugControl->EvaluateWide(expression.utf16(),
const HRESULT hr = m_d->m_cif.debugControl->EvaluateWide(expression.utf16(),
DEBUG_VALUE_INVALID, &debugValue,
&errorPosition); if (FAILED(hr)) {
if (HRESULT_CODE(hr) == 517) {
@@ -959,7 +977,7 @@ bool CdbDebugEngine::evaluateExpression(const QString &expression,
}
return false;
}
*value = CdbSymbolGroupContext::debugValueToString(debugValue, m_d->m_pDebugControl, type);
*value = CdbSymbolGroupContext::debugValueToString(debugValue, m_d->m_cif.debugControl, type);
return true;
}
@@ -1044,7 +1062,7 @@ bool CdbDebugEnginePrivate::attemptBreakpointSynchronization(QString *errorMessa
// called again from the debug event handler.
ULONG dummy;
const bool wasRunning = !CDBBreakPoint::getBreakPointCount(m_pDebugControl, &dummy);
const bool wasRunning = !CDBBreakPoint::getBreakPointCount(m_cif.debugControl, &dummy);
if (debugCDB)
qDebug() << Q_FUNC_INFO << "\n Running=" << wasRunning;
@@ -1058,8 +1076,8 @@ bool CdbDebugEnginePrivate::attemptBreakpointSynchronization(QString *errorMessa
return true;
}
return CDBBreakPoint::synchronizeBreakPoints(m_pDebugControl,
m_pDebugSymbols,
return CDBBreakPoint::synchronizeBreakPoints(m_cif.debugControl,
m_cif.debugSymbols,
m_debuggerManagerAccess->breakHandler(),
errorMessage);
}
@@ -1085,7 +1103,7 @@ void CdbDebugEngine::reloadDisassembler()
QList<DisassemblerLine> lines;
QString errorMessage;
QApplication::setOverrideCursor(Qt::WaitCursor);
const bool drc = dissassemble(m_d->m_pDebugClient, m_d->m_pDebugControl, offset,
const bool drc = dissassemble(m_d->m_cif.debugClient, m_d->m_cif.debugControl, offset,
ContextLines, ContextLines, &lines, &errorMessage);
QApplication::restoreOverrideCursor();
if (drc) {
@@ -1126,7 +1144,7 @@ QList<Symbol> CdbDebugEngine::moduleSymbols(const QString &moduleName)
errorMessage = tr("Cannot retrieve symbols while the debuggee is running.");
break;
}
if (!getModuleSymbols(m_d->m_pDebugSymbols, moduleName, &rc, &errorMessage))
if (!getModuleSymbols(m_d->m_cif.debugSymbols, moduleName, &rc, &errorMessage))
break;
success = true;
} while (false);
@@ -1161,7 +1179,7 @@ void CdbDebugEngine::reloadRegisters()
qDebug() << Q_FUNC_INFO << intBase;
QList<Register> registers;
QString errorMessage;
if (!getRegisters(m_d->m_pDebugControl, m_d->m_pDebugRegisters, &registers, &errorMessage, intBase))
if (!getRegisters(m_d->m_cif.debugControl, m_d->m_cif.debugRegisters, &registers, &errorMessage, intBase))
qWarning("reloadRegisters() failed: %s\n", qPrintable(errorMessage));
m_d->m_debuggerManagerAccess->registerHandler()->setRegisters(registers);
}
@@ -1171,7 +1189,7 @@ void CdbDebugEngine::timerEvent(QTimerEvent* te)
if (te->timerId() != m_d->m_watchTimer)
return;
const HRESULT hr = m_d->m_pDebugControl->WaitForEvent(0, 1);
const HRESULT hr = m_d->m_cif.debugControl->WaitForEvent(0, 1);
if (debugCDB)
if (debugCDB > 1 || hr != S_FALSE)
qDebug() << Q_FUNC_INFO << "WaitForEvent" << m_d->m_debuggerManager->status() << msgDebugEngineComResult(hr);
@@ -1266,7 +1284,7 @@ void CdbDebugEnginePrivate::updateThreadList()
QString errorMessage;
do {
ULONG numberOfThreads;
HRESULT hr= m_pDebugSystemObjects->GetNumberThreads(&numberOfThreads);
HRESULT hr= m_cif.debugSystemObjects->GetNumberThreads(&numberOfThreads);
if (FAILED(hr)) {
errorMessage= msgComFailed("GetNumberThreads", hr);
break;
@@ -1274,7 +1292,7 @@ void CdbDebugEnginePrivate::updateThreadList()
const ULONG maxThreadIds = 256;
ULONG threadIds[maxThreadIds];
ULONG biggestThreadId = qMin(maxThreadIds, numberOfThreads - 1);
hr = m_pDebugSystemObjects->GetThreadIdsByIndex(0, biggestThreadId, threadIds, 0);
hr = m_cif.debugSystemObjects->GetThreadIdsByIndex(0, biggestThreadId, threadIds, 0);
if (FAILED(hr)) {
errorMessage= msgComFailed("GetThreadIdsByIndex", hr);
break;
@@ -1301,8 +1319,8 @@ void CdbDebugEnginePrivate::updateStackTrace()
QString errorMessage;
m_engine->reloadRegisters();
m_currentStackTrace =
CdbStackTraceContext::create(m_pDebugControl, m_pDebugSystemObjects,
m_pDebugSymbols, m_currentThreadId, &errorMessage);
CdbStackTraceContext::create(m_cif.debugControl, m_cif.debugSystemObjects,
m_cif.debugSymbols, m_currentThreadId, &errorMessage);
if (!m_currentStackTrace) {
qWarning("%s: failed to create trace context: %s", Q_FUNC_INFO, qPrintable(errorMessage));
return;
@@ -1329,17 +1347,36 @@ void CdbDebugEnginePrivate::updateStackTrace()
}
}
void CdbDebugEnginePrivate::updateModules()
{
QList<Module> modules;
QString errorMessage;
if (!getModuleList(m_pDebugSymbols, &modules, &errorMessage))
if (!getModuleList(m_cif.debugSymbols, &modules, &errorMessage))
qWarning("updateModules() failed: %s\n", qPrintable(errorMessage));
m_debuggerManagerAccess->modulesHandler()->setModules(modules);
}
void CdbDebugEnginePrivate::handleBreakpointEvent(PDEBUG_BREAKPOINT pBP)
static const char *dumperPrefixC = "dumper";
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)
{
Q_UNUSED(pBP)
if (debugCDB)

View File

@@ -33,6 +33,7 @@
#include "cdbdebugeventcallback.h"
#include "cdbdebugoutput.h"
#include "cdboptions.h"
#include "cdbdumperhelper.h"
#include "stackhandler.h"
#include "debuggermanager.h"
@@ -67,6 +68,31 @@ private:
DebugCreateFunction m_debugCreate;
};
// A class that sets an expression syntax on the debug control while in scope.
// Can be nested as it checks for the old value.
class SyntaxSetter {
Q_DISABLE_COPY(SyntaxSetter)
public:
explicit inline SyntaxSetter(IDebugControl4 *ctl, ULONG desiredSyntax);
inline ~SyntaxSetter();
private:
const ULONG m_desiredSyntax;
IDebugControl4 *m_ctl;
ULONG m_oldSyntax;
};
// helper struct to pass interfaces around
struct CdbComInterfaces
{
CdbComInterfaces();
IDebugClient5* debugClient;
IDebugControl4* debugControl;
IDebugSystemObjects4* debugSystemObjects;
IDebugSymbols3* debugSymbols;
IDebugRegisters2* debugRegisters;
IDebugDataSpaces4* debugDataSpaces;
};
struct CdbDebugEnginePrivate
{
enum HandleBreakEventMode { // Special modes for break event handler.
@@ -90,9 +116,10 @@ struct CdbDebugEnginePrivate
bool updateLocals(int frameIndex, WatchHandler *wh, QString *errorMessage);
void updateModules();
void handleBreakpointEvent(PDEBUG_BREAKPOINT pBP);
void handleBreakpointEvent(PDEBUG_BREAKPOINT2 pBP);
void cleanStackTrace();
void clearForRun();
void handleModuleLoad(const QString &);
CdbSymbolGroupContext *getStackFrameSymbolGroupContext(int frameIndex, QString *errorMessage) const;
void clearDisplay();
@@ -103,6 +130,8 @@ struct CdbDebugEnginePrivate
bool attemptBreakpointSynchronization(QString *errorMessage);
static bool executeDebuggerCommand(IDebugControl4 *ctrl, const QString &command, QString *errorMessage);
const QSharedPointer<CdbOptions> m_options;
HANDLE m_hDebuggeeProcess;
HANDLE m_hDebuggeeThread;
@@ -110,13 +139,10 @@ struct CdbDebugEnginePrivate
HandleBreakEventMode m_breakEventMode;
int m_watchTimer;
IDebugClient5* m_pDebugClient;
IDebugControl4* m_pDebugControl;
IDebugSystemObjects4* m_pDebugSystemObjects;
IDebugSymbols3* m_pDebugSymbols;
IDebugRegisters2* m_pDebugRegisters;
CdbComInterfaces m_cif;
CdbDebugEventCallback m_debugEventCallBack;
CdbDebugOutput m_debugOutputCallBack;
CdbDumperHelper m_dumper;
CdbDebugEngine* m_engine;
DebuggerManager *m_debuggerManager;

View File

@@ -38,12 +38,12 @@
namespace Debugger {
namespace Internal {
CdbDebugEventCallback::CdbDebugEventCallback(CdbDebugEngine* dbg) :
m_pEngine(dbg)
// CdbDebugEventCallbackBase
CdbDebugEventCallbackBase::CdbDebugEventCallbackBase()
{
}
STDMETHODIMP CdbDebugEventCallback::QueryInterface(
STDMETHODIMP CdbDebugEventCallbackBase::QueryInterface(
THIS_
IN REFIID InterfaceId,
OUT PVOID* Interface)
@@ -60,32 +60,170 @@ STDMETHODIMP CdbDebugEventCallback::QueryInterface(
}
}
STDMETHODIMP_(ULONG) CdbDebugEventCallback::AddRef(THIS)
STDMETHODIMP_(ULONG) CdbDebugEventCallbackBase::AddRef(THIS)
{
// This class is designed to be static so
// there's no true refcount.
return 1;
}
STDMETHODIMP_(ULONG) CdbDebugEventCallback::Release(THIS)
STDMETHODIMP_(ULONG) CdbDebugEventCallbackBase::Release(THIS)
{
// This class is designed to be static so
// there's no true refcount.
return 0;
}
STDMETHODIMP CdbDebugEventCallbackBase::Breakpoint(THIS_ __in PDEBUG_BREAKPOINT2)
{
return S_OK;
}
STDMETHODIMP CdbDebugEventCallbackBase::Exception(
THIS_
__in PEXCEPTION_RECORD64,
__in ULONG /* FirstChance */
)
{
return S_OK;
}
STDMETHODIMP CdbDebugEventCallbackBase::CreateThread(
THIS_
__in ULONG64 /* Handle */,
__in ULONG64 /* DataOffset */,
__in ULONG64 /* StartOffset */
)
{
return S_OK;
}
STDMETHODIMP CdbDebugEventCallbackBase::ExitThread(
THIS_
__in ULONG /* ExitCode */
)
{
return S_OK;
}
STDMETHODIMP CdbDebugEventCallbackBase::CreateProcess(
THIS_
__in ULONG64 /* ImageFileHandle */,
__in ULONG64 /* Handle */,
__in ULONG64 /* BaseOffset */,
__in ULONG /* ModuleSize */,
__in_opt PCWSTR /* ModuleName */,
__in_opt PCWSTR /* ImageName */,
__in ULONG /* CheckSum */,
__in ULONG /* TimeDateStamp */,
__in ULONG64 /* InitialThreadHandle */,
__in ULONG64 /* ThreadDataOffset */,
__in ULONG64 /* StartOffset */
)
{
return S_OK;
}
STDMETHODIMP CdbDebugEventCallbackBase::ExitProcess(
THIS_
__in ULONG /* ExitCode */
)
{
return S_OK;
}
STDMETHODIMP CdbDebugEventCallbackBase::LoadModule(
THIS_
__in ULONG64 /* ImageFileHandle */,
__in ULONG64 /* BaseOffset */,
__in ULONG /* ModuleSize */,
__in_opt PCWSTR /* ModuleName */,
__in_opt PCWSTR /* ImageName */,
__in ULONG /* CheckSum */,
__in ULONG /* TimeDateStamp */
)
{
return S_OK;
}
STDMETHODIMP CdbDebugEventCallbackBase::UnloadModule(
THIS_
__in_opt PCWSTR /* ImageBaseName */,
__in ULONG64 /* BaseOffset */
)
{
return S_OK;
}
STDMETHODIMP CdbDebugEventCallbackBase::SystemError(
THIS_
__in ULONG /* Error */,
__in ULONG /* Level */
)
{
return S_OK;
}
STDMETHODIMP CdbDebugEventCallbackBase::SessionStatus(
THIS_
__in ULONG /* Status */
)
{
return S_OK;
}
STDMETHODIMP CdbDebugEventCallbackBase::ChangeDebuggeeState(
THIS_
__in ULONG /* Flags */,
__in ULONG64 /* Argument */
)
{
return S_OK;
}
STDMETHODIMP CdbDebugEventCallbackBase::ChangeEngineState(
THIS_
__in ULONG /* Flags */,
__in ULONG64 /* Argument */
)
{
return S_OK;
}
STDMETHODIMP CdbDebugEventCallbackBase::ChangeSymbolState(
THIS_
__in ULONG /* Flags */,
__in ULONG64 /* Argument */
)
{
return S_OK;
}
IDebugEventCallbacksWide *CdbDebugEventCallbackBase::getEventCallback(IDebugClient5 *clnt)
{
IDebugEventCallbacksWide *rc = 0;
if (SUCCEEDED(clnt->GetEventCallbacksWide(&rc)))
return rc;
return 0;
}
// ---------- CdbDebugEventCallback
CdbDebugEventCallback::CdbDebugEventCallback(CdbDebugEngine* dbg) :
m_pEngine(dbg)
{
}
STDMETHODIMP CdbDebugEventCallback::GetInterestMask(THIS_ __out PULONG mask)
{
*mask = DEBUG_EVENT_CREATE_PROCESS | DEBUG_EVENT_EXIT_PROCESS
| DEBUG_EVENT_LOAD_MODULE | DEBUG_EVENT_UNLOAD_MODULE
| DEBUG_EVENT_CREATE_THREAD | DEBUG_EVENT_EXIT_THREAD
| DEBUG_EVENT_BREAKPOINT
| DEBUG_EVENT_EXCEPTION
;
| DEBUG_EVENT_EXCEPTION;
return S_OK;
}
STDMETHODIMP CdbDebugEventCallback::Breakpoint(THIS_ __in PDEBUG_BREAKPOINT Bp)
STDMETHODIMP CdbDebugEventCallback::Breakpoint(THIS_ __in PDEBUG_BREAKPOINT2 Bp)
{
if (debugCDB)
qDebug() << Q_FUNC_INFO;
@@ -150,8 +288,8 @@ STDMETHODIMP CdbDebugEventCallback::CreateProcess(
__in ULONG64 Handle,
__in ULONG64 BaseOffset,
__in ULONG ModuleSize,
__in_opt PCSTR ModuleName,
__in_opt PCSTR ImageName,
__in_opt PCWSTR ModuleName,
__in_opt PCWSTR ImageName,
__in ULONG CheckSum,
__in ULONG TimeDateStamp,
__in ULONG64 InitialThreadHandle,
@@ -175,7 +313,7 @@ STDMETHODIMP CdbDebugEventCallback::CreateProcess(
m_pEngine->m_d->m_debuggerManagerAccess->notifyInferiorRunning();
ULONG currentThreadId;
if (SUCCEEDED(m_pEngine->m_d->m_pDebugSystemObjects->GetThreadIdByHandle(InitialThreadHandle, &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;
@@ -201,8 +339,8 @@ STDMETHODIMP CdbDebugEventCallback::LoadModule(
__in ULONG64 ImageFileHandle,
__in ULONG64 BaseOffset,
__in ULONG ModuleSize,
__in_opt PCSTR ModuleName,
__in_opt PCSTR ImageName,
__in_opt PCWSTR ModuleName,
__in_opt PCWSTR ImageName,
__in ULONG CheckSum,
__in ULONG TimeDateStamp
)
@@ -216,13 +354,13 @@ STDMETHODIMP CdbDebugEventCallback::LoadModule(
Q_UNUSED(TimeDateStamp)
if (debugCDB > 1)
qDebug() << Q_FUNC_INFO << ModuleName;
m_pEngine->m_d->updateModules();
m_pEngine->m_d->handleModuleLoad(QString::fromUtf16(ModuleName));
return S_OK;
}
STDMETHODIMP CdbDebugEventCallback::UnloadModule(
THIS_
__in_opt PCSTR ImageBaseName,
__in_opt PCWSTR ImageBaseName,
__in ULONG64 BaseOffset
)
{
@@ -245,46 +383,28 @@ STDMETHODIMP CdbDebugEventCallback::SystemError(
return S_OK;
}
STDMETHODIMP CdbDebugEventCallback::SessionStatus(
THIS_
__in ULONG Status
)
// -----------IgnoreDebugEventCallback
IgnoreDebugEventCallback::IgnoreDebugEventCallback()
{
Q_UNUSED(Status)
}
STDMETHODIMP IgnoreDebugEventCallback::GetInterestMask(THIS_ __out PULONG mask)
{
*mask = 0;
return S_OK;
}
STDMETHODIMP CdbDebugEventCallback::ChangeDebuggeeState(
THIS_
__in ULONG Flags,
__in ULONG64 Argument
)
// --------- EventCallbackRedirector
EventCallbackRedirector::EventCallbackRedirector(IDebugClient5 *client, IDebugEventCallbacksWide *cb) :
m_client(client),
m_oldCb(CdbDebugEventCallbackBase::getEventCallback(client))
{
Q_UNUSED(Flags)
Q_UNUSED(Argument)
return S_OK;
client->SetEventCallbacksWide(cb);
}
STDMETHODIMP CdbDebugEventCallback::ChangeEngineState(
THIS_
__in ULONG Flags,
__in ULONG64 Argument
)
EventCallbackRedirector::~EventCallbackRedirector()
{
Q_UNUSED(Flags)
Q_UNUSED(Argument)
return S_OK;
}
STDMETHODIMP CdbDebugEventCallback::ChangeSymbolState(
THIS_
__in ULONG Flags,
__in ULONG64 Argument
)
{
Q_UNUSED(Flags)
Q_UNUSED(Argument)
return S_OK;
m_client->SetEventCallbacksWide(m_oldCb);
}
} // namespace Internal

View File

@@ -32,17 +32,21 @@
#include <windows.h>
#include <inc/dbgeng.h>
#include <QtCore/QtGlobal>
namespace Debugger {
namespace Internal {
class CdbDebugEngine;
class CdbDebugEventCallback : public IDebugEventCallbacks
// Base class for event callbacks that takes care
// Active X magic. Provides base implementations with
// the exception of GetInterestMask
class CdbDebugEventCallbackBase : public IDebugEventCallbacksWide
{
protected:
CdbDebugEventCallbackBase();
public:
explicit CdbDebugEventCallback(CdbDebugEngine* dbg);
// IUnknown.
STDMETHOD(QueryInterface)(
THIS_
@@ -58,14 +62,9 @@ public:
// IDebugEventCallbacks.
STDMETHOD(GetInterestMask)(
THIS_
__out PULONG mask
);
STDMETHOD(Breakpoint)(
THIS_
__in PDEBUG_BREAKPOINT Bp
__in PDEBUG_BREAKPOINT2 Bp
);
STDMETHOD(Exception)(
@@ -91,8 +90,8 @@ public:
__in ULONG64 Handle,
__in ULONG64 BaseOffset,
__in ULONG ModuleSize,
__in_opt PCSTR ModuleName,
__in_opt PCSTR ImageName,
__in_opt PCWSTR ModuleName,
__in_opt PCWSTR ImageName,
__in ULONG CheckSum,
__in ULONG TimeDateStamp,
__in ULONG64 InitialThreadHandle,
@@ -110,15 +109,15 @@ public:
__in ULONG64 ImageFileHandle,
__in ULONG64 BaseOffset,
__in ULONG ModuleSize,
__in_opt PCSTR ModuleName,
__in_opt PCSTR ImageName,
__in_opt PCWSTR ModuleName,
__in_opt PCWSTR ImageName,
__in ULONG CheckSum,
__in ULONG TimeDateStamp
);
STDMETHOD(UnloadModule)(
THIS_
__in_opt PCSTR ImageBaseName,
__in_opt PCWSTR ImageBaseName,
__in ULONG64 BaseOffset
);
@@ -151,10 +150,116 @@ public:
__in ULONG64 Argument
);
static IDebugEventCallbacksWide *getEventCallback(IDebugClient5 *clnt);
};
class CdbDebugEventCallback : public CdbDebugEventCallbackBase
{
public:
explicit CdbDebugEventCallback(CdbDebugEngine* dbg);
// IDebugEventCallbacks.
STDMETHOD(GetInterestMask)(
THIS_
__out PULONG mask
);
STDMETHOD(Breakpoint)(
THIS_
__in PDEBUG_BREAKPOINT2 Bp
);
STDMETHOD(Exception)(
THIS_
__in PEXCEPTION_RECORD64 Exception,
__in ULONG FirstChance
);
STDMETHOD(CreateThread)(
THIS_
__in ULONG64 Handle,
__in ULONG64 DataOffset,
__in ULONG64 StartOffset
);
STDMETHOD(ExitThread)(
THIS_
__in ULONG ExitCode
);
STDMETHOD(CreateProcess)(
THIS_
__in ULONG64 ImageFileHandle,
__in ULONG64 Handle,
__in ULONG64 BaseOffset,
__in ULONG ModuleSize,
__in_opt PCWSTR ModuleName,
__in_opt PCWSTR ImageName,
__in ULONG CheckSum,
__in ULONG TimeDateStamp,
__in ULONG64 InitialThreadHandle,
__in ULONG64 ThreadDataOffset,
__in ULONG64 StartOffset
);
STDMETHOD(ExitProcess)(
THIS_
__in ULONG ExitCode
);
STDMETHOD(LoadModule)(
THIS_
__in ULONG64 ImageFileHandle,
__in ULONG64 BaseOffset,
__in ULONG ModuleSize,
__in_opt PCWSTR ModuleName,
__in_opt PCWSTR ImageName,
__in ULONG CheckSum,
__in ULONG TimeDateStamp
);
STDMETHOD(UnloadModule)(
THIS_
__in_opt PCWSTR ImageBaseName,
__in ULONG64 BaseOffset
);
STDMETHOD(SystemError)(
THIS_
__in ULONG Error,
__in ULONG Level
);
private:
CdbDebugEngine *m_pEngine;
};
// Event handler that ignores everything
class IgnoreDebugEventCallback : public CdbDebugEventCallbackBase
{
public:
explicit IgnoreDebugEventCallback();
STDMETHOD(GetInterestMask)(
THIS_
__out PULONG mask
);
};
// Utility class to temporarily redirect events to another handler
// as long as in scope
class EventCallbackRedirector {
Q_DISABLE_COPY(EventCallbackRedirector)
public:
explicit EventCallbackRedirector(IDebugClient5 *client, IDebugEventCallbacksWide *cb);
~EventCallbackRedirector();
private:
IDebugClient5 *m_client;
IDebugEventCallbacksWide *m_oldCb;
};
} // namespace Internal
} // namespace Debugger

View File

@@ -155,5 +155,18 @@ void CdbDebugOutput::output(ULONG mask, const QString &msg)
}
}
// Utility class to temporarily redirect output to another handler
// as long as in scope
OutputRedirector::OutputRedirector(IDebugClient5 *client, IDebugOutputCallbacksWide *newHandler) :
m_client(client),
m_oldHandler(CdbDebugOutputBase::getOutputCallback(client))
{
m_client->SetOutputCallbacksWide(newHandler);
}
OutputRedirector::~OutputRedirector()
{
m_client->SetOutputCallbacksWide(m_oldHandler);
}
} // namespace Internal
} // namespace Debugger

View File

@@ -105,6 +105,18 @@ private:
QString m_result;
};
// Utility class to temporarily redirect output to another handler
// as long as in scope
class OutputRedirector {
Q_DISABLE_COPY(OutputRedirector)
public:
explicit OutputRedirector(IDebugClient5 *client, IDebugOutputCallbacksWide *newHandler);
~OutputRedirector();
private:
IDebugClient5 *m_client;
IDebugOutputCallbacksWide *m_oldHandler;
};
} // namespace Internal
} // namespace Debugger

View File

@@ -0,0 +1,223 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Qt Software Information (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at qt-sales@nokia.com.
**
**************************************************************************/
#include "cdbdumperhelper.h"
#include "cdbmodules.h"
#include "cdbdebugengine_p.h"
#include "cdbdebugoutput.h"
#include "cdbdebugeventcallback.h"
#include <QtCore/QRegExp>
enum { loadDebug = 0 };
namespace Debugger {
namespace Internal {
// Alloc memory in debuggee using the ".dvalloc" command as
// there seems to be no API for it.
static bool allocDebuggeeMemory(IDebugControl4 *ctl,
IDebugClient5 *client,
int size, ULONG64 *address, QString *errorMessage)
{
*address = 0;
const QString allocCmd = QLatin1String(".dvalloc ") + QString::number(size);
StringOutputHandler stringHandler;
OutputRedirector redir(client, &stringHandler);
if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, allocCmd, errorMessage))
return false;
// "Allocated 1000 bytes starting at 003a0000" .. hopefully never localized
bool ok = false;
const QString output = stringHandler.result();
const int lastBlank = output.lastIndexOf(QLatin1Char(' '));
if (lastBlank != -1) {
const qint64 addri = output.mid(lastBlank + 1).toLongLong(&ok, 16);
if (ok)
*address = addri;
}
if (!ok) {
*errorMessage = QString::fromLatin1("Failed to parse output '%1'").arg(output);
return false;
}
return true;
}
// Alloc an AscII string in debuggee
static bool createDebuggeeAscIIString(IDebugControl4 *ctl,
IDebugClient5 *client,
IDebugDataSpaces4 *data,
const QString &s,
ULONG64 *address,
QString *errorMessage)
{
QByteArray sAsciiData = s.toLocal8Bit();
sAsciiData += '\0';
if (!allocDebuggeeMemory(ctl, client, sAsciiData.size(), address, errorMessage))
return false;
const HRESULT hr = data->WriteVirtual(*address, sAsciiData.data(), sAsciiData.size(), 0);
if (FAILED(hr)) {
*errorMessage= msgComFailed("WriteVirtual", hr);
return false;
}
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(IDebugSymbols3 *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
// make sense for Qt apps.
static bool debuggeeLoadLibrary(IDebugControl4 *ctl,
IDebugClient5 *client,
IDebugSymbols3 *syms,
IDebugDataSpaces4 *data,
const QString &moduleName, QString *errorMessage)
{
if (loadDebug)
qDebug() << Q_FUNC_INFO << moduleName;
// Try to ignore the breakpoints
IgnoreDebugEventCallback devNull;
EventCallbackRedirector eventRedir(client, &devNull);
// Make a call to LoadLibraryA. First, reserve memory in debugger
// and copy name over.
ULONG64 nameAddress;
if (!createDebuggeeAscIIString(ctl, client, data, moduleName, &nameAddress, errorMessage))
return false;
// We want to call "HMODULE LoadLibraryA(LPCTSTR lpFileName)"
// (void* LoadLibraryA(char*)). However, despite providing a symbol
// 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())
return false;
QString callCmd = QLatin1String(".call ");
callCmd += dummyFunc;
callCmd += QLatin1String("(0x");
callCmd += QString::number(nameAddress, 16);
callCmd += QLatin1Char(')');
if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, callCmd, errorMessage))
return false;
if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, QLatin1String("r eip=Kernel32!LoadLibraryA"), errorMessage))
return false;
// This will hit a breakpoint
if (loadDebug)
qDebug() << " executing 'g'";
if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, QString(QLatin1Char('g')), errorMessage))
return false;
// @Todo: We cannot evaluate output here as it is asynchronous
return true;
}
CdbDumperHelper::CdbDumperHelper(CdbComInterfaces *cif) :
m_state(NotLoaded),
m_cif(cif)
{
}
void CdbDumperHelper::reset(const QString &library, bool enabled)
{
m_library = library;
m_state = enabled ? NotLoaded : Disabled;
m_dumpObjectSymbol = QLatin1String("qDumpObjectData440");
m_errorMessage.clear();
}
bool CdbDumperHelper::moduleLoadHook(const QString &name, bool *ignoreNextBreakPoint)
{
*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;
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;
}
}
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;
}
}
break;
case Loaded:
break;
case Failed:
break;
};
return ok;
}
bool CdbDumperHelper::resolveSymbols(QString *errorMessage)
{
// Resolve the symbols we need
m_dumpObjectSymbol = QLatin1String("qDumpObjectData440");
const bool rc = resolveSymbol(m_cif->debugSymbols, &m_dumpObjectSymbol, errorMessage) == ResolveSymbolOk;
if (loadDebug)
qDebug() << Q_FUNC_INFO << '\n' << rc << m_dumpObjectSymbol;
return rc;
}
} // namespace Internal
} // namespace Debugger

View File

@@ -0,0 +1,87 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Qt Software Information (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at qt-sales@nokia.com.
**
**************************************************************************/
#ifndef CDBDUMPERHELPER_H
#define CDBDUMPERHELPER_H
#include <QtCore/QString>
namespace Debugger {
namespace Internal {
struct CdbComInterfaces;
// For code clarity, all the stuff related to custom dumpers
// goes here.
// "Custom dumper" is a library compiled against the current
// Qt containing functions to evaluate values of Qt classes
// (such as QString, taking pointers to their addresses).
// The library must be loaded into the debuggee.
class CdbDumperHelper
{
public:
enum State {
Disabled,
NotLoaded,
Loading,
Loaded,
Failed
};
explicit CdbDumperHelper(CdbComInterfaces *cif);
// Call before starting the debugger
void reset(const QString &library, bool enabled);
// Call from the module loaded event handler.
// It will load the dumper library and resolve the required symbols
// when appropriate.
bool moduleLoadHook(const QString &name, bool *ignoreNextBreakPoint);
State state() const { return m_state; }
QString errorMessage() const { return m_errorMessage; }
QString library() const { return m_library; }
private:
bool resolveSymbols(QString *errorMessage);
State m_state;
CdbComInterfaces *m_cif;
QString m_library;
QString m_dumpObjectSymbol;
QString m_errorMessage;
};
} // namespace Internal
} // namespace Debugger
#endif // CDBDUMPERHELPER_H

View File

@@ -56,6 +56,7 @@
#ifdef Q_OS_WIN
# include "peutils.h"
#endif
#include <coreplugin/icore.h>
#include <utils/qtcassert.h>
#include <QtCore/QDebug>
@@ -79,6 +80,7 @@
#include <QtGui/QTextCursor>
#include <QtGui/QToolBar>
#include <QtGui/QToolButton>
#include <QtGui/QPushButton>
#include <QtGui/QToolTip>
using namespace Debugger;
@@ -1466,6 +1468,52 @@ void DebuggerManager::reloadRegisters()
m_engine->reloadRegisters();
}
//////////////////////////////////////////////////////////////////////
//
// Dumpers. "Custom dumpers" are a library compiled against the current
// Qt containing functions to evaluate values of Qt classes
// (such as QString, taking pointers to their addresses).
// The library must be loaded into the debuggee.
//
//////////////////////////////////////////////////////////////////////
bool DebuggerManager::qtDumperLibraryEnabled() const
{
return theDebuggerBoolSetting(UseDebuggingHelpers);
}
QString DebuggerManager::qtDumperLibraryName() const
{
if (theDebuggerAction(UseCustomDebuggingHelperLocation)->value().toBool())
return theDebuggerAction(CustomDebuggingHelperLocation)->value().toString();
return m_dumperLib;
}
void DebuggerManager::showQtDumperLibraryWarning(const QString &details)
{
QMessageBox dialog(mainWindow());
QPushButton *qtPref = dialog.addButton(tr("Open Qt preferences"), QMessageBox::ActionRole);
QPushButton *helperOff = dialog.addButton(tr("Turn helper usage off"), QMessageBox::ActionRole);
QPushButton *justContinue = dialog.addButton(tr("Continue anyway"), QMessageBox::AcceptRole);
dialog.setDefaultButton(justContinue);
dialog.setWindowTitle(tr("Debugging helper missing"));
dialog.setText(tr("The debugger did not find the debugging helper library."));
dialog.setInformativeText(tr("The debugging helper is used to nicely format the values of Qt "
"data types and some STL data types. "
"It must be compiled for each Qt version, "
"you can do this in the Qt preferences page by selecting "
"a Qt installation and clicking on 'Rebuild' for the debugging "
"helper."));
if (!details.isEmpty())
dialog.setDetailedText(details);
dialog.exec();
if (dialog.clickedButton() == qtPref) {
Core::ICore::instance()->showOptionsDialog(QLatin1String("Qt4"), QLatin1String("Qt Versions"));
} else if (dialog.clickedButton() == helperOff) {
theDebuggerAction(UseDebuggingHelpers)->setValue(qVariantFromValue(false), false);
}
}
//////////////////////////////////////////////////////////////////////
//

View File

@@ -164,6 +164,8 @@ private:
virtual SourceFilesWindow *sourceFileWindow() = 0;
virtual void showApplicationOutput(const QString &data) = 0;
virtual void showDebuggerOutput(const QString &prefix, const QString &msg) = 0;
virtual void showDebuggerInput(const QString &prefix, const QString &msg) = 0;
virtual void reloadDisassembler() = 0;
virtual void reloadModules() = 0;
@@ -250,6 +252,10 @@ public slots:
void showStatusMessage(const QString &msg, int timeout = -1); // -1 forever
bool qtDumperLibraryEnabled() const;
QString qtDumperLibraryName() const;
void showQtDumperLibraryWarning(const QString &details);
private slots:
void showDebuggerOutput(const QString &prefix, const QString &msg);
void showDebuggerInput(const QString &prefix, const QString &msg);

View File

@@ -4128,36 +4128,6 @@ void GdbEngine::assignValueInDebugger(const QString &expression, const QString &
sendCommand("-var-assign assign " + value, WatchVarAssign);
}
QString GdbEngine::dumperLibraryName() const
{
if (theDebuggerAction(UseCustomDebuggingHelperLocation)->value().toBool())
return theDebuggerAction(CustomDebuggingHelperLocation)->value().toString();
return q->m_dumperLib;
}
void GdbEngine::showDebuggingHelperWarning()
{
QMessageBox dialog(q->mainWindow());
QPushButton *qtPref = dialog.addButton(tr("Open Qt preferences"), QMessageBox::ActionRole);
QPushButton *helperOff = dialog.addButton(tr("Turn helper usage off"), QMessageBox::ActionRole);
QPushButton *justContinue = dialog.addButton(tr("Continue anyway"), QMessageBox::AcceptRole);
dialog.setDefaultButton(justContinue);
dialog.setWindowTitle(tr("Debugging helper missing"));
dialog.setText(tr("The debugger did not find the debugging helper library."));
dialog.setInformativeText(tr("The debugging helper is used to nicely format the values of Qt "
"data types and some STL data types. "
"It must be compiled for each Qt version, "
"you can do this in the Qt preferences page by selecting "
"a Qt installation and clicking on 'Rebuild' for the debugging "
"helper."));
dialog.exec();
if (dialog.clickedButton() == qtPref) {
Core::ICore::instance()->showOptionsDialog("Qt4", "Qt Versions");
} else if (dialog.clickedButton() == helperOff) {
theDebuggerAction(UseDebuggingHelpers)->setValue(qVariantFromValue(false), false);
}
}
void GdbEngine::tryLoadDebuggingHelpers()
{
if (m_debuggingHelperState != DebuggingHelperUninitialized)
@@ -4165,16 +4135,16 @@ void GdbEngine::tryLoadDebuggingHelpers()
PENDING_DEBUG("TRY LOAD CUSTOM DUMPERS");
m_debuggingHelperState = DebuggingHelperUnavailable;
QString lib = dumperLibraryName();
if (!q->qtDumperLibraryEnabled())
return;
const QString lib = q->qtDumperLibraryName();
//qDebug() << "DUMPERLIB: " << lib;
if (!QFileInfo(lib).exists()) {
debugMessage(QString("DEBUG HELPER LIBRARY IS NOT USABLE: "
" %1 EXISTS: %2, EXECUTABLE: %3").arg(lib)
.arg(QFileInfo(lib).exists())
.arg(QFileInfo(lib).isExecutable()));
if (theDebuggerBoolSetting(UseDebuggingHelpers))
showDebuggingHelperWarning();
// @TODO: same in CDB engine...
const QFileInfo fi(lib);
if (!fi.exists()) {
const QString msg = tr("The dumper library '%1' does not exist.").arg(lib);
debugMessage(msg);
q->showQtDumperLibraryWarning(msg);
return;
}

View File

@@ -137,7 +137,7 @@ private:
//
// Own stuff
//
void showDebuggingHelperWarning();
int currentFrame() const;
QString currentWorkingDirectory() const { return m_pwd; }
@@ -202,7 +202,6 @@ private:
void handleTargetCore(const GdbResultRecord &response);
void handleExit(const GdbResultRecord &response);
void debugMessage(const QString &msg);
QString dumperLibraryName() const;
OutputCollector m_outputCollector;
QTextCodec *m_outputCodec;