diff --git a/src/plugins/debugger/cdb/cdb.pri b/src/plugins/debugger/cdb/cdb.pri index 6ac5997efa9c610c48920b59481b94bfe55bb5f6..050083339741180d4df7e8bf7167810346ab61a7 100644 --- a/src/plugins/debugger/cdb/cdb.pri +++ b/src/plugins/debugger/cdb/cdb.pri @@ -1,32 +1,8 @@ -# Detect presence of "Debugging Tools For Windows" -# in case VS compilers are used. +include(cdbcore.pri) -win32 { -contains(QMAKE_CXX, cl) { - -CDB_PATH="$$(CDB_PATH)" -isEmpty(CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows/sdk" - -!exists($$CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows (x86)/sdk" -!exists($$CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows (x64)/sdk" -!exists($$CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows 64-bit/sdk" - -exists($$CDB_PATH) { - -message("Adding support for $$CDB_PATH") - -DEFINES+=CDB_ENABLED - -CDB_PLATFORM=i386 - -INCLUDEPATH*=$$CDB_PATH -INCLUDEPATH*=$$PWD -DEPENDPATH*=$$PWD - -CDB_LIBPATH=$$CDB_PATH/lib/$$CDB_PLATFORM +!isEmpty(CDB_PATH) { HEADERS += \ - $$PWD/cdbcom.h \ $$PWD/cdbdebugengine.h \ $$PWD/cdbdebugengine_p.h \ $$PWD/cdbdebugeventcallback.h \ @@ -63,8 +39,4 @@ SOURCES += \ FORMS += $$PWD/cdboptionspagewidget.ui LIBS+=-lpsapi -} else { - message("Debugging Tools for Windows could not be found in $$CDB_PATH") -} # exists($$CDB_PATH) -} # (QMAKE_CXX, cl) -} # win32 +} diff --git a/src/plugins/debugger/cdb/cdbassembler.cpp b/src/plugins/debugger/cdb/cdbassembler.cpp index 268c664ddb0e20fe527f44ac6ff42f0a5c1f56b8..db21fbf1e76cc97db940c1022491c52a1b822edd 100644 --- a/src/plugins/debugger/cdb/cdbassembler.cpp +++ b/src/plugins/debugger/cdb/cdbassembler.cpp @@ -70,7 +70,7 @@ bool getRegisters(CIDebugControl *ctl, ULONG count; HRESULT hr = ireg->GetNumberRegisters(&count); if (FAILED(hr)) { - *errorMessage= msgComFailed("GetNumberRegisters", hr); + *errorMessage= CdbCore::msgComFailed("GetNumberRegisters", hr); return false; } if (!count) @@ -80,7 +80,7 @@ bool getRegisters(CIDebugControl *ctl, for (ULONG r = 0; r < count; r++) { hr = ireg->GetDescriptionWide(r, wszBuf, MAX_PATH - 1, 0, 0); if (FAILED(hr)) { - *errorMessage= msgComFailed("GetDescriptionWide", hr); + *errorMessage= CdbCore::msgComFailed("GetDescriptionWide", hr); return false; } Register reg; @@ -93,13 +93,13 @@ bool getRegisters(CIDebugControl *ctl, memset(valuesPtr, 0, count * sizeof(DEBUG_VALUE)); hr = ireg->GetValues(count, 0, 0, valuesPtr); if (FAILED(hr)) { - *errorMessage= msgComFailed("GetValues", hr); + *errorMessage= CdbCore::msgComFailed("GetValues", hr); return false; } if (base < 2) base = 10; for (ULONG r = 0; r < count; r++) - (*registers)[r].value = CdbSymbolGroupContext::debugValueToString(values.at(r), ctl, 0, base); + (*registers)[r].value = CdbCore::debugValueToString(values.at(r), 0, base, ctl); return true; } @@ -211,8 +211,7 @@ void DisassemblerOutputParser::parse(const QStringList &l) } } -bool dissassemble(CIDebugClient *client, - CIDebugControl *ctl, +bool dissassemble(CdbCore::CoreEngine *engine, ULONG64 offset, unsigned long beforeLines, unsigned long afterLines, @@ -222,26 +221,11 @@ bool dissassemble(CIDebugClient *client, { if (debugCDB) qDebug() << Q_FUNC_INFO << offset; - - const ULONG flags = DEBUG_DISASM_MATCHING_SYMBOLS|DEBUG_DISASM_SOURCE_LINE_NUMBER|DEBUG_DISASM_SOURCE_FILE_NAME; - // Catch the output by temporarily setting another handler. - // We use the method that outputs to the output handler as it - // conveniently provides the 'beforeLines' context (stepping back - // in assembler code). We build a complete string first as line breaks - // may occur in-between messages. - StringOutputHandler 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); - if (FAILED(hr)) { - *errorMessage= QString::fromLatin1("Unable to disassamble at 0x%1: %2"). - arg(offset, 0, 16).arg(msgComFailed("OutputDisassemblyLines", hr)); + QString lines; + if (!engine->dissassemble(offset, beforeLines, afterLines, &lines, errorMessage)) return false; - } DisassemblerOutputParser parser(str, addressFieldWidth); - parser.parse(stringHandler.result().split(QLatin1Char('\n'))); + parser.parse(lines.split(QLatin1Char('\n'))); return true; } diff --git a/src/plugins/debugger/cdb/cdbassembler.h b/src/plugins/debugger/cdb/cdbassembler.h index b029f67fd6d8d8304501a165d78ebe9e34ae77a4..a6ae15f098b4187d2be4647e87ce02dd4d62f560 100644 --- a/src/plugins/debugger/cdb/cdbassembler.h +++ b/src/plugins/debugger/cdb/cdbassembler.h @@ -39,6 +39,10 @@ QT_BEGIN_NAMESPACE class QTextStream; QT_END_NAMESPACE +namespace CdbCore { + class CoreEngine; +} + namespace Debugger { namespace Internal { @@ -51,8 +55,7 @@ bool getRegisters(CIDebugControl *ctl, QString *errorMessage, int base = 10 /* 16 for hex, etc */); -bool dissassemble(CIDebugClient *client, - CIDebugControl *ctl, +bool dissassemble(CdbCore::CoreEngine *engine, ULONG64 offset, unsigned long beforeLines, unsigned long afterLines, diff --git a/src/plugins/debugger/cdb/cdbbreakpoint.cpp b/src/plugins/debugger/cdb/cdbbreakpoint.cpp index 9ec69d3be35faff45bf6f86e975af591967fe4eb..6b3fda2c72740e8bad8bda2840f3a370a213b6ce 100644 --- a/src/plugins/debugger/cdb/cdbbreakpoint.cpp +++ b/src/plugins/debugger/cdb/cdbbreakpoint.cpp @@ -161,7 +161,7 @@ bool CDBBreakPoint::apply(CIDebugBreakpoint *ibp, QString *errorMessage) const const HRESULT hr = ibp->SetOffsetExpressionWide(reinterpret_cast<PCWSTR>(expr.utf16())); if (FAILED(hr)) { *errorMessage = QString::fromLatin1("Unable to set breakpoint '%1' : %2"). - arg(expr, msgComFailed("SetOffsetExpressionWide", hr)); + arg(expr, CdbCore::msgComFailed("SetOffsetExpressionWide", hr)); return false; } // Pass Count is ignoreCount + 1 @@ -192,7 +192,7 @@ bool CDBBreakPoint::add(CIDebugControl* debugControl, *id = 0; HRESULT hr = debugControl->AddBreakpoint2(DEBUG_BREAKPOINT_CODE, DEBUG_ANY_ID, &ibp); if (FAILED(hr)) { - *errorMessage = msgCannotAddBreakPoint(msgComFailed("AddBreakpoint2", hr)); + *errorMessage = msgCannotAddBreakPoint(CdbCore::msgComFailed("AddBreakpoint2", hr)); return false; } if (!ibp) { @@ -210,7 +210,7 @@ bool CDBBreakPoint::add(CIDebugControl* debugControl, if (id) { hr = ibp->GetId(id); if (FAILED(hr)) { - *errorMessage = msgCannotAddBreakPoint(msgComFailed("GetId", hr)); + *errorMessage = msgCannotAddBreakPoint(CdbCore::msgComFailed("GetId", hr)); return false; } } @@ -326,7 +326,7 @@ bool CDBBreakPoint::retrieve(CIDebugBreakpoint *ibp, QString *errorMessage) const HRESULT hr =ibp->GetOffsetExpressionWide(wszBuf, MAX_PATH, 0); if (FAILED(hr)) { *errorMessage = QString::fromLatin1("Cannot retrieve breakpoint: %1"). - arg(msgComFailed("GetOffsetExpressionWide", hr)); + arg(CdbCore::msgComFailed("GetOffsetExpressionWide", hr)); return false; } // Pass Count is ignoreCount + 1 @@ -398,7 +398,7 @@ bool CDBBreakPoint::getBreakPointCount(CIDebugControl* debugControl, ULONG *coun if (FAILED(hr)) { if (errorMessage) *errorMessage = QString::fromLatin1("Cannot determine breakpoint count: %1"). - arg(msgComFailed("GetNumberBreakpoints", hr)); + arg(CdbCore::msgComFailed("GetNumberBreakpoints", hr)); return false; } return true; @@ -416,7 +416,7 @@ bool CDBBreakPoint::getBreakPoints(CIDebugControl* debugControl, QList<CDBBreakP const HRESULT hr = debugControl->GetBreakpointByIndex2(b, &ibp); if (FAILED(hr)) { *errorMessage = QString::fromLatin1("Cannot retrieve breakpoint %1: %2"). - arg(b).arg(msgComFailed("GetBreakpointByIndex2", hr)); + arg(b).arg(CdbCore::msgComFailed("GetBreakpointByIndex2", hr)); return false; } CDBBreakPoint bp; @@ -438,7 +438,7 @@ static IDebugBreakpoint2 *breakPointById(CIDebugControl *ctl, unsigned long id, CIDebugBreakpoint *ibp = 0; const HRESULT hr = ctl->GetBreakpointById2(id, &ibp); if (FAILED(hr)) { - *errorMessage = msgNoBreakPointWithId(id, msgComFailed("GetBreakpointById2", hr)); + *errorMessage = msgNoBreakPointWithId(id, CdbCore::msgComFailed("GetBreakpointById2", hr)); return 0; } if (!ibp) { @@ -458,7 +458,7 @@ static bool removeBreakPointById(CIDebugControl *ctl, unsigned long id, QString return false; const HRESULT hr = ctl->RemoveBreakpoint2(ibp); if (FAILED(hr)) { - *errorMessage = QString::fromLatin1("Cannot remove breakpoint %1: %2").arg(id).arg(msgComFailed("RemoveBreakpointById2", hr)); + *errorMessage = QString::fromLatin1("Cannot remove breakpoint %1: %2").arg(id).arg(CdbCore::msgComFailed("RemoveBreakpointById2", hr)); return false; } return true; @@ -486,7 +486,7 @@ static bool setBreakPointEnabledById(CIDebugControl *ctl, unsigned long id, bool ULONG flags; HRESULT hr = ibp->GetFlags(&flags); if (FAILED(hr)) { - *errorMessage = msgCannotSetBreakPointEnabled(id, enabled, msgComFailed("GetFlags", hr)); + *errorMessage = msgCannotSetBreakPointEnabled(id, enabled, CdbCore::msgComFailed("GetFlags", hr)); return false; } const bool wasEnabled = (flags & DEBUG_BREAKPOINT_ENABLED); @@ -500,7 +500,7 @@ static bool setBreakPointEnabledById(CIDebugControl *ctl, unsigned long id, bool } hr = ibp->SetFlags(flags); if (FAILED(hr)) { - *errorMessage = msgCannotSetBreakPointEnabled(id, enabled, msgComFailed("SetFlags", hr)); + *errorMessage = msgCannotSetBreakPointEnabled(id, enabled, CdbCore::msgComFailed("SetFlags", hr)); return false; } return true; diff --git a/src/plugins/debugger/cdb/cdbcore.pri b/src/plugins/debugger/cdb/cdbcore.pri new file mode 100644 index 0000000000000000000000000000000000000000..1232f516b6e953a98215318ba123174bcc8c93e1 --- /dev/null +++ b/src/plugins/debugger/cdb/cdbcore.pri @@ -0,0 +1,43 @@ +# Detect presence of "Debugging Tools For Windows" +# in case VS compilers are used. + +win32 { +contains(QMAKE_CXX, cl) { + +CDB_PATH="$$(CDB_PATH)" +isEmpty(CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows/sdk" + +!exists($$CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows (x86)/sdk" +!exists($$CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows (x64)/sdk" +!exists($$CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows 64-bit/sdk" + +exists($$CDB_PATH) { + +message("Adding support for $$CDB_PATH") + +DEFINES+=CDB_ENABLED + +CDB_PLATFORM=i386 + +INCLUDEPATH*=$$CDB_PATH +CDB_LIBPATH=$$CDB_PATH/lib/$$CDB_PLATFORM + +HEADERS += \ + $$PWD/cdbcom.h \ + $$PWD/coreengine.h \ + $$PWD/debugoutputbase.h \ + $$PWD/debugeventcallbackbase.h +SOURCES += \ + $$PWD/coreengine.cpp \ + $$PWD/debugoutputbase.cpp \ + $$PWD/debugeventcallbackbase.cpp + +INCLUDEPATH*=$$PWD +DEPENDPATH*=$$PWD + +} else { + message("Debugging Tools for Windows could not be found in $$CDB_PATH") + CDB_PATH="" +} # exists($$CDB_PATH) +} # (QMAKE_CXX, cl) +} # win32 diff --git a/src/plugins/debugger/cdb/cdbdebugengine.cpp b/src/plugins/debugger/cdb/cdbdebugengine.cpp index fc4d0136fb5588e09273c0259c0d082108421b0e..26f269581e1f76f3d264305fd440e03f1a8a31f0 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine.cpp +++ b/src/plugins/debugger/cdb/cdbdebugengine.cpp @@ -29,6 +29,8 @@ #include "cdbdebugengine.h" #include "cdbdebugengine_p.h" +#include "cdbdebugoutput.h" +#include "cdbdebugeventcallback.h" #include "cdbstacktracecontext.h" #include "cdbstackframecontext.h" #include "cdbsymbolgroupcontext.h" @@ -71,10 +73,6 @@ #define DBGHELP_TRANSLATE_TCHAR #include <inc/Dbghelp.h> -static const char *dbgHelpDllC = "dbghelp"; -static const char *dbgEngineDllC = "dbgeng"; -static const char *debugCreateFuncC = "DebugCreate"; - static const char *localSymbolRootC = "local"; namespace Debugger { @@ -84,56 +82,18 @@ typedef QList<WatchData> WatchList; // ----- Message helpers -QString msgDebugEngineComResult(HRESULT hr) -{ - switch (hr) { - case S_OK: - return QLatin1String("S_OK"); - case S_FALSE: - return QLatin1String("S_FALSE"); - case E_FAIL: - break; - case E_INVALIDARG: - return QLatin1String("E_INVALIDARG"); - case E_NOINTERFACE: - return QLatin1String("E_NOINTERFACE"); - case E_OUTOFMEMORY: - return QLatin1String("E_OUTOFMEMORY"); - case E_UNEXPECTED: - return QLatin1String("E_UNEXPECTED"); - case 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 QLatin1String("E_FAIL ") + Utils::winErrorMessage(HRESULT_CODE(hr)); -} - static QString msgStackIndexOutOfRange(int idx, int size) { return QString::fromLatin1("Frame index %1 out of range (%2).").arg(idx).arg(size); } -QString msgComFailed(const char *func, HRESULT hr) -{ - return QString::fromLatin1("%1 failed: %2").arg(QLatin1String(func), msgDebugEngineComResult(hr)); -} - QString msgDebuggerCommandFailed(const QString &command, HRESULT hr) { - return QString::fromLatin1("Unable to execute '%1': %2").arg(command, msgDebugEngineComResult(hr)); + return QString::fromLatin1("Unable to execute '%1': %2").arg(command, CdbCore::msgDebugEngineComResult(hr)); } static const char *msgNoStackTraceC = "Internal error: no stack trace present."; -static inline QString msgLibLoadFailed(const QString &lib, const QString &why) -{ - return CdbDebugEngine::tr("Unable to load the debugger engine library '%1': %2"). - arg(lib, why); -} - // Format function failure message. Pass in Q_FUNC_INFO static QString msgFunctionFailed(const char *func, const QString &why) { @@ -152,148 +112,6 @@ static QString msgFunctionFailed(const char *func, const QString &why) // ----- Engine helpers -static inline ULONG getInterruptTimeOutSecs(CIDebugControl *ctl) -{ - ULONG rc = 0; - ctl->GetInterruptTimeout(&rc); - return rc; -} - -bool getExecutionStatus(CIDebugControl *ctl, - ULONG *executionStatus, - QString *errorMessage /* = 0 */) -{ - const HRESULT hr = ctl->GetExecutionStatus(executionStatus); - if (FAILED(hr)) { - if (errorMessage) - *errorMessage = msgComFailed("GetExecutionStatus", hr); - return false; - } - return true; -} - -const char *executionStatusString(ULONG executionStatus) -{ - switch (executionStatus) { - case DEBUG_STATUS_NO_CHANGE: - return "DEBUG_STATUS_NO_CHANGE"; - case DEBUG_STATUS_GO: - return "DEBUG_STATUS_GO"; - case DEBUG_STATUS_GO_HANDLED: - return "DEBUG_STATUS_GO_HANDLED"; - case DEBUG_STATUS_GO_NOT_HANDLED: - return "DEBUG_STATUS_GO_NOT_HANDLED"; - case DEBUG_STATUS_STEP_OVER: - return "DEBUG_STATUS_STEP_OVER"; - case DEBUG_STATUS_STEP_INTO: - return "DEBUG_STATUS_STEP_INTO"; - case DEBUG_STATUS_BREAK: - return "DEBUG_STATUS_BREAK"; - case DEBUG_STATUS_NO_DEBUGGEE: - return "DEBUG_STATUS_NO_DEBUGGEE"; - case DEBUG_STATUS_STEP_BRANCH: - return "DEBUG_STATUS_STEP_BRANCH"; - case DEBUG_STATUS_IGNORE_EVENT: - return "DEBUG_STATUS_IGNORE_EVENT"; - case DEBUG_STATUS_RESTART_REQUESTED: - return "DEBUG_STATUS_RESTART_REQUESTED"; - case DEBUG_STATUS_REVERSE_GO: - return "DEBUG_STATUS_REVERSE_GO"; - case DEBUG_STATUS_REVERSE_STEP_BRANCH: - return "DEBUG_STATUS_REVERSE_STEP_BRANCH"; - case DEBUG_STATUS_REVERSE_STEP_OVER: - return "DEBUG_STATUS_REVERSE_STEP_OVER"; - case DEBUG_STATUS_REVERSE_STEP_INTO: - return "DEBUG_STATUS_REVERSE_STEP_INTO"; - default: - break; - } - return "<Unknown execution status>"; -} - -// Debug convenience -const char *executionStatusString(CIDebugControl *ctl) -{ - ULONG executionStatus; - if (getExecutionStatus(ctl, &executionStatus)) - return executionStatusString(executionStatus); - return "<failed>"; -} - -// --------- DebuggerEngineLibrary -DebuggerEngineLibrary::DebuggerEngineLibrary() : - m_debugCreate(0) -{ -} - -// Build a lib name as "Path\x.dll" -static inline QString libPath(const QString &libName, const QString &path = QString()) -{ - QString rc = path; - if (!rc.isEmpty()) - rc += QDir::separator(); - rc += libName; - rc += QLatin1String(".dll"); - return rc; -} - -bool DebuggerEngineLibrary::init(const QString &path, - QString *dbgEngDLL, - QString *errorMessage) -{ - // Load the dependent help lib first - const QString helpLibPath = libPath(QLatin1String(dbgHelpDllC), path); - QLibrary helpLib(helpLibPath, 0); - if (!helpLib.isLoaded() && !helpLib.load()) { - *errorMessage = msgLibLoadFailed(helpLibPath, helpLib.errorString()); - return false; - } - // Load dbgeng lib - const QString engineLibPath = libPath(QLatin1String(dbgEngineDllC), path); - QLibrary lib(engineLibPath, 0); - if (!lib.isLoaded() && !lib.load()) { - *errorMessage = msgLibLoadFailed(engineLibPath, lib.errorString()); - return false; - } - *dbgEngDLL = engineLibPath; - // Locate symbols - void *createFunc = lib.resolve(debugCreateFuncC); - if (!createFunc) { - *errorMessage = CdbDebugEngine::tr("Unable to resolve '%1' in the debugger engine library '%2'"). - arg(QLatin1String(debugCreateFuncC), QLatin1String(dbgEngineDllC)); - return false; - } - m_debugCreate = static_cast<DebugCreateFunction>(createFunc); - return true; -} - -// ----- SyntaxSetter -SyntaxSetter::SyntaxSetter(CIDebugControl *ctl, ULONG desiredSyntax) : - m_desiredSyntax(desiredSyntax), - m_ctl(ctl) -{ - m_ctl->GetExpressionSyntax(&m_oldSyntax); - if (m_oldSyntax != m_desiredSyntax) - m_ctl->SetExpressionSyntax(m_desiredSyntax); -} - -SyntaxSetter::~SyntaxSetter() -{ - if (m_oldSyntax != m_desiredSyntax) - m_ctl->SetExpressionSyntax(m_oldSyntax); -} - -// CdbComInterfaces -CdbComInterfaces::CdbComInterfaces() : - debugClient(0), - debugControl(0), - debugSystemObjects(0), - debugSymbols(0), - debugRegisters(0), - debugDataSpaces(0) -{ -} - // --- CdbDebugEnginePrivate CdbDebugEnginePrivate::CdbDebugEnginePrivate(DebuggerManager *manager, @@ -303,82 +121,41 @@ CdbDebugEnginePrivate::CdbDebugEnginePrivate(DebuggerManager *manager, m_hDebuggeeProcess(0), m_hDebuggeeThread(0), m_breakEventMode(BreakEventHandle), - m_dumper(new CdbDumperHelper(manager, &m_cif)), + m_dumper(new CdbDumperHelper(manager, this)), m_currentThreadId(-1), m_eventThreadId(-1), m_interruptArticifialThreadId(-1), m_ignoreInitialBreakPoint(false), m_interrupted(false), - m_watchTimer(-1), - m_debugEventCallBack(engine), m_engine(engine), m_currentStackTrace(0), m_firstActivatedFrame(true), m_inferiorStartupComplete(false), m_mode(AttachCore) { + connect(this, SIGNAL(watchTimerDebugEvent()), this, SLOT(handleDebugEvent())); } bool CdbDebugEnginePrivate::init(QString *errorMessage) { enum { bufLen = 10240 }; - // Load the DLL - DebuggerEngineLibrary lib; - if (!lib.init(m_options->path, &m_dbengDLL, errorMessage)) - return false; - // Initialize the COM interfaces - HRESULT hr; - 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_cif.debugClient->SetOutputCallbacksWide(&m_debugOutputCallBack); - m_cif.debugClient->SetEventCallbacksWide(&m_debugEventCallBack); - - 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; - } - - setCodeLevel(); - - 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_cif.debugSymbols)); - if (FAILED(hr)) { - *errorMessage = QString::fromLatin1("Creation of IDebugSymbols3 failed: %1").arg(msgDebugEngineComResult(hr)); - return false; - } - - WCHAR buf[bufLen]; - hr = m_cif.debugSymbols->GetImagePathWide(buf, bufLen, 0); - if (FAILED(hr)) { - *errorMessage = msgComFailed("GetImagePathWide", hr); - return false; - } - m_baseImagePath = QString::fromUtf16(buf); - - 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)); + if (!CdbCore::CoreEngine::init(m_options->path, errorMessage)) return false; - } + CdbDebugOutput *output = new CdbDebugOutput; + setDebugOutput(DebugOutputBasePtr(output)); + connect(output, SIGNAL(debuggerOutput(int,QString)), + manager(), SLOT(showDebuggerOutput(int,QString))); + connect(output, SIGNAL(debuggerInputPrompt(int,QString)), + manager(), SLOT(showDebuggerInput(int,QString))); + connect(output, SIGNAL(debuggeeOutput(QString)), + manager(), SLOT(showApplicationOutput(QString))); + connect(output, SIGNAL(debuggeeInputPrompt(QString)), + manager(), SLOT(showApplicationOutput(QString))); + + setDebugEventCallback(DebugEventCallbackBasePtr(new CdbDebugEventCallback(m_engine))); + updateCodeLevel(); - if (debugCDB) - qDebug() << QString::fromLatin1("CDB Initialization succeeded, interrupt time out %1s.").arg(getInterruptTimeOutSecs(m_cif.debugControl)); return true; } @@ -395,49 +172,16 @@ IDebuggerEngine *CdbDebugEngine::create(Debugger::DebuggerManager *manager, return 0; } -// Adapt code level setting to the setting of the action. -static inline const char *codeLevelName(ULONG level) +void CdbDebugEnginePrivate::updateCodeLevel() { - return level == DEBUG_LEVEL_ASSEMBLY ? "assembly" : "source"; -} - -bool CdbDebugEnginePrivate::setCodeLevel() -{ - const ULONG codeLevel = theDebuggerBoolSetting(OperateByInstruction) ? - DEBUG_LEVEL_ASSEMBLY : DEBUG_LEVEL_SOURCE; - ULONG currentCodeLevel = DEBUG_LEVEL_ASSEMBLY; - HRESULT hr = m_cif.debugControl->GetCodeLevel(¤tCodeLevel); - if (FAILED(hr)) { - m_engine->warning(QString::fromLatin1("Cannot determine code level: %1").arg(msgComFailed("GetCodeLevel", hr))); - return true; - } - if (debugCDB) - qDebug() << Q_FUNC_INFO << "\nSetting code level to " << codeLevelName(codeLevel) << " (was" << codeLevelName(currentCodeLevel) << ')'; - if (currentCodeLevel == codeLevel) - return false; - hr = m_cif.debugControl->SetCodeLevel(codeLevel); - if (FAILED(hr)) { - m_engine->warning(QString::fromLatin1("Cannot set code level: %1").arg(msgComFailed("SetCodeLevel", hr))); - return false; - } - return true; + const CdbCore::CoreEngine::CodeLevel cl = theDebuggerBoolSetting(OperateByInstruction) ? + CdbCore::CoreEngine::CodeLevelAssembly : CdbCore::CoreEngine::CodeLevelSource; + setCodeLevel(cl); } CdbDebugEnginePrivate::~CdbDebugEnginePrivate() { cleanStackTrace(); - 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(); } DebuggerManager *CdbDebugEnginePrivate::manager() const @@ -477,14 +221,6 @@ CdbDebugEngine::CdbDebugEngine(DebuggerManager *manager, const QSharedPointer<Cd this, SLOT(slotConsoleStubStarted())); connect(&m_d->m_consoleStubProc, SIGNAL(wrapperStopped()), this, SLOT(slotConsoleStubTerminated())); - connect(&m_d->m_debugOutputCallBack, SIGNAL(debuggerOutput(int,QString)), - manager, SLOT(showDebuggerOutput(int,QString))); - connect(&m_d->m_debugOutputCallBack, SIGNAL(debuggerInputPrompt(int,QString)), - manager, SLOT(showDebuggerInput(int,QString))); - connect(&m_d->m_debugOutputCallBack, SIGNAL(debuggeeOutput(QString)), - manager, SLOT(showApplicationOutput(QString))); - connect(&m_d->m_debugOutputCallBack, SIGNAL(debuggeeInputPrompt(QString)), - manager, SLOT(showApplicationOutput(QString))); } CdbDebugEngine::~CdbDebugEngine() @@ -499,26 +235,6 @@ void CdbDebugEngine::setState(DebuggerState state, const char *func, int line) IDebuggerEngine::setState(state); } -void CdbDebugEngine::startWatchTimer() -{ - if (debugCDB) - qDebug() << Q_FUNC_INFO; - - if (m_d->m_watchTimer == -1) - m_d->m_watchTimer = startTimer(0); -} - -void CdbDebugEngine::killWatchTimer() -{ - if (debugCDB) - qDebug() << Q_FUNC_INFO; - - if (m_d->m_watchTimer != -1) { - killTimer(m_d->m_watchTimer); - m_d->m_watchTimer = -1; - } -} - void CdbDebugEngine::shutdown() { exitDebugger(); @@ -543,7 +259,7 @@ QString CdbDebugEngine::editorToolTip(const QString &exp, const QString &functio // No function/symbol context found, try to evaluate in current context. // Do not append type as this will mostly be 'long long' for integers, etc. QString type; - if (!evaluateExpression(exp, &rc, &type, &errorMessage)) + if (!m_d->evaluateExpression(exp, &rc, &type, &errorMessage)) return QString(); return rc; } @@ -601,7 +317,7 @@ void CdbDebugEnginePrivate::checkVersion() versionNotChecked = false; // Get engine DLL version QString errorMessage; - const QString version = Utils::winGetDLLVersion(Utils::WinDLLProductVersion, m_dbengDLL, &errorMessage); + const QString version = Utils::winGetDLLVersion(Utils::WinDLLProductVersion, dbengDLL(), &errorMessage); if (version.isEmpty()) { qWarning("%s\n", qPrintable(errorMessage)); return; @@ -663,12 +379,12 @@ void CdbDebugEngine::startDebugger(const QSharedPointer<DebuggerStartParameters> bool rc = false; bool needWatchTimer = false; m_d->clearForRun(); - m_d->setCodeLevel(); + m_d->updateCodeLevel(); m_d->m_ignoreInitialBreakPoint = false; switch (mode) { case AttachExternal: case AttachCrashedExternal: - rc = startAttachDebugger(sp->attachPID, mode, &errorMessage); + rc = m_d->startAttachDebugger(sp->attachPID, mode, &errorMessage); needWatchTimer = true; // Fetch away module load, etc. even if crashed break; case StartInternal: @@ -695,7 +411,7 @@ void CdbDebugEngine::startDebugger(const QSharedPointer<DebuggerStartParameters> } if (rc) { if (needWatchTimer) - startWatchTimer(); + m_d->startWatchTimer(); emit startSuccessful(); } else { warning(errorMessage); @@ -706,7 +422,7 @@ void CdbDebugEngine::startDebugger(const QSharedPointer<DebuggerStartParameters> bool CdbDebugEngine::startAttachDebugger(qint64 pid, DebuggerStartMode sm, QString *errorMessage) { - // Need to attrach invasively, otherwise, no notification signals + // Need to attach invasively, otherwise, no notification signals // for for CreateProcess/ExitProcess occur. // Initial breakpoint occur: // 1) Desired: When attaching to a crashed process @@ -716,75 +432,25 @@ bool CdbDebugEngine::startAttachDebugger(qint64 pid, DebuggerStartMode sm, QStri // by lookup at the state of the application (startup trap). However, // there is no startup trap when attaching to a process that has been // running for a while. (see notifyException). - ULONG flags = DEBUG_ATTACH_INVASIVE_RESUME_PROCESS; - if (manager()->startParameters()->startMode != AttachCrashedExternal) - flags |= DEBUG_ATTACH_INVASIVE_NO_INITIAL_BREAK; - const HRESULT hr = m_d->m_cif.debugClient->AttachProcess(NULL, pid, flags); - if (debugCDB) - qDebug() << "Attaching to " << pid << " using flags" << flags << " returns " << hr << executionStatusString(m_d->m_cif.debugControl); - if (FAILED(hr)) { - *errorMessage = tr("Attaching to a process failed for process id %1: %2").arg(pid).arg(msgDebugEngineComResult(hr)); - return false; - } else { + const bool suppressInitialBreakPoint = sm != AttachCrashedExternal; + const bool rc = m_d->startAttachDebugger(pid, suppressInitialBreakPoint, errorMessage); + if (rc) m_d->m_mode = sm; - } - return true; + return rc; } bool CdbDebugEngine::startDebuggerWithExecutable(DebuggerStartMode sm, QString *errorMessage) { - DEBUG_CREATE_PROCESS_OPTIONS dbgopts; - memset(&dbgopts, 0, sizeof(dbgopts)); - dbgopts.CreateFlags = DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS; - const QSharedPointer<DebuggerStartParameters> sp = manager()->startParameters(); - const QString filename(sp->executable); - // Set image path - const QFileInfo fi(filename); - QString imagePath = QDir::toNativeSeparators(fi.absolutePath()); - if (!m_d->m_baseImagePath.isEmpty()) { - imagePath += QLatin1Char(';'); - imagePath += m_d->m_baseImagePath; - } - HRESULT hr = m_d->m_cif.debugSymbols->SetImagePathWide(reinterpret_cast<PCWSTR>(imagePath.utf16())); - if (FAILED(hr)) { - *errorMessage = tr("Unable to set the image path to %1: %2").arg(imagePath, msgComFailed("SetImagePathWide", hr)); - return false; - } - - if (debugCDB) - qDebug() << Q_FUNC_INFO <<'\n' << filename << imagePath; - - ULONG symbolOptions = SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS; - if (m_d->m_options->verboseSymbolLoading) - symbolOptions |= SYMOPT_DEBUG; - m_d->m_cif.debugSymbols->SetSymbolOptions(symbolOptions); - - const QString cmd = Utils::AbstractProcess::createWinCommandline(filename, sp->processArgs); - if (debugCDB) - qDebug() << "Starting " << cmd; - PCWSTR env = 0; - QByteArray envData; - if (!sp->environment.empty()) { - envData = Utils::AbstractProcess::createWinEnvironment(Utils::AbstractProcess::fixWinEnvironment(sp->environment)); - env = reinterpret_cast<PCWSTR>(envData.data()); - } - // The working directory cannot be empty. - PCWSTR workingDirC = 0; - const QString workingDir = sp->workingDir.isEmpty() ? QString() : QDir::toNativeSeparators(sp->workingDir); - if (!workingDir.isEmpty()) - workingDirC = workingDir.utf16(); - hr = m_d->m_cif.debugClient->CreateProcess2Wide(NULL, - reinterpret_cast<PWSTR>(const_cast<ushort *>(cmd.utf16())), - &dbgopts, sizeof(dbgopts), - workingDirC, env); - if (FAILED(hr)) { - *errorMessage = tr("Unable to create a process '%1': %2").arg(cmd, msgDebugEngineComResult(hr)); - return false; - } else { + const bool rc = m_d->startDebuggerWithExecutable(sp->workingDir, + sp->executable, + sp->processArgs, + sp->environment, + m_d->m_options->verboseSymbolLoading, + errorMessage); + if (rc) m_d->m_mode = sm; - } - return true; + return rc; } void CdbDebugEnginePrivate::processCreatedAttached(ULONG64 processHandle, ULONG64 initialThreadHandle) @@ -792,7 +458,7 @@ void CdbDebugEnginePrivate::processCreatedAttached(ULONG64 processHandle, ULONG6 m_engine->setState(InferiorRunningRequested, Q_FUNC_INFO, __LINE__); setDebuggeeHandles(reinterpret_cast<HANDLE>(processHandle), reinterpret_cast<HANDLE>(initialThreadHandle)); ULONG currentThreadId; - if (SUCCEEDED(m_cif.debugSystemObjects->GetThreadIdByHandle(initialThreadHandle, ¤tThreadId))) { + if (SUCCEEDED(interfaces().debugSystemObjects->GetThreadIdByHandle(initialThreadHandle, ¤tThreadId))) { m_currentThreadId = currentThreadId; } else { m_currentThreadId = 0; @@ -810,20 +476,20 @@ void CdbDebugEnginePrivate::processCreatedAttached(ULONG64 processHandle, ULONG6 const QString crashParameter = manager()->startParameters()->crashParameter; if (!crashParameter.isEmpty()) { ULONG64 evtNr = crashParameter.toULongLong(); - const HRESULT hr = m_cif.debugControl->SetNotifyEventHandle(evtNr); + const HRESULT hr = interfaces().debugControl->SetNotifyEventHandle(evtNr); // Unless QtCreator is spawned by the debugger and inherits the handles, // the event handling does not work reliably // (that is, the crash event is not delivered). if (SUCCEEDED(hr)) { QTimer::singleShot(0, m_engine, SLOT(slotBreakAttachToCrashed())); } else { - m_engine->warning(QString::fromLatin1("Handshake failed on event #%1: %2").arg(evtNr).arg(msgComFailed("SetNotifyEventHandle", hr))); + m_engine->warning(QString::fromLatin1("Handshake failed on event #%1: %2").arg(evtNr).arg(CdbCore::msgComFailed("SetNotifyEventHandle", hr))); } } } m_engine->setState(InferiorRunning, Q_FUNC_INFO, __LINE__); if (debugCDBExecution) - qDebug() << "<processCreatedAttached" << executionStatusString(m_cif.debugControl); + qDebug() << "<processCreatedAttached"; } void CdbDebugEngine::processTerminated(unsigned long exitCode) @@ -851,41 +517,27 @@ bool CdbDebugEnginePrivate::endInferior(EndInferiorAction action, QString *error } bool success = false; switch (action) { - case DetachInferior: { - const HRESULT hr = m_cif.debugClient->DetachCurrentProcess(); - if (SUCCEEDED(hr)) { + case DetachInferior: + if (detachCurrentProcess(errorMessage)) success = true; - } else { - *errorMessage += msgComFailed("DetachCurrentProcess", hr); - } - } break; - case TerminateInferior: { + case TerminateInferior: do { // The exit process event handler will not be called. - HRESULT hr = m_cif.debugClient->TerminateCurrentProcess(); - if (FAILED(hr)) { - *errorMessage += msgComFailed("TerminateCurrentProcess", hr); - break; - } + terminateCurrentProcess(errorMessage); if (wasRunning) { success = true; break; } - hr = m_cif.debugClient->TerminateProcesses(); - if (SUCCEEDED(hr)) { + if (terminateProcesses(errorMessage)) success = true; - } else { - *errorMessage += msgComFailed("TerminateProcesses", hr); - } } while (false); QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); break; - } } // Perform cleanup even when failed..no point clinging to the process setDebuggeeHandles(0, 0); - m_engine->killWatchTimer(); + killWatchTimer(); m_engine->setState(success ? InferiorShutDown : InferiorShutdownFailed, Q_FUNC_INFO, __LINE__); return success; } @@ -929,10 +581,10 @@ void CdbDebugEnginePrivate::endDebugging(EndDebuggingMode em) // Clean up resources (open files, etc.) m_engine->setState(EngineShuttingDown, Q_FUNC_INFO, __LINE__); clearForRun(); - const HRESULT hr = m_cif.debugClient->EndSession(DEBUG_END_PASSIVE); + const bool endedCleanly = endSession(&errorMessage); m_engine->setState(DebuggerNotReady, Q_FUNC_INFO, __LINE__); - if (!SUCCEEDED(hr)) { - errorMessage = QString::fromLatin1("There were errors trying to end debugging: %1").arg(msgComFailed("EndSession", hr)); + if (!endedCleanly) { + errorMessage = QString::fromLatin1("There were errors trying to end debugging:\n%1").arg(errorMessage); manager()->showDebuggerOutput(LogError, errorMessage); } } @@ -965,7 +617,7 @@ void CdbDebugEngine::evaluateWatcher(WatchData *wd) QString errorMessage; QString value; QString type; - if (evaluateExpression(wd->exp, &value, &type, &errorMessage)) { + if (m_d->evaluateExpression(wd->exp, &value, &type, &errorMessage)) { wd->setValue(value); wd->setType(type); } else { @@ -1017,14 +669,14 @@ bool CdbDebugEnginePrivate::executeContinueCommand(const QString &command) if (debugCDB) qDebug() << Q_FUNC_INFO << command; clearForRun(); - setCodeLevel(); // Step by instruction + updateCodeLevel(); // Step by instruction m_engine->setState(InferiorRunningRequested, Q_FUNC_INFO, __LINE__); manager()->showDebuggerOutput(LogMisc, CdbDebugEngine::tr("Continuing with '%1'...").arg(command)); QString errorMessage; - const bool success = CdbDebugEnginePrivate::executeDebuggerCommand(m_cif.debugControl, command, &errorMessage); + const bool success = executeDebuggerCommand(command, &errorMessage); if (success) { m_engine->setState(InferiorRunning, Q_FUNC_INFO, __LINE__); - m_engine->startWatchTimer(); + startWatchTimer(); } else { m_engine->setState(InferiorStopped, Q_FUNC_INFO, __LINE__); m_engine->warning(CdbDebugEngine::tr("Unable to continue: %1").arg(errorMessage)); @@ -1077,14 +729,14 @@ bool CdbDebugEngine::step(unsigned long executionStatus) || m_d->m_currentThreadId == triggeringEventThread || manager()->threadsHandler()->threads().size() == 1; m_d->clearForRun(); // clears thread ids - m_d->setCodeLevel(); // Step by instruction or source line + m_d->updateCodeLevel(); // Step by instruction or source line setState(InferiorRunningRequested, Q_FUNC_INFO, __LINE__); bool success = false; if (sameThread && executionStatus != CdbExtendedExecutionStatusStepOut) { // Step event-triggering thread, use fast API - const HRESULT hr = m_d->m_cif.debugControl->SetExecutionStatus(executionStatus); + const HRESULT hr = m_d->interfaces().debugControl->SetExecutionStatus(executionStatus); success = SUCCEEDED(hr); if (!success) - warning(msgStepFailed(executionStatus, m_d->m_currentThreadId, msgComFailed("SetExecutionStatus", hr))); + warning(msgStepFailed(executionStatus, m_d->m_currentThreadId, CdbCore::msgComFailed("SetExecutionStatus", hr))); } else { // Need to use a command to explicitly specify the current thread QString command; @@ -1102,7 +754,7 @@ bool CdbDebugEngine::step(unsigned long executionStatus) break; } manager()->showDebuggerOutput(tr("Stepping %1").arg(command)); - const HRESULT hr = m_d->m_cif.debugControl->Execute(DEBUG_OUTCTL_THIS_CLIENT, command.toLatin1().constData(), DEBUG_EXECUTE_ECHO); + const HRESULT hr = m_d->interfaces().debugControl->Execute(DEBUG_OUTCTL_THIS_CLIENT, command.toLatin1().constData(), DEBUG_EXECUTE_ECHO); success = SUCCEEDED(hr); if (!success) warning(msgStepFailed(executionStatus, m_d->m_currentThreadId, msgDebuggerCommandFailed(command, hr))); @@ -1111,7 +763,7 @@ bool CdbDebugEngine::step(unsigned long executionStatus) // Oddity: Step into will first break at the calling function. Ignore if (executionStatus == DEBUG_STATUS_STEP_INTO || executionStatus == DEBUG_STATUS_REVERSE_STEP_INTO) m_d->m_breakEventMode = CdbDebugEnginePrivate::BreakEventIgnoreOnce; - startWatchTimer(); + m_d->startWatchTimer(); setState(InferiorRunning, Q_FUNC_INFO, __LINE__); } else { setState(InferiorStopped, Q_FUNC_INFO, __LINE__); @@ -1159,9 +811,9 @@ bool CdbDebugEnginePrivate::continueInferiorProcess(QString *errorMessagePtr /* { if (debugCDBExecution) qDebug() << "continueInferiorProcess"; - const HRESULT hr = m_cif.debugControl->SetExecutionStatus(DEBUG_STATUS_GO); + const HRESULT hr = interfaces().debugControl->SetExecutionStatus(DEBUG_STATUS_GO); if (FAILED(hr)) { - const QString errorMessage = msgComFailed("SetExecutionStatus", hr); + const QString errorMessage = CdbCore::msgComFailed("SetExecutionStatus", hr); if (errorMessagePtr) { *errorMessagePtr = errorMessage; } else { @@ -1176,14 +828,11 @@ bool CdbDebugEnginePrivate::continueInferiorProcess(QString *errorMessagePtr /* bool CdbDebugEnginePrivate::continueInferior(QString *errorMessage) { // Check state: Are we running? - ULONG executionStatus; - if (!getExecutionStatus(m_cif.debugControl, &executionStatus, errorMessage)) - return false; - + const ULONG ex = executionStatus(); if (debugCDB) - qDebug() << Q_FUNC_INFO << "\n ex=" << executionStatus; + qDebug() << Q_FUNC_INFO << "\n ex=" << ex; - if (executionStatus == DEBUG_STATUS_GO) { + if (ex == DEBUG_STATUS_GO) { m_engine->warning(QLatin1String("continueInferior() called while debuggee is running.")); return true; } @@ -1192,15 +841,15 @@ bool CdbDebugEnginePrivate::continueInferior(QString *errorMessage) bool success = false; do { clearForRun(); - setCodeLevel(); - m_engine->killWatchTimer(); + updateCodeLevel(); + killWatchTimer(); manager()->resetLocation(); manager()->showStatusMessage(CdbDebugEngine::tr("Running requested..."), messageTimeOut); if (!continueInferiorProcess(errorMessage)) break; - m_engine->startWatchTimer(); + startWatchTimer(); success = true; } while (false); if (success) { @@ -1215,28 +864,14 @@ bool CdbDebugEnginePrivate::interruptInterferiorProcess(QString *errorMessage) { // Interrupt the interferior process without notifications + // Could use setInterrupt, but that does not work. if (debugCDBExecution) { - ULONG executionStatus; - getExecutionStatus(m_cif.debugControl, &executionStatus, errorMessage); - qDebug() << "interruptInterferiorProcess ex=" << executionStatus; + qDebug() << "interruptInterferiorProcess ex=" << executionStatus(); } - - if (DebugBreakProcess(m_hDebuggeeProcess)) { + const bool rc = debugBreakProcess(m_hDebuggeeProcess, errorMessage); + if (rc) m_interrupted = true; - } else { - *errorMessage = QString::fromLatin1("DebugBreakProcess failed: %1").arg(Utils::winErrorMessage(GetLastError())); - return false; - } -#if 0 - 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_cif.debugControl)).arg(msgComFailed("SetInterrupt", hr)); - return false; - } - m_interrupted = true; -#endif - return true; + return rc; } void CdbDebugEngine::slotBreakAttachToCrashed() @@ -1271,7 +906,7 @@ void CdbDebugEngine::runToLineExec(const QString &fileName, int lineNumber) tempBreakPoint.fileName = fileName; tempBreakPoint.lineNumber = lineNumber; tempBreakPoint.oneShot = true; - const bool ok = tempBreakPoint.add(m_d->m_cif.debugControl, &errorMessage) + const bool ok = tempBreakPoint.add(m_d->interfaces().debugControl, &errorMessage) && m_d->continueInferior(&errorMessage); if (!ok) warning(errorMessage); @@ -1284,7 +919,7 @@ void CdbDebugEngine::runToFunctionExec(const QString &functionName) CDBBreakPoint tempBreakPoint; tempBreakPoint.funcName = functionName; tempBreakPoint.oneShot = true; - const bool ok = tempBreakPoint.add(m_d->m_cif.debugControl, &errorMessage) + const bool ok = tempBreakPoint.add(m_d->interfaces().debugControl, &errorMessage) && m_d->continueInferior(&errorMessage); if (!ok) warning(errorMessage); @@ -1327,64 +962,10 @@ void CdbDebugEngine::assignValueInDebugger(const QString &expr, const QString &v void CdbDebugEngine::executeDebuggerCommand(const QString &command) { QString errorMessage; - if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_d->m_cif.debugControl, command, &errorMessage)) + if (!m_d->executeDebuggerCommand(command, &errorMessage)) warning(errorMessage); } -bool CdbDebugEnginePrivate::executeDebuggerCommand(CIDebugControl *ctrl, const QString &command, QString *errorMessage) -{ - // output to all clients, else we do not see anything - const HRESULT hr = ctrl->ExecuteWide(DEBUG_OUTCTL_ALL_CLIENTS, reinterpret_cast<PCWSTR>(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)); - return false; - } - return true; -} - -bool CdbDebugEngine::evaluateExpression(const QString &expression, - QString *value, - QString *type, - QString *errorMessage) -{ - DEBUG_VALUE debugValue; - if (!m_d->evaluateExpression(m_d->m_cif.debugControl, expression, &debugValue, errorMessage)) - return false; - *value = CdbSymbolGroupContext::debugValueToString(debugValue, m_d->m_cif.debugControl, type); - return true; -} - -bool CdbDebugEnginePrivate::evaluateExpression(CIDebugControl *ctrl, - const QString &expression, - DEBUG_VALUE *debugValue, - QString *errorMessage) -{ - if (debugCDB > 1) - qDebug() << Q_FUNC_INFO << expression; - - memset(debugValue, 0, sizeof(DEBUG_VALUE)); - // Original syntax must be restored, else setting breakpoints will fail. - SyntaxSetter syntaxSetter(ctrl, DEBUG_EXPR_CPLUSPLUS); - ULONG errorPosition = 0; - const HRESULT hr = ctrl->EvaluateWide(reinterpret_cast<PCWSTR>(expression.utf16()), - DEBUG_VALUE_INVALID, debugValue, - &errorPosition); - if (FAILED(hr)) { - if (HRESULT_CODE(hr) == 517) { - *errorMessage = QString::fromLatin1("Unable to evaluate '%1': Expression out of scope."). - arg(expression); - } else { - *errorMessage = QString::fromLatin1("Unable to evaluate '%1': Error at %2: %3"). - arg(expression).arg(errorPosition).arg(msgDebugEngineComResult(hr)); - } - return false; - } - return true; -} - void CdbDebugEngine::activateFrame(int frameIndex) { if (debugCDB) @@ -1478,7 +1059,7 @@ bool CdbDebugEnginePrivate::attemptBreakpointSynchronization(QString *errorMessa // called again from the debug event handler. ULONG dummy; - const bool wasRunning = !CDBBreakPoint::getBreakPointCount(m_cif.debugControl, &dummy); + const bool wasRunning = !CDBBreakPoint::getBreakPointCount(interfaces().debugControl, &dummy); if (debugCDB) qDebug() << Q_FUNC_INFO << "\n Running=" << wasRunning; @@ -1493,8 +1074,8 @@ bool CdbDebugEnginePrivate::attemptBreakpointSynchronization(QString *errorMessa } QStringList warnings; - const bool ok = CDBBreakPoint::synchronizeBreakPoints(m_cif.debugControl, - m_cif.debugSymbols, + const bool ok = CDBBreakPoint::synchronizeBreakPoints(interfaces().debugControl, + interfaces().debugSymbols, manager()->breakHandler(), errorMessage, &warnings); if (const int warningsCount = warnings.size()) @@ -1535,8 +1116,7 @@ void CdbDebugEngine::fetchDisassembler(DisassemblerViewAgent *agent, } QString disassembly; QApplication::setOverrideCursor(Qt::WaitCursor); - ok = dissassemble(m_d->m_cif.debugClient, m_d->m_cif.debugControl, offset, - ContextLines, ContextLines, addressFieldWith, QTextStream(&disassembly), &errorMessage); + ok = dissassemble(m_d, offset, ContextLines, ContextLines, addressFieldWith, QTextStream(&disassembly), &errorMessage); QApplication::restoreOverrideCursor(); if (!ok) break; @@ -1556,10 +1136,10 @@ void CdbDebugEngine::fetchMemory(MemoryViewAgent *agent, quint64 addr, quint64 l return; ULONG received; QByteArray data(length, '\0'); - const HRESULT hr = m_d->m_cif.debugDataSpaces->ReadVirtual(addr, data.data(), length, &received); + const HRESULT hr = m_d->interfaces().debugDataSpaces->ReadVirtual(addr, data.data(), length, &received); if (FAILED(hr)) { warning(tr("Unable to retrieve %1 bytes of memory at 0x%2: %3"). - arg(length).arg(addr, 0, 16).arg(msgComFailed("ReadVirtual", hr))); + arg(length).arg(addr, 0, 16).arg(CdbCore::msgComFailed("ReadVirtual", hr))); return; } if (received < length) @@ -1593,7 +1173,7 @@ QList<Symbol> CdbDebugEngine::moduleSymbols(const QString &moduleName) errorMessage = tr("Cannot retrieve symbols while the debuggee is running."); break; } - if (!getModuleSymbols(m_d->m_cif.debugSymbols, moduleName, &rc, &errorMessage)) + if (!getModuleSymbols(m_d->interfaces().debugSymbols, moduleName, &rc, &errorMessage)) break; success = true; } while (false); @@ -1611,40 +1191,11 @@ void CdbDebugEngine::reloadRegisters() qDebug() << Q_FUNC_INFO << intBase; QList<Register> registers; QString errorMessage; - if (!getRegisters(m_d->m_cif.debugControl, m_d->m_cif.debugRegisters, ®isters, &errorMessage, intBase)) + if (!getRegisters(m_d->interfaces().debugControl, m_d->interfaces().debugRegisters, ®isters, &errorMessage, intBase)) warning(msgFunctionFailed("reloadRegisters" , errorMessage)); manager()->registerHandler()->setRegisters(registers); } -void CdbDebugEngine::timerEvent(QTimerEvent* te) -{ - // Fetch away the debug events and notify if debuggee - // stops. Note that IDebugEventCallback does not - // cover all cases of a debuggee stopping execution - // (such as step over,etc). - if (te->timerId() != m_d->m_watchTimer) - return; - - const HRESULT hr = m_d->m_cif.debugControl->WaitForEvent(0, 1); - if (debugCDB) - if (debugCDB > 1 || hr != S_FALSE) - qDebug() << Q_FUNC_INFO << "WaitForEvent" << state() << msgDebugEngineComResult(hr); - - switch (hr) { - case S_OK: - killWatchTimer(); - m_d->handleDebugEvent(); - break; - case S_FALSE: - case E_PENDING: - case E_FAIL: - break; - case E_UNEXPECTED: // Occurs on ExitProcess. - killWatchTimer(); - break; - } -} - void CdbDebugEngine::slotConsoleStubStarted() { const qint64 appPid = m_d->m_consoleStubProc.applicationPID(); @@ -1653,7 +1204,7 @@ void CdbDebugEngine::slotConsoleStubStarted() // Attach to console process QString errorMessage; if (startAttachDebugger(appPid, AttachExternal, &errorMessage)) { - startWatchTimer(); + m_d->startWatchTimer(); manager()->notifyInferiorPidChanged(appPid); } else { QMessageBox::critical(manager()->mainWindow(), tr("Debugger Error"), errorMessage); @@ -1713,7 +1264,7 @@ void CdbDebugEnginePrivate::handleDebugEvent() { if (debugCDBExecution) qDebug() << "handleDebugEvent mode " << m_breakEventMode - << executionStatusString(m_cif.debugControl) << " interrupt" << m_interrupted + << CdbCore::msgExecutionStatusString(executionStatus()) << " interrupt" << m_interrupted << " startupcomplete" << m_inferiorStartupComplete; // restore mode and do special handling const HandleBreakEventMode mode = m_breakEventMode; @@ -1754,7 +1305,7 @@ void CdbDebugEnginePrivate::handleDebugEvent() } break; case BreakEventIgnoreOnce: - m_engine->startWatchTimer(); + startWatchTimer(); m_interrupted = false; break; case BreakEventSyncBreakPoints: { @@ -1762,7 +1313,7 @@ void CdbDebugEnginePrivate::handleDebugEvent() // Temp stop to sync breakpoints QString errorMessage; attemptBreakpointSynchronization(&errorMessage); - m_engine->startWatchTimer(); + startWatchTimer(); continueInferiorProcess(&errorMessage); if (!errorMessage.isEmpty()) m_engine->warning(QString::fromLatin1("In handleDebugEvent: %1").arg(errorMessage)); @@ -1783,17 +1334,17 @@ void CdbDebugEnginePrivate::setDebuggeeHandles(HANDLE hDebuggeeProcess, HANDLE bool CdbDebugEnginePrivate::setCDBThreadId(unsigned long threadId, QString *errorMessage) { ULONG currentThreadId; - HRESULT hr = m_cif.debugSystemObjects->GetCurrentThreadId(¤tThreadId); + HRESULT hr = interfaces().debugSystemObjects->GetCurrentThreadId(¤tThreadId); if (FAILED(hr)) { - *errorMessage = msgComFailed("GetCurrentThreadId", hr); + *errorMessage = CdbCore::msgComFailed("GetCurrentThreadId", hr); return false; } if (currentThreadId == threadId) return true; - hr = m_cif.debugSystemObjects->SetCurrentThreadId(threadId); + hr = interfaces().debugSystemObjects->SetCurrentThreadId(threadId); if (FAILED(hr)) { *errorMessage = QString::fromLatin1("Failed to change to from thread %1 to %2: SetCurrentThreadId() failed: %3"). - arg(currentThreadId).arg(threadId).arg(msgDebugEngineComResult(hr)); + arg(currentThreadId).arg(threadId).arg(CdbCore::msgDebugEngineComResult(hr)); return false; } const QString msg = CdbDebugEngine::tr("Changing threads: %1 -> %2").arg(currentThreadId).arg(threadId); @@ -1810,7 +1361,7 @@ ULONG CdbDebugEnginePrivate::updateThreadList() ULONG currentThreadId; QString errorMessage; // When interrupting, an artifical thread with a breakpoint is created. - if (!CdbStackTraceContext::getThreads(m_cif, true, &threads, ¤tThreadId, &errorMessage)) + if (!CdbStackTraceContext::getThreads(interfaces(), true, &threads, ¤tThreadId, &errorMessage)) m_engine->warning(errorMessage); manager()->threadsHandler()->setThreads(threads); return currentThreadId; @@ -1903,7 +1454,7 @@ void CdbDebugEnginePrivate::updateModules() { QList<Module> modules; QString errorMessage; - if (!getModuleList(m_cif.debugSymbols, &modules, &errorMessage)) + if (!getModuleList(interfaces().debugSymbols, &modules, &errorMessage)) m_engine->warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage)); manager()->modulesHandler()->setModules(modules); } @@ -1929,14 +1480,6 @@ void CdbDebugEngine::reloadSourceFiles() { } -QStringList CdbDebugEnginePrivate::sourcePaths() const -{ - WCHAR wszBuf[MAX_PATH]; - if (SUCCEEDED(m_cif.debugSymbols->GetSourcePathWide(wszBuf, MAX_PATH, 0))) - return QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf)).split(QLatin1Char(';')); - return QStringList(); -} - void CdbDebugEngine::syncDebuggerPaths() { if (debugCDB) @@ -1949,39 +1492,6 @@ void CdbDebugEngine::syncDebuggerPaths() } } -static inline QString pathString(const QStringList &s) -{ return s.join(QString(QLatin1Char(';'))); } - -bool CdbDebugEnginePrivate::setSourcePaths(const QStringList &s, QString *errorMessage) -{ - const HRESULT hr = m_cif.debugSymbols->SetSourcePathWide(reinterpret_cast<PCWSTR>(pathString(s).utf16())); - if (FAILED(hr)) { - if (errorMessage) - *errorMessage = msgComFailed("SetSourcePathWide", hr); - return false; - } - return true; -} - -QStringList CdbDebugEnginePrivate::symbolPaths() const -{ - WCHAR wszBuf[MAX_PATH]; - if (SUCCEEDED(m_cif.debugSymbols->GetSymbolPathWide(wszBuf, MAX_PATH, 0))) - return QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf)).split(QLatin1Char(';')); - return QStringList(); -} - -bool CdbDebugEnginePrivate::setSymbolPaths(const QStringList &s, QString *errorMessage) -{ - const HRESULT hr = m_cif.debugSymbols->SetSymbolPathWide(reinterpret_cast<PCWSTR>(pathString(s).utf16())); - if (FAILED(hr)) { - if (errorMessage) - *errorMessage = msgComFailed("SetSymbolPathWide", hr); - return false; - } - return true; -} - // Accessed by DebuggerManager IDebuggerEngine *createWinEngine(DebuggerManager *parent, bool cmdLineEnabled, diff --git a/src/plugins/debugger/cdb/cdbdebugengine.h b/src/plugins/debugger/cdb/cdbdebugengine.h index 59d7658a925c5a3d3791ab961e18ba9cfefcd16c..52fb70119d6d8d53c24bc9800f1ba2bf01dd88c8 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine.h +++ b/src/plugins/debugger/cdb/cdbdebugengine.h @@ -43,7 +43,7 @@ namespace Internal { class DisassemblerViewAgent; class CdbDebugEventCallback; class CdbDebugOutput; -struct CdbDebugEnginePrivate; +class CdbDebugEnginePrivate; struct CdbOptions; class CdbDebugEngine : public IDebuggerEngine @@ -104,9 +104,6 @@ public: public slots: void syncDebuggerPaths(); -protected: - void timerEvent(QTimerEvent*); - private slots: void slotConsoleStubStarted(); void slotConsoleStubError(const QString &msg); @@ -116,19 +113,16 @@ private slots: private: void setState(DebuggerState state, const char *func, int line); - bool startAttachDebugger(qint64 pid, DebuggerStartMode sm, QString *errorMessage); - bool startDebuggerWithExecutable(DebuggerStartMode sm, QString *errorMessage); - void startWatchTimer(); - void killWatchTimer(); + inline bool startAttachDebugger(qint64 pid, DebuggerStartMode sm, QString *errorMessage); + inline bool startDebuggerWithExecutable(DebuggerStartMode sm, QString *errorMessage); void processTerminated(unsigned long exitCode); - bool evaluateExpression(const QString &expression, QString *value, QString *type, QString *errorMessage); void evaluateWatcher(WatchData *wd); QString editorToolTip(const QString &exp, const QString &function); bool step(unsigned long executionStatus); CdbDebugEnginePrivate *m_d; - friend struct CdbDebugEnginePrivate; + friend class CdbDebugEnginePrivate; friend class CdbDebugEventCallback; friend class CdbDebugOutput; }; diff --git a/src/plugins/debugger/cdb/cdbdebugengine_p.h b/src/plugins/debugger/cdb/cdbdebugengine_p.h index 89a7fa15f74fbf3c64c60331da1baf54e7a19c8c..55e7287723074f7241b68567baada8101e94047b 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine_p.h +++ b/src/plugins/debugger/cdb/cdbdebugengine_p.h @@ -30,8 +30,7 @@ #ifndef DEBUGGER_CDBENGINEPRIVATE_H #define DEBUGGER_CDBENGINEPRIVATE_H -#include "cdbdebugeventcallback.h" -#include "cdbdebugoutput.h" +#include "coreengine.h" #include "cdboptions.h" #include "cdbdumperhelper.h" #include "stackhandler.h" @@ -50,52 +49,11 @@ class WatchHandler; class CdbStackFrameContext; class CdbStackTraceContext; -// Thin wrapper around the 'DBEng' debugger engine shared library -// which is loaded at runtime. - -class DebuggerEngineLibrary +class CdbDebugEnginePrivate : public CdbCore::CoreEngine { + Q_OBJECT public: - DebuggerEngineLibrary(); - bool init(const QString &path, QString *dbgEngDLL, QString *errorMessage); - - inline HRESULT debugCreate(REFIID interfaceId, PVOID *interfaceHandle) const - { return m_debugCreate(interfaceId, interfaceHandle); } - -private: - // The exported functions of the library - typedef HRESULT (STDAPICALLTYPE *DebugCreateFunction)(REFIID, PVOID *); - - 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(CIDebugControl *ctl, ULONG desiredSyntax); - inline ~SyntaxSetter(); -private: - const ULONG m_desiredSyntax; - CIDebugControl *m_ctl; - ULONG m_oldSyntax; -}; - -// helper struct to pass interfaces around -struct CdbComInterfaces -{ - CdbComInterfaces(); - CIDebugClient* debugClient; - CIDebugControl* debugControl; - CIDebugSystemObjects* debugSystemObjects; - CIDebugSymbols* debugSymbols; - CIDebugRegisters* debugRegisters; - CIDebugDataSpaces* debugDataSpaces; -}; -struct CdbDebugEnginePrivate -{ typedef QMap<QString, QString> EditorToolTipCache; enum HandleBreakEventMode { // Special modes for break event handler. @@ -107,15 +65,14 @@ struct CdbDebugEnginePrivate explicit CdbDebugEnginePrivate(DebuggerManager *parent, const QSharedPointer<CdbOptions> &options, CdbDebugEngine* engine); + ~CdbDebugEnginePrivate(); bool init(QString *errorMessage); - ~CdbDebugEnginePrivate(); void checkVersion(); void processCreatedAttached(ULONG64 processHandle, ULONG64 initialThreadHandle); void setDebuggeeHandles(HANDLE hDebuggeeProcess, HANDLE hDebuggeeThread); - bool isDebuggeeRunning() const { return m_watchTimer != -1; } - void handleDebugEvent(); + bool isDebuggeeRunning() const { return isWatchTimerRunning(); } ULONG updateThreadList(); bool setCDBThreadId(unsigned long threadId, QString *errorMessage); void updateStackTrace(); @@ -143,17 +100,12 @@ struct CdbDebugEnginePrivate enum EndDebuggingMode { EndDebuggingDetach, EndDebuggingTerminate, EndDebuggingAuto }; void endDebugging(EndDebuggingMode em = EndDebuggingAuto); - static bool executeDebuggerCommand(CIDebugControl *ctrl, const QString &command, QString *errorMessage); - static bool evaluateExpression(CIDebugControl *ctrl, const QString &expression, DEBUG_VALUE *v, QString *errorMessage); - - QStringList sourcePaths() const; - bool setSourcePaths(const QStringList &s, QString *errorMessage); - - QStringList symbolPaths() const; - bool setSymbolPaths(const QStringList &s, QString *errorMessage); + void updateCodeLevel(); - bool setCodeLevel(); +public slots: + void handleDebugEvent(); +public: const QSharedPointer<CdbOptions> m_options; HANDLE m_hDebuggeeProcess; HANDLE m_hDebuggeeThread; @@ -164,12 +116,7 @@ struct CdbDebugEnginePrivate bool m_ignoreInitialBreakPoint; HandleBreakEventMode m_breakEventMode; - int m_watchTimer; - CdbComInterfaces m_cif; - CdbDebugEventCallback m_debugEventCallBack; - CdbDebugOutput m_debugOutputCallBack; QSharedPointer<CdbDumperHelper> m_dumper; - QString m_baseImagePath; CdbDebugEngine *m_engine; inline DebuggerManager *manager() const; @@ -181,19 +128,8 @@ struct CdbDebugEnginePrivate DebuggerStartMode m_mode; Utils::ConsoleProcess m_consoleStubProc; - QString m_dbengDLL; }; -// helper functions - -bool getExecutionStatus(CIDebugControl *ctl, ULONG *executionStatus, QString *errorMessage = 0); -const char *executionStatusString(ULONG executionStatus); -const char *executionStatusString(CIDebugControl *ctl); - -// Message -QString msgDebugEngineComResult(HRESULT hr); -QString msgComFailed(const char *func, HRESULT hr); - enum { messageTimeOut = 5000 }; enum { debugCDB = 0 }; diff --git a/src/plugins/debugger/cdb/cdbdebugeventcallback.cpp b/src/plugins/debugger/cdb/cdbdebugeventcallback.cpp index 26c24b2058ba22fc3afd99809c46f5d35670d46a..0ffc51ff20eda399951dc68beec59dcc55e86d32 100644 --- a/src/plugins/debugger/cdb/cdbdebugeventcallback.cpp +++ b/src/plugins/debugger/cdb/cdbdebugeventcallback.cpp @@ -40,174 +40,6 @@ namespace Debugger { namespace Internal { -// CdbDebugEventCallbackBase -CdbDebugEventCallbackBase::CdbDebugEventCallbackBase() -{ -} - -STDMETHODIMP CdbDebugEventCallbackBase::QueryInterface( - THIS_ - IN REFIID InterfaceId, - OUT PVOID* Interface) -{ - *Interface = NULL; - - if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) || - IsEqualIID(InterfaceId, __uuidof(IDebugOutputCallbacks))) { - *Interface = (IDebugOutputCallbacks *)this; - AddRef(); - return S_OK; - } else { - return E_NOINTERFACE; - } -} - -STDMETHODIMP_(ULONG) CdbDebugEventCallbackBase::AddRef(THIS) -{ - // This class is designed to be static so - // there's no true refcount. - return 1; -} - -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(CIDebugClient *clnt) -{ - IDebugEventCallbacksWide *rc = 0; - if (SUCCEEDED(clnt->GetEventCallbacksWide(&rc))) - return rc; - return 0; -} - // ---------- CdbDebugEventCallback CdbDebugEventCallback::CdbDebugEventCallback(CdbDebugEngine* dbg) : @@ -420,18 +252,5 @@ STDMETHODIMP IgnoreDebugEventCallback::GetInterestMask(THIS_ __out PULONG mask) return S_OK; } -// --------- EventCallbackRedirector -EventCallbackRedirector::EventCallbackRedirector(CIDebugClient *client, IDebugEventCallbacksWide *cb) : - m_client(client), - m_oldCb(CdbDebugEventCallbackBase::getEventCallback(client)) -{ - client->SetEventCallbacksWide(cb); -} - -EventCallbackRedirector::~EventCallbackRedirector() -{ - m_client->SetEventCallbacksWide(m_oldCb); -} - } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/cdb/cdbdebugeventcallback.h b/src/plugins/debugger/cdb/cdbdebugeventcallback.h index 4250cd5b24c03d106c0284542552522fb9c0bb28..99c99a937a118625265ab9fe23b2168b6eb9b232 100644 --- a/src/plugins/debugger/cdb/cdbdebugeventcallback.h +++ b/src/plugins/debugger/cdb/cdbdebugeventcallback.h @@ -30,7 +30,7 @@ #ifndef DEBUGGER_CDBDEBUGEVENTCALLBACK_H #define DEBUGGER_CDBDEBUGEVENTCALLBACK_H -#include "cdbcom.h" +#include "debugeventcallbackbase.h" #include <QtCore/QStringList> @@ -41,122 +41,7 @@ namespace Internal { class CdbDebugEngine; -// 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: - // IUnknown. - STDMETHOD(QueryInterface)( - THIS_ - IN REFIID InterfaceId, - OUT PVOID* Interface - ); - STDMETHOD_(ULONG, AddRef)( - THIS - ); - STDMETHOD_(ULONG, Release)( - THIS - ); - - // IDebugEventCallbacks. - - 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 - ); - - STDMETHOD(SessionStatus)( - THIS_ - __in ULONG Status - ); - - STDMETHOD(ChangeDebuggeeState)( - THIS_ - __in ULONG Flags, - __in ULONG64 Argument - ); - - STDMETHOD(ChangeEngineState)( - THIS_ - __in ULONG Flags, - __in ULONG64 Argument - ); - - STDMETHOD(ChangeSymbolState)( - THIS_ - __in ULONG Flags, - __in ULONG64 Argument - ); - - - static IDebugEventCallbacksWide *getEventCallback(CIDebugClient *clnt); -}; - -class CdbDebugEventCallback : public CdbDebugEventCallbackBase +class CdbDebugEventCallback : public CdbCore::DebugEventCallbackBase { public: explicit CdbDebugEventCallback(CdbDebugEngine* dbg); @@ -239,7 +124,7 @@ private: // Event handler logs exceptions to the debugger window // and ignores the rest. To be used for running dumper calls. -class CdbExceptionLoggerEventCallback : public CdbDebugEventCallbackBase +class CdbExceptionLoggerEventCallback : public CdbCore::DebugEventCallbackBase { public: CdbExceptionLoggerEventCallback(int logChannel, @@ -270,7 +155,7 @@ private: }; // Event handler that ignores everything -class IgnoreDebugEventCallback : public CdbDebugEventCallbackBase +class IgnoreDebugEventCallback : public CdbCore::DebugEventCallbackBase { public: explicit IgnoreDebugEventCallback(); @@ -281,19 +166,6 @@ public: ); }; -// Utility class to temporarily redirect events to another handler -// as long as in scope -class EventCallbackRedirector { - Q_DISABLE_COPY(EventCallbackRedirector) -public: - explicit EventCallbackRedirector(CIDebugClient *client, IDebugEventCallbacksWide *cb); - ~EventCallbackRedirector(); -private: - CIDebugClient *m_client; - IDebugEventCallbacksWide *m_oldCb; -}; - - } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/cdb/cdbdebugoutput.cpp b/src/plugins/debugger/cdb/cdbdebugoutput.cpp index b3c8a76575f5518e71c86c4aa116574bcb7e2d27..04a2729f0f6a79aab50691ab8d91dd58a99ebddc 100644 --- a/src/plugins/debugger/cdb/cdbdebugoutput.cpp +++ b/src/plugins/debugger/cdb/cdbdebugoutput.cpp @@ -37,62 +37,6 @@ namespace Debugger { namespace Internal { -CdbDebugOutputBase::CdbDebugOutputBase() -{ -} - -STDMETHODIMP CdbDebugOutputBase::QueryInterface( - THIS_ - IN REFIID InterfaceId, - OUT PVOID* Interface - ) -{ - *Interface = NULL; - - if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) || - IsEqualIID(InterfaceId, __uuidof(IDebugOutputCallbacksWide))) - { - *Interface = (IDebugOutputCallbacksWide*)this; - AddRef(); - return S_OK; - } else { - return E_NOINTERFACE; - } -} - -STDMETHODIMP_(ULONG) CdbDebugOutputBase::AddRef(THIS) -{ - // This class is designed to be static so - // there's no true refcount. - return 1; -} - -STDMETHODIMP_(ULONG) CdbDebugOutputBase::Release(THIS) -{ - // This class is designed to be static so - // there's no true refcount. - return 0; -} - -STDMETHODIMP CdbDebugOutputBase::Output( - THIS_ - IN ULONG mask, - IN PCWSTR text - ) -{ - const QString msg = QString::fromUtf16(reinterpret_cast<const ushort *>(text)); - output(mask, msg); - return S_OK; -} - -IDebugOutputCallbacksWide *CdbDebugOutputBase::getOutputCallback(CIDebugClient *client) -{ - IDebugOutputCallbacksWide *rc; - if (FAILED(client->GetOutputCallbacksWide(&rc))) - return 0; - return rc; -} - // ------------------------- CdbDebugOutput // Return a prefix for debugger messages @@ -145,18 +89,5 @@ void CdbDebugOutput::output(ULONG mask, const QString &msg) } } -// Utility class to temporarily redirect output to another handler -// as long as in scope -OutputRedirector::OutputRedirector(CIDebugClient *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 diff --git a/src/plugins/debugger/cdb/cdbdebugoutput.h b/src/plugins/debugger/cdb/cdbdebugoutput.h index ad2473c787425bb245c2544da383ccac62eaebca..90ed71bfd5121540833349b61b00d51d11235431 100644 --- a/src/plugins/debugger/cdb/cdbdebugoutput.h +++ b/src/plugins/debugger/cdb/cdbdebugoutput.h @@ -30,49 +30,15 @@ #ifndef DEBUGGER_CDBOUTPUT_H #define DEBUGGER_CDBOUTPUT_H -#include "cdbcom.h" +#include "debugoutputbase.h" #include <QtCore/QObject> namespace Debugger { namespace Internal { -// CdbDebugOutputBase is a base class for output handlers -// that takes care of the Active X magic and conversion to QString. - -class CdbDebugOutputBase : public IDebugOutputCallbacksWide -{ -public: - // IUnknown. - STDMETHOD(QueryInterface)( - THIS_ - IN REFIID InterfaceId, - OUT PVOID* Interface - ); - STDMETHOD_(ULONG, AddRef)( - THIS - ); - STDMETHOD_(ULONG, Release)( - THIS - ); - - // IDebugOutputCallbacks. - STDMETHOD(Output)( - THIS_ - IN ULONG mask, - IN PCWSTR text - ); - - // Helpers to retrieve the output callbacks IF - static IDebugOutputCallbacksWide *getOutputCallback(CIDebugClient *client); - -protected: - CdbDebugOutputBase(); - virtual void output(ULONG mask, const QString &message) = 0; -}; - // Standard CDB output handler -class CdbDebugOutput : public QObject, public CdbDebugOutputBase +class CdbDebugOutput : public QObject, public CdbCore::DebugOutputBase { Q_OBJECT public: @@ -88,34 +54,6 @@ signals: void debuggeeInputPrompt(const QString &message); }; -// An output handler that adds lines to a string (to be -// used for cases in which linebreaks occur in-between calls -// to output). -class StringOutputHandler : public CdbDebugOutputBase -{ -public: - StringOutputHandler() {} - QString result() const { return m_result; } - -protected: - virtual void output(ULONG, const QString &message) { m_result += message; } - -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(CIDebugClient *client, IDebugOutputCallbacksWide *newHandler); - ~OutputRedirector(); -private: - CIDebugClient *m_client; - IDebugOutputCallbacksWide *m_oldHandler; -}; - } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/cdb/cdbdumperhelper.cpp b/src/plugins/debugger/cdb/cdbdumperhelper.cpp index 852f3f929b3db8a2b8547d21135a5b2384de8d14..10f9405e65871f9137a8a797a5c33edf5cc7b042 100644 --- a/src/plugins/debugger/cdb/cdbdumperhelper.cpp +++ b/src/plugins/debugger/cdb/cdbdumperhelper.cpp @@ -94,59 +94,13 @@ namespace Debugger { namespace Internal { // ------- Call load helpers -// Alloc memory in debuggee using the ".dvalloc" command as -// there seems to be no API for it. -static bool allocDebuggeeMemory(CdbComInterfaces *cif, - int size, ULONG64 *address, QString *errorMessage) -{ - *address = 0; - const QString allocCmd = QLatin1String(".dvalloc ") + QString::number(size); - StringOutputHandler stringHandler; - OutputRedirector redir(cif->debugClient, &stringHandler); - if (!CdbDebugEnginePrivate::executeDebuggerCommand(cif->debugControl, 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 (loadDebug > 1) - qDebug() << Q_FUNC_INFO << '\n' << output << *address << ok; - 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(CdbComInterfaces *cif, - const QString &s, - ULONG64 *address, - QString *errorMessage) -{ - QByteArray sAsciiData = s.toLocal8Bit(); - sAsciiData += '\0'; - if (!allocDebuggeeMemory(cif, sAsciiData.size(), address, errorMessage)) - return false; - const HRESULT hr = cif->debugDataSpaces->WriteVirtual(*address, sAsciiData.data(), sAsciiData.size(), 0); - if (FAILED(hr)) { - *errorMessage= msgComFailed("WriteVirtual", hr); - return false; - } - return true; -} -// Load a library into the debuggee. Currently requires + // 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(DebuggerManager *manager, - CdbComInterfaces *cif, + CdbCore::CoreEngine *engine, unsigned long threadId, const QString &moduleName, QString *errorMessage) @@ -154,12 +108,13 @@ static bool debuggeeLoadLibrary(DebuggerManager *manager, if (loadDebug > 1) qDebug() << Q_FUNC_INFO << moduleName; // Try to ignore the breakpoints, skip stray startup-complete trap exceptions - CdbExceptionLoggerEventCallback exLogger(LogWarning, true, manager); - EventCallbackRedirector eventRedir(cif->debugClient, &exLogger); + QSharedPointer<CdbExceptionLoggerEventCallback> exLogger(new CdbExceptionLoggerEventCallback(LogWarning, true, manager)); + CdbCore::EventCallbackRedirector eventRedir(engine, exLogger); + Q_UNUSED(eventRedir) // Make a call to LoadLibraryA. First, reserve memory in debugger // and copy name over. ULONG64 nameAddress; - if (!createDebuggeeAscIIString(cif, moduleName, &nameAddress, errorMessage)) + if (!engine->createDebuggeeAscIIString(moduleName, &nameAddress, errorMessage)) return false; // We want to call "HMODULE LoadLibraryA(LPCTSTR lpFileName)" // (void* LoadLibraryA(char*)). However, despite providing a symbol @@ -168,7 +123,7 @@ static bool debuggeeLoadLibrary(DebuggerManager *manager, // 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(cif->debugSymbols, QLatin1String("QtCore[d]*4!"), &dummyFunc, errorMessage) != ResolveSymbolOk) + if (resolveSymbol(engine->interfaces().debugSymbols, QLatin1String("QtCore[d]*4!"), &dummyFunc, errorMessage) != ResolveSymbolOk) return false; QString callCmd; { @@ -179,16 +134,16 @@ static bool debuggeeLoadLibrary(DebuggerManager *manager, if (loadDebug) qDebug() << "Calling" << callCmd; - if (!CdbDebugEnginePrivate::executeDebuggerCommand(cif->debugControl, callCmd, errorMessage)) + if (!engine->executeDebuggerCommand(callCmd, errorMessage)) return false; // Execute current thread. This will hit a breakpoint. QString goCmd; QTextStream(&goCmd) << '~' << threadId << " g"; - if (!CdbDebugEnginePrivate::executeDebuggerCommand(cif->debugControl, goCmd, errorMessage)) + if (!engine->executeDebuggerCommand(goCmd, errorMessage)) return false; - const HRESULT hr = cif->debugControl->WaitForEvent(0, waitTimeOutMS); + const HRESULT hr = engine->waitForEvent(waitTimeOutMS); if (FAILED(hr)) { - *errorMessage = msgComFailed("WaitForEvent", hr); + *errorMessage = CdbCore::msgComFailed("WaitForEvent", hr); return false; } return true; @@ -339,13 +294,13 @@ void CdbDumperInitThread ::run() // ------------------- CdbDumperHelper CdbDumperHelper::CdbDumperHelper(DebuggerManager *manager, - CdbComInterfaces *cif) : + CdbCore::CoreEngine *coreEngine) : m_tryInjectLoad(true), m_msgDisabled(QLatin1String("Dumpers are disabled")), m_msgNotInScope(QLatin1String("Data not in scope")), m_state(NotLoaded), m_manager(manager), - m_cif(cif), + m_coreEngine(coreEngine), m_inBufferAddress(0), m_inBufferSize(0), m_outBufferAddress(0), @@ -433,7 +388,7 @@ CdbDumperHelper::CallLoadResult CdbDumperHelper::initCallLoad(QString *errorMess qDebug() << Q_FUNC_INFO; // Do we have Qt and are we already loaded by accident? QStringList modules; - if (!getModuleNameList(m_cif->debugSymbols, &modules, errorMessage)) + if (!getModuleNameList(m_coreEngine->interfaces().debugSymbols, &modules, errorMessage)) return CallLoadError; // Are we already loaded by some accident? if (!modules.filter(QLatin1String(dumperModuleNameC), Qt::CaseInsensitive).isEmpty()) @@ -442,7 +397,7 @@ CdbDumperHelper::CallLoadResult CdbDumperHelper::initCallLoad(QString *errorMess if (modules.filter(QLatin1String(qtCoreModuleNameC), Qt::CaseInsensitive).isEmpty()) return CallLoadNoQtApp; // Try to load - if (!debuggeeLoadLibrary(m_manager, m_cif, m_dumperCallThread, m_library, errorMessage)) + if (!debuggeeLoadLibrary(m_manager, m_coreEngine, m_dumperCallThread, m_library, errorMessage)) return CallLoadError; return CallLoadOk; } @@ -457,7 +412,7 @@ static inline bool getSymbolAddress(CIDebugSymbols *sg, // Get address HRESULT hr = sg->GetOffsetByNameWide(reinterpret_cast<PCWSTR>(name.utf16()), address); if (FAILED(hr)) { - *errorMessage = msgComFailed("GetOffsetByNameWide", hr); + *errorMessage = CdbCore::msgComFailed("GetOffsetByNameWide", hr); return false; } // Get size. Even works for arrays @@ -466,12 +421,12 @@ static inline bool getSymbolAddress(CIDebugSymbols *sg, ULONG type; hr = sg->GetOffsetTypeId(*address, &type, &moduleAddress); if (FAILED(hr)) { - *errorMessage = msgComFailed("GetOffsetTypeId", hr); + *errorMessage = CdbCore::msgComFailed("GetOffsetTypeId", hr); return false; } hr = sg->GetTypeSize(moduleAddress, type, size); if (FAILED(hr)) { - *errorMessage = msgComFailed("GetTypeSize", hr); + *errorMessage = CdbCore::msgComFailed("GetTypeSize", hr); return false; } } // size desired @@ -488,14 +443,14 @@ bool CdbDumperHelper::initResolveSymbols(QString *errorMessage) QString inBufferSymbol = QLatin1String("*qDumpInBuffer"); QString outBufferSymbol = QLatin1String("*qDumpOutBuffer"); const QString dumperModuleName = QLatin1String(dumperModuleNameC); - bool rc = resolveSymbol(m_cif->debugSymbols, &m_dumpObjectSymbol, errorMessage) == ResolveSymbolOk - && resolveSymbol(m_cif->debugSymbols, dumperModuleName, &inBufferSymbol, errorMessage) == ResolveSymbolOk - && resolveSymbol(m_cif->debugSymbols, dumperModuleName, &outBufferSymbol, errorMessage) == ResolveSymbolOk; + bool rc = resolveSymbol(m_coreEngine->interfaces().debugSymbols, &m_dumpObjectSymbol, errorMessage) == ResolveSymbolOk + && resolveSymbol(m_coreEngine->interfaces().debugSymbols, dumperModuleName, &inBufferSymbol, errorMessage) == ResolveSymbolOk + && resolveSymbol(m_coreEngine->interfaces().debugSymbols, dumperModuleName, &outBufferSymbol, errorMessage) == ResolveSymbolOk; if (!rc) return false; // Determine buffer addresses, sizes and alloc buffer - rc = getSymbolAddress(m_cif->debugSymbols, inBufferSymbol, &m_inBufferAddress, &m_inBufferSize, errorMessage) - && getSymbolAddress(m_cif->debugSymbols, outBufferSymbol, &m_outBufferAddress, &m_outBufferSize, errorMessage); + rc = getSymbolAddress(m_coreEngine->interfaces().debugSymbols, inBufferSymbol, &m_inBufferAddress, &m_inBufferSize, errorMessage) + && getSymbolAddress(m_coreEngine->interfaces().debugSymbols, outBufferSymbol, &m_outBufferAddress, &m_outBufferSize, errorMessage); if (!rc) return false; m_buffer = new char[qMax(m_inBufferSize, m_outBufferSize)]; @@ -532,41 +487,24 @@ bool CdbDumperHelper::initKnownTypes(QString *errorMessage) return true; } -// Write to debuggee memory in chunks -bool CdbDumperHelper::writeToDebuggee(CIDebugDataSpaces *ds, const QByteArray &buffer, quint64 address, QString *errorMessage) -{ - char *ptr = const_cast<char*>(buffer.data()); - ULONG bytesToWrite = buffer.size(); - while (bytesToWrite > 0) { - ULONG bytesWritten = 0; - const HRESULT hr = ds->WriteVirtual(address, ptr, bytesToWrite, &bytesWritten); - if (FAILED(hr)) { - *errorMessage = msgComFailed("WriteVirtual", hr); - return false; - } - bytesToWrite -= bytesWritten; - ptr += bytesWritten; - } - return true; -} - CdbDumperHelper::CallResult CdbDumperHelper::callDumper(const QString &callCmd, const QByteArray &inBuffer, const char **outDataPtr, bool ignoreAccessViolation, QString *errorMessage) { *outDataPtr = 0; // Skip stray startup-complete trap exceptions. - CdbExceptionLoggerEventCallback exLogger(LogWarning, true, m_manager); - EventCallbackRedirector eventRedir(m_cif->debugClient, &exLogger); + QSharedPointer<CdbExceptionLoggerEventCallback> exLogger(new CdbExceptionLoggerEventCallback(LogWarning, true, m_manager)); + CdbCore::EventCallbackRedirector eventRedir(m_coreEngine, exLogger); + Q_UNUSED(eventRedir) // write input buffer if (!inBuffer.isEmpty()) { - if (!writeToDebuggee(m_cif->debugDataSpaces, inBuffer, m_inBufferAddress, errorMessage)) + if (!m_coreEngine->writeToDebuggee(inBuffer, m_inBufferAddress, errorMessage)) return CallFailed; } - if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_cif->debugControl, callCmd, errorMessage)) { + if (!m_coreEngine->executeDebuggerCommand(callCmd, errorMessage)) { // Clear the outstanding call in case we triggered a debug library assert with a message box QString clearError; - if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_cif->debugControl, QLatin1String(".call /c"), &clearError)) { + if (!m_coreEngine->executeDebuggerCommand(QLatin1String(".call /c"), &clearError)) { *errorMessage += QString::fromLatin1("/Unable to clear call %1").arg(clearError); } return CallSyntaxError; @@ -575,40 +513,40 @@ CdbDumperHelper::CallResult // Try to skip debuggee crash exceptions and dumper exceptions // by using 'gN' (go not handled -> pass handling to dumper __try/__catch block) for (int i = 0; i < 10; i++) { - const int oldExceptionCount = exLogger.exceptionCount(); + const int oldExceptionCount = exLogger->exceptionCount(); // Go in current thread. If an exception occurs in loop 2, // let the dumper handle it. QString goCmd = m_goCommand; if (i) goCmd = QLatin1Char('N'); - if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_cif->debugControl, goCmd, errorMessage)) + if (!m_coreEngine->executeDebuggerCommand(goCmd, errorMessage)) return CallFailed; - HRESULT hr = m_cif->debugControl->WaitForEvent(0, waitTimeOutMS); + HRESULT hr = m_coreEngine->waitForEvent(waitTimeOutMS); if (FAILED(hr)) { - *errorMessage = msgComFailed("WaitForEvent", hr); + *errorMessage = CdbCore::msgComFailed("WaitForEvent", hr); return CallFailed; } - const int newExceptionCount = exLogger.exceptionCount(); + const int newExceptionCount = exLogger->exceptionCount(); // no new exceptions? -> break if (oldExceptionCount == newExceptionCount) break; // If we are to ignore EXCEPTION_ACCESS_VIOLATION, check if anything // else occurred. if (ignoreAccessViolation) { - const QList<ULONG> newExceptionCodes = exLogger.exceptionCodes().mid(oldExceptionCount); + const QList<ULONG> newExceptionCodes = exLogger->exceptionCodes().mid(oldExceptionCount); if (newExceptionCodes.count(EXCEPTION_ACCESS_VIOLATION) == newExceptionCodes.size()) break; } } - if (exLogger.exceptionCount()) { - const QString exMsgs = exLogger.exceptionMessages().join(QString(QLatin1Char(','))); + if (exLogger->exceptionCount()) { + const QString exMsgs = exLogger->exceptionMessages().join(QString(QLatin1Char(','))); *errorMessage = QString::fromLatin1("Exceptions occurred during the dumper call: %1").arg(exMsgs); return CallFailed; } // Read output - const HRESULT hr = m_cif->debugDataSpaces->ReadVirtual(m_outBufferAddress, m_buffer, m_outBufferSize, 0); + const HRESULT hr = m_coreEngine->interfaces().debugDataSpaces->ReadVirtual(m_outBufferAddress, m_buffer, m_outBufferSize, 0); if (FAILED(hr)) { - *errorMessage = msgComFailed("ReadVirtual", hr); + *errorMessage = CdbCore::msgComFailed("ReadVirtual", hr); return CallFailed; } // see QDumper implementation @@ -742,7 +680,7 @@ CdbDumperHelper::DumpExecuteResult qDebug() << "Query: " << wd.toString() << "\nwith: " << callCmd << '\n'; const char *outputData; // Completely ignore EXCEPTION_ACCESS_VIOLATION crashes in the dumpers. - ExceptionBlocker eb(m_cif->debugControl, EXCEPTION_ACCESS_VIOLATION, ExceptionBlocker::IgnoreException); + ExceptionBlocker eb(m_coreEngine->interfaces().debugControl, EXCEPTION_ACCESS_VIOLATION, ExceptionBlocker::IgnoreException); if (!eb) { *errorMessage = eb.errorString(); return DumpExecuteCallFailed; @@ -808,11 +746,10 @@ bool CdbDumperHelper::runTypeSizeQuery(const QString &typeName, int *size, QStri QString expression = QLatin1String("sizeof("); expression += typeName; expression += QLatin1Char(')'); - if (!CdbDebugEnginePrivate::evaluateExpression(m_cif->debugControl, - expression, &sizeValue, errorMessage)) + if (!m_coreEngine->evaluateExpression(expression, &sizeValue, errorMessage)) return false; qint64 size64; - if (!CdbSymbolGroupContext::debugValueToInteger(sizeValue, &size64)) { + if (!CdbCore::debugValueToInteger(sizeValue, &size64)) { *errorMessage = QLatin1String("Expression result is not an integer"); return false; } @@ -833,6 +770,11 @@ void CdbDumperHelper::setDumperCallThread(unsigned long t) } } +const CdbCore::ComInterfaces *CdbDumperHelper::comInterfaces() const +{ + return &m_coreEngine->interfaces(); +} + } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/cdb/cdbdumperhelper.h b/src/plugins/debugger/cdb/cdbdumperhelper.h index be326768df63f5faffa89bd0630cdac3192acd83..b4173b5664b9f3175e56fb1815fd1313b9f6f8b9 100644 --- a/src/plugins/debugger/cdb/cdbdumperhelper.h +++ b/src/plugins/debugger/cdb/cdbdumperhelper.h @@ -36,11 +36,15 @@ #include <QtCore/QStringList> #include <QtCore/QMap> +namespace CdbCore { + class CoreEngine; + struct ComInterfaces; +} + namespace Debugger { class DebuggerManager; namespace Internal { -struct CdbComInterfaces; class CdbDumperInitThread; /* For code clarity, all the stuff related to custom dumpers goes here. @@ -81,8 +85,8 @@ public: Initialized, // List of types, etc. retrieved }; - explicit CdbDumperHelper(DebuggerManager *manager, - CdbComInterfaces *cif); + explicit CdbDumperHelper(DebuggerManager *manager, + CdbCore::CoreEngine *coreEngine); ~CdbDumperHelper(); State state() const { return m_state; } @@ -102,7 +106,7 @@ public: DumpResult dumpType(const WatchData &d, bool dumpChildren, QList<WatchData> *result, QString *errorMessage); - inline CdbComInterfaces *comInterfaces() const { return m_cif; } + const CdbCore::ComInterfaces *comInterfaces() const; enum { InvalidDumperCallThread = 0xFFFFFFFF }; unsigned long dumperCallThread(); @@ -131,14 +135,12 @@ private: const QtDumperHelper::TypeData& td, bool dumpChildren, QList<WatchData> *result, QString *errorMessage); - static bool writeToDebuggee(CIDebugDataSpaces *ds, const QByteArray &buffer, quint64 address, QString *errorMessage); - const bool m_tryInjectLoad; const QString m_msgDisabled; const QString m_msgNotInScope; State m_state; DebuggerManager *m_manager; - CdbComInterfaces *m_cif; + CdbCore::CoreEngine *m_coreEngine; QString m_library; QString m_dumpObjectSymbol; diff --git a/src/plugins/debugger/cdb/cdbexceptionutils.cpp b/src/plugins/debugger/cdb/cdbexceptionutils.cpp index 2c9b49d413e1a2e3bdf409a2763efc0b5837a4c5..a68544f7bc3b0e1e17e467c79730db573c14ff9c 100644 --- a/src/plugins/debugger/cdb/cdbexceptionutils.cpp +++ b/src/plugins/debugger/cdb/cdbexceptionutils.cpp @@ -122,7 +122,7 @@ bool ExceptionBlocker::getExceptionParameters(CIDebugControl *ctrl, ULONG exCode { const HRESULT ihr = ctrl->GetExceptionFilterParameters(1, &exCode, 0, result); if (FAILED(ihr)) { - *errorMessage = msgComFailed("GetExceptionFilterParameters", ihr); + *errorMessage = CdbCore::msgComFailed("GetExceptionFilterParameters", ihr); return false; } return true; @@ -132,7 +132,7 @@ bool ExceptionBlocker::setExceptionParameters(CIDebugControl *ctrl, const DEBUG_ { const HRESULT ihr = ctrl->SetExceptionFilterParameters(1, const_cast<DEBUG_EXCEPTION_FILTER_PARAMETERS*>(&p)); if (FAILED(ihr)) { - *errorMessage = msgComFailed("GetExceptionFilterParameters", ihr); + *errorMessage = CdbCore::msgComFailed("GetExceptionFilterParameters", ihr); return false; } return true; diff --git a/src/plugins/debugger/cdb/cdbmodules.cpp b/src/plugins/debugger/cdb/cdbmodules.cpp index fd2653cc937051877dc3fac8cb654ed5def49328..dbb12f7f0c686a9de442d31b3ff999e93c8ac047 100644 --- a/src/plugins/debugger/cdb/cdbmodules.cpp +++ b/src/plugins/debugger/cdb/cdbmodules.cpp @@ -43,7 +43,7 @@ static inline bool getModuleCount(CIDebugSymbols *syms, ULONG *count, QString *e ULONG loadedCount, unloadedCount; const HRESULT hr = syms->GetNumberModules(&loadedCount, &unloadedCount); if (FAILED(hr)) { - *errorMessage= msgComFailed("GetNumberModules", hr); + *errorMessage= CdbCore::msgComFailed("GetNumberModules", hr); return false; } *count = loadedCount + unloadedCount; @@ -75,7 +75,7 @@ bool getModuleList(CIDebugSymbols *syms, QList<Module> *modules, QString *errorM HRESULT hr = syms->GetModuleParameters(count, 0, 0u, parmPtr); // E_INVALIDARG indicates 'Partial results' according to docu if (FAILED(hr) && hr != E_INVALIDARG) { - *errorMessage= msgComFailed("GetModuleParameters", hr); + *errorMessage= CdbCore::msgComFailed("GetModuleParameters", hr); return false; } // fill array @@ -91,7 +91,7 @@ bool getModuleList(CIDebugSymbols *syms, QList<Module> *modules, QString *errorM module.endAddress = hexPrefix + QString::number((p.Base + p.Size), 16); hr = syms->GetModuleNameStringWide(DEBUG_MODNAME_IMAGE, m, 0, wszBuf, MAX_PATH - 1, 0); if (FAILED(hr) && hr != E_INVALIDARG) { - *errorMessage= msgComFailed("GetModuleNameStringWide", hr); + *errorMessage= CdbCore::msgComFailed("GetModuleNameStringWide", hr); return false; } module.moduleName = QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf)); @@ -116,7 +116,7 @@ bool searchSymbols(CIDebugSymbols *syms, const QString &pattern, return true; } if (FAILED(hr)) { - *errorMessage= msgComFailed("StartSymbolMatchWide", hr); + *errorMessage= CdbCore::msgComFailed("StartSymbolMatchWide", hr); return false; } WCHAR wszBuf[MAX_PATH]; diff --git a/src/plugins/debugger/cdb/cdboptions.cpp b/src/plugins/debugger/cdb/cdboptions.cpp index 320feb74b47e734282b5ce62ccbad5b3ff3109ca..e3ecd1b5b0477d8d82ffe72599967f5e27625fa1 100644 --- a/src/plugins/debugger/cdb/cdboptions.cpp +++ b/src/plugins/debugger/cdb/cdboptions.cpp @@ -28,6 +28,7 @@ **************************************************************************/ #include "cdboptions.h" +#include "coreengine.h" #include <QtCore/QSettings> #include <QtCore/QDir> @@ -65,7 +66,7 @@ void CdbOptions::fromSettings(const QSettings *s) const QString enabledKey = keyRoot + QLatin1String(enabledKeyC); const bool firstTime = !s->contains(enabledKey); if (firstTime) { - enabled = autoDetectPath(&path); + enabled = CdbCore::CoreEngine::autoDetectPath(&path); return; } enabled = s->value(enabledKey, false).toBool(); @@ -86,46 +87,6 @@ void CdbOptions::toSettings(QSettings *s) const s->endGroup(); } -bool CdbOptions::autoDetectPath(QString *outPath, QStringList *checkedDirectories /* = 0 */) -{ - // Look for $ProgramFiles/"Debugging Tools For Windows <bit-idy>" and its - // " (x86)", " (x64)" variations. Qt Creator needs 64/32 bit depending - // on how it was built. -#ifdef Q_OS_WIN64 - static const char *postFixes[] = {" (x64)", " 64-bit" }; -#else - static const char *postFixes[] = { " (x86)", " (x32)" }; -#endif - - outPath->clear(); - const QByteArray programDirB = qgetenv("ProgramFiles"); - if (programDirB.isEmpty()) - return false; - - const QString programDir = QString::fromLocal8Bit(programDirB) + QDir::separator(); - const QString installDir = QLatin1String("Debugging Tools For Windows"); - - QString path = programDir + installDir; - if (checkedDirectories) - checkedDirectories->push_back(path); - if (QFileInfo(path).isDir()) { - *outPath = QDir::toNativeSeparators(path); - return true; - } - const int rootLength = path.size(); - for (int i = 0; i < sizeof(postFixes)/sizeof(const char*); i++) { - path.truncate(rootLength); - path += QLatin1String(postFixes[i]); - if (checkedDirectories) - checkedDirectories->push_back(path); - if (QFileInfo(path).isDir()) { - *outPath = QDir::toNativeSeparators(path); - return true; - } - } - return false; -} - unsigned CdbOptions::compare(const CdbOptions &rhs) const { unsigned rc = 0; diff --git a/src/plugins/debugger/cdb/cdboptions.h b/src/plugins/debugger/cdb/cdboptions.h index 14709d1f86bd0024b7ff1f4edf4b164e099b6b3c..887b13c862e03339bee3ab5269db1ee2271a3c94 100644 --- a/src/plugins/debugger/cdb/cdboptions.h +++ b/src/plugins/debugger/cdb/cdboptions.h @@ -54,9 +54,6 @@ public: SymbolOptionsChanged = 0x4 }; unsigned compare(const CdbOptions &s) const; - // Locate the debugging tools - static bool autoDetectPath(QString *path, QStringList *checkedDirectories = 0); - bool enabled; QString path; QStringList symbolPaths; diff --git a/src/plugins/debugger/cdb/cdboptionspage.cpp b/src/plugins/debugger/cdb/cdboptionspage.cpp index f8a97f4e67c06a2c04810fc25db523d080734f39..583f6d77920ebe12a717270dbed69de1f2f6fdb3 100644 --- a/src/plugins/debugger/cdb/cdboptionspage.cpp +++ b/src/plugins/debugger/cdb/cdboptionspage.cpp @@ -30,6 +30,7 @@ #include "cdboptionspage.h" #include "cdboptions.h" #include "debuggerconstants.h" +#include "coreengine.h" #include <coreplugin/icore.h> @@ -102,7 +103,7 @@ void CdbOptionsPageWidget::autoDetect() { QString path; QStringList checkedDirectories; - const bool ok = CdbOptions::autoDetectPath(&path, &checkedDirectories); + const bool ok = CdbCore::CoreEngine::autoDetectPath(&path, &checkedDirectories); m_ui.cdbPathGroupBox->setChecked(ok); if (ok) { m_ui.pathChooser->setPath(path); diff --git a/src/plugins/debugger/cdb/cdbstackframecontext.cpp b/src/plugins/debugger/cdb/cdbstackframecontext.cpp index 46e6aadefcb3c60dde9f25e4f9422711853251d2..90d0e3e4e8090db4b7263c70a805ae5c23cc5c02 100644 --- a/src/plugins/debugger/cdb/cdbstackframecontext.cpp +++ b/src/plugins/debugger/cdb/cdbstackframecontext.cpp @@ -33,6 +33,7 @@ #include "cdbdumperhelper.h" #include "debuggeractions.h" #include "watchhandler.h" +#include "coreengine.h" #include <QtCore/QDebug> #include <QtCore/QCoreApplication> diff --git a/src/plugins/debugger/cdb/cdbstacktracecontext.cpp b/src/plugins/debugger/cdb/cdbstacktracecontext.cpp index 8eb1998e71dc09be95908bbce77beee2447f77e9..bc9143032ae5dfd50cfcf34b6d457ea0f96c0909 100644 --- a/src/plugins/debugger/cdb/cdbstacktracecontext.cpp +++ b/src/plugins/debugger/cdb/cdbstacktracecontext.cpp @@ -28,6 +28,7 @@ **************************************************************************/ #include "cdbstacktracecontext.h" +#include "coreengine.h" #include "cdbstackframecontext.h" #include "cdbbreakpoint.h" #include "cdbsymbolgroupcontext.h" @@ -67,7 +68,7 @@ CdbStackTraceContext *CdbStackTraceContext::create(const QSharedPointer<CdbDumpe const HRESULT hr = dumper->comInterfaces()->debugControl->GetStackTrace(0, 0, 0, ctx->m_cdbFrames, CdbStackTraceContext::maxFrames, &frameCount); if (FAILED(hr)) { delete ctx; - *errorMessage = msgComFailed("GetStackTrace", hr); + *errorMessage = CdbCore::msgComFailed("GetStackTrace", hr); return 0; } if (!ctx->init(frameCount, errorMessage)) { @@ -189,20 +190,20 @@ CIDebugSymbolGroup *CdbStackTraceContext::createSymbolGroup(int index, QString * CIDebugSymbolGroup *sg = 0; HRESULT hr = m_cif->debugSymbols->GetScopeSymbolGroup2(DEBUG_SCOPE_GROUP_LOCALS, NULL, &sg); if (FAILED(hr)) { - *errorMessage = msgComFailed("GetScopeSymbolGroup", hr); + *errorMessage = CdbCore::msgComFailed("GetScopeSymbolGroup", hr); return 0; } hr = m_cif->debugSymbols->SetScope(0, m_cdbFrames + index, NULL, 0); if (FAILED(hr)) { - *errorMessage = msgComFailed("SetScope", hr); + *errorMessage = CdbCore::msgComFailed("SetScope", hr); sg->Release(); return 0; } // refresh with current frame hr = m_cif->debugSymbols->GetScopeSymbolGroup2(DEBUG_SCOPE_GROUP_LOCALS, sg, &sg); if (FAILED(hr)) { - *errorMessage = msgComFailed("GetScopeSymbolGroup", hr); + *errorMessage = CdbCore::msgComFailed("GetScopeSymbolGroup", hr); sg->Release(); return 0; } @@ -247,7 +248,7 @@ static inline QString msgGetThreadStateFailed(unsigned long threadId, const QStr // Determine information about thread. This changes the // current thread to thread->id. -static inline bool getStoppedThreadState(const CdbComInterfaces &cif, +static inline bool getStoppedThreadState(const CdbCore::ComInterfaces &cif, ThreadData *t, QString *errorMessage) { @@ -255,13 +256,13 @@ static inline bool getStoppedThreadState(const CdbComInterfaces &cif, ULONG currentThread; HRESULT hr = cif.debugSystemObjects->GetCurrentThreadId(¤tThread); if (FAILED(hr)) { - *errorMessage = msgGetThreadStateFailed(t->id, msgComFailed("GetCurrentThreadId", hr)); + *errorMessage = msgGetThreadStateFailed(t->id, CdbCore::msgComFailed("GetCurrentThreadId", hr)); return false; } if (currentThread != t->id) { hr = cif.debugSystemObjects->SetCurrentThreadId(t->id); if (FAILED(hr)) { - *errorMessage = msgGetThreadStateFailed(t->id, msgComFailed("SetCurrentThreadId", hr)); + *errorMessage = msgGetThreadStateFailed(t->id, CdbCore::msgComFailed("SetCurrentThreadId", hr)); return false; } } @@ -271,7 +272,7 @@ static inline bool getStoppedThreadState(const CdbComInterfaces &cif, DEBUG_STACK_FRAME frames[MaxFrames]; hr = cif.debugControl->GetStackTrace(0, 0, 0, frames, MaxFrames, &frameCount); if (FAILED(hr)) { - *errorMessage = msgGetThreadStateFailed(t->id, msgComFailed("GetStackTrace", hr)); + *errorMessage = msgGetThreadStateFailed(t->id, CdbCore::msgComFailed("GetStackTrace", hr)); return false; } // Ignore the top frame if it is "ntdll!KiFastSystemCallRet", which is @@ -307,7 +308,7 @@ static inline QString msgGetThreadsFailed(const QString &why) return QString::fromLatin1("Unable to determine the thread information: %1").arg(why); } -bool CdbStackTraceContext::getThreads(const CdbComInterfaces &cif, +bool CdbStackTraceContext::getThreads(const CdbCore::ComInterfaces &cif, bool isStopped, QList<ThreadData> *threads, ULONG *currentThreadId, @@ -318,7 +319,7 @@ bool CdbStackTraceContext::getThreads(const CdbComInterfaces &cif, *currentThreadId = 0; HRESULT hr= cif.debugSystemObjects->GetNumberThreads(&threadCount); if (FAILED(hr)) { - *errorMessage= msgGetThreadsFailed(msgComFailed("GetNumberThreads", hr)); + *errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("GetNumberThreads", hr)); return false; } // Get ids and index of current @@ -326,14 +327,14 @@ bool CdbStackTraceContext::getThreads(const CdbComInterfaces &cif, return true; hr = cif.debugSystemObjects->GetCurrentThreadId(currentThreadId); if (FAILED(hr)) { - *errorMessage= msgGetThreadsFailed(msgComFailed("GetCurrentThreadId", hr)); + *errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("GetCurrentThreadId", hr)); return false; } QVector<ULONG> threadIds(threadCount); hr = cif.debugSystemObjects->GetThreadIdsByIndex(0, threadCount, &(*threadIds.begin()), 0); if (FAILED(hr)) { - *errorMessage= msgGetThreadsFailed(msgComFailed("GetThreadIdsByIndex", hr)); + *errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("GetThreadIdsByIndex", hr)); return false; } for (ULONG i = 0; i < threadCount; i++) { @@ -350,7 +351,7 @@ bool CdbStackTraceContext::getThreads(const CdbComInterfaces &cif, if (isStopped && threads->back().id != *currentThreadId) { hr = cif.debugSystemObjects->SetCurrentThreadId(*currentThreadId); if (FAILED(hr)) { - *errorMessage= msgGetThreadsFailed(msgComFailed("SetCurrentThreadId", hr)); + *errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("SetCurrentThreadId", hr)); return false; } } diff --git a/src/plugins/debugger/cdb/cdbstacktracecontext.h b/src/plugins/debugger/cdb/cdbstacktracecontext.h index ac9cb030b06b3f6f3ad9659708e462a2a617066b..873fa7404a0f535d34a331b1346961c28e3929a4 100644 --- a/src/plugins/debugger/cdb/cdbstacktracecontext.h +++ b/src/plugins/debugger/cdb/cdbstacktracecontext.h @@ -42,10 +42,13 @@ QT_BEGIN_NAMESPACE class QTextStream; QT_END_NAMESPACE +namespace CdbCore { + struct ComInterfaces; +} + namespace Debugger { namespace Internal { -struct CdbComInterfaces; class CdbSymbolGroupContext; class CdbStackFrameContext; class CdbDumperHelper; @@ -93,7 +96,7 @@ public: // Retrieve information about threads. When stopped, add // current stack frame. - static bool getThreads(const CdbComInterfaces &cif, + static bool getThreads(const CdbCore::ComInterfaces &cif, bool isStopped, QList<ThreadData> *threads, ULONG *currentThreadId, @@ -104,7 +107,7 @@ private: CIDebugSymbolGroup *createSymbolGroup(int index, QString *errorMessage); const QSharedPointer<CdbDumperHelper> m_dumper; - CdbComInterfaces *m_cif; + const CdbCore::ComInterfaces *m_cif; DEBUG_STACK_FRAME m_cdbFrames[maxFrames]; QVector <CdbStackFrameContext*> m_frameContexts; diff --git a/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp index 480c9a74438ab5d775549a176d0ab7dfdd5f1e86..dc9cf017907b5afecadd932298bbec41fa2aeb4c 100644 --- a/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp +++ b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp @@ -175,7 +175,7 @@ bool CdbSymbolGroupContext::init(QString *errorMessage) ULONG count; HRESULT hr = m_symbolGroup->GetNumberSymbols(&count); if (FAILED(hr)) { - *errorMessage = msgComFailed("GetNumberSymbols", hr); + *errorMessage = CdbCore::msgComFailed("GetNumberSymbols", hr); return false; } @@ -185,7 +185,8 @@ bool CdbSymbolGroupContext::init(QString *errorMessage) hr = m_symbolGroup->GetSymbolParameters(0, count, symbolParameters()); if (FAILED(hr)) { - *errorMessage = QString::fromLatin1("In %1: %2 (%3 symbols)").arg(QLatin1String(Q_FUNC_INFO), msgComFailed("GetSymbolParameters", hr)).arg(count); + *errorMessage = QString::fromLatin1("In %1: %2 (%3 symbols)").arg(QLatin1String(Q_FUNC_INFO), + CdbCore::msgComFailed("GetSymbolParameters", hr)).arg(count); return false; } populateINameIndexMap(m_prefix, DEBUG_ANY_ID, count); @@ -355,7 +356,7 @@ bool CdbSymbolGroupContext::expandSymbol(const QString &prefix, unsigned long in HRESULT hr = m_symbolGroup->ExpandSymbol(index, TRUE); if (FAILED(hr)) { - *errorMessage = msgExpandFailed(prefix, index, msgComFailed("ExpandSymbol", hr)); + *errorMessage = msgExpandFailed(prefix, index, CdbCore::msgComFailed("ExpandSymbol", hr)); return false; } // Hopefully, this will never fail, else data structure will be foobar. @@ -363,7 +364,7 @@ bool CdbSymbolGroupContext::expandSymbol(const QString &prefix, unsigned long in ULONG newSize; hr = m_symbolGroup->GetNumberSymbols(&newSize); if (FAILED(hr)) { - *errorMessage = msgExpandFailed(prefix, index, msgComFailed("GetNumberSymbols", hr)); + *errorMessage = msgExpandFailed(prefix, index, CdbCore::msgComFailed("GetNumberSymbols", hr)); return false; } @@ -373,7 +374,7 @@ bool CdbSymbolGroupContext::expandSymbol(const QString &prefix, unsigned long in hr = m_symbolGroup->GetSymbolParameters(0, newSize, symbolParameters()); if (FAILED(hr)) { - *errorMessage = msgExpandFailed(prefix, index, msgComFailed("GetSymbolParameters", hr)); + *errorMessage = msgExpandFailed(prefix, index, CdbCore::msgComFailed("GetSymbolParameters", hr)); return false; } // The new symbols are inserted after the parent symbol. @@ -531,7 +532,7 @@ bool CdbSymbolGroupContext::assignValue(const QString &iname, const QString &val const HRESULT hr = m_symbolGroup->WriteSymbolWide(index, reinterpret_cast<PCWSTR>(value.utf16())); if (FAILED(hr)) { *errorMessage = QString::fromLatin1("Unable to assign '%1' to '%2': %3"). - arg(value, iname, msgComFailed("WriteSymbolWide", hr)); + arg(value, iname, CdbCore::msgComFailed("WriteSymbolWide", hr)); return false; } if (newValue) @@ -539,126 +540,6 @@ bool CdbSymbolGroupContext::assignValue(const QString &iname, const QString &val return true; } -// format an array of integers as "0x323, 0x2322, ..." -template <class Integer> -static QString formatArrayHelper(const Integer *array, int size, int base = 10) -{ - QString rc; - const QString hexPrefix = QLatin1String("0x"); - const QString separator = QLatin1String(", "); - const bool hex = base == 16; - for (int i = 0; i < size; i++) { - if (i) - rc += separator; - if (hex) - rc += hexPrefix; - rc += QString::number(array[i], base); - } - return rc; -} - -QString CdbSymbolGroupContext::hexFormatArray(const unsigned short *array, int size) -{ - return formatArrayHelper(array, size, 16); -} - -// Helper to format an integer with -// a hex prefix in case base = 16 -template <class Integer> - inline QString formatInteger(Integer value, int base) -{ - QString rc; - if (base == 16) - rc = QLatin1String("0x"); - rc += QString::number(value, base); - return rc; -} - -QString CdbSymbolGroupContext::debugValueToString(const DEBUG_VALUE &dv, CIDebugControl *ctl, - QString *qType, - int integerBase) -{ - switch (dv.Type) { - case DEBUG_VALUE_INT8: - if (qType) - *qType = QLatin1String("char"); - return formatInteger(dv.I8, integerBase); - case DEBUG_VALUE_INT16: - if (qType) - *qType = QLatin1String("short"); - return formatInteger(static_cast<short>(dv.I16), integerBase); - case DEBUG_VALUE_INT32: - if (qType) - *qType = QLatin1String("long"); - return formatInteger(static_cast<long>(dv.I32), integerBase); - case DEBUG_VALUE_INT64: - if (qType) - *qType = QLatin1String("long long"); - return formatInteger(static_cast<long long>(dv.I64), integerBase); - case DEBUG_VALUE_FLOAT32: - if (qType) - *qType = QLatin1String("float"); - return QString::number(dv.F32); - case DEBUG_VALUE_FLOAT64: - if (qType) - *qType = QLatin1String("double"); - return QString::number(dv.F64); - case DEBUG_VALUE_FLOAT80: - case DEBUG_VALUE_FLOAT128: { // Convert to double - DEBUG_VALUE doubleValue; - double d = 0.0; - if (SUCCEEDED(ctl->CoerceValue(const_cast<DEBUG_VALUE*>(&dv), DEBUG_VALUE_FLOAT64, &doubleValue))) - d = dv.F64; - if (qType) - *qType = QLatin1String(dv.Type == DEBUG_VALUE_FLOAT80 ? "80bit-float" : "128bit-float"); - return QString::number(d); - } - case DEBUG_VALUE_VECTOR64: { - if (qType) - *qType = QLatin1String("64bit-vector"); - QString rc = QLatin1String("bytes: "); - rc += formatArrayHelper(dv.VI8, 8, integerBase); - rc += QLatin1String(" long: "); - rc += formatArrayHelper(dv.VI32, 2, integerBase); - return rc; - } - case DEBUG_VALUE_VECTOR128: { - if (qType) - *qType = QLatin1String("128bit-vector"); - QString rc = QLatin1String("bytes: "); - rc += formatArrayHelper(dv.VI8, 16, integerBase); - rc += QLatin1String(" long long: "); - rc += formatArrayHelper(dv.VI64, 2, integerBase); - return rc; - } - } - if (qType) - *qType = QString::fromLatin1("Unknown type #%1:").arg(dv.Type); - return formatArrayHelper(dv.RawBytes, 24, integerBase); -} - -bool CdbSymbolGroupContext::debugValueToInteger(const DEBUG_VALUE &dv, qint64 *value) -{ - *value = 0; - switch (dv.Type) { - case DEBUG_VALUE_INT8: - *value = dv.I8; - return true; - case DEBUG_VALUE_INT16: - *value = static_cast<short>(dv.I16); - return true; - case DEBUG_VALUE_INT32: - *value = static_cast<long>(dv.I32); - return true; - case DEBUG_VALUE_INT64: - *value = static_cast<long long>(dv.I64); - return true; - default: - break; - } - return false; -} - /* The special type dumpers have an integer return code meaning: * 0: ok * 1: Dereferencing or retrieving memory failed, this is out of scope, diff --git a/src/plugins/debugger/cdb/cdbsymbolgroupcontext.h b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.h index 2db042423f07f7b170af324ad1876a358f09a790..b99cbf154979bdc9c00638b8ef1d3626e0ea823b 100644 --- a/src/plugins/debugger/cdb/cdbsymbolgroupcontext.h +++ b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.h @@ -133,13 +133,6 @@ public: inline bool isExpanded(unsigned long index) const { return symbolState(index) == ExpandedSymbol; } inline bool isExpanded(const QString &prefix) const { return symbolState(prefix) == ExpandedSymbol; } - // Helper to convert a DEBUG_VALUE structure to a string representation - static QString debugValueToString(const DEBUG_VALUE &dv, CIDebugControl *ctl, QString *type = 0, int integerBase = 10); - static bool debugValueToInteger(const DEBUG_VALUE &dv, qint64 *value); - - // format an array of unsigned longs as "0x323, 0x2322, ..." - static QString hexFormatArray(const unsigned short *array, int size); - // Dump enum DumperResult { DumperOk, DumperError, DumperNotHandled }; DumperResult dump(CIDebugDataSpaces *ds, WatchData *wd); diff --git a/src/plugins/debugger/cdb/coreengine.cpp b/src/plugins/debugger/cdb/coreengine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..77ca81e8cf31ea1883a07551f67b4ef8fa1ef622 --- /dev/null +++ b/src/plugins/debugger/cdb/coreengine.cpp @@ -0,0 +1,944 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (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 http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "coreengine.h" +#include "debugeventcallbackbase.h" +#include "debugoutputbase.h" + +#include <utils/winutils.h> +#include <utils/abstractprocess.h> + +#include <QtCore/QCoreApplication> +#include <QtCore/QDir> +#include <QtCore/QLibrary> +#include <QtCore/QDebug> + +#define DBGHELP_TRANSLATE_TCHAR +#include <inc/Dbghelp.h> + +static const char *dbgHelpDllC = "dbghelp"; +static const char *dbgEngineDllC = "dbgeng"; +static const char *debugCreateFuncC = "DebugCreate"; + +enum { debug = 0 }; + +static inline QString msgLibLoadFailed(const QString &lib, const QString &why) +{ + return QCoreApplication::translate("Debugger::Cdb", + "Unable to load the debugger engine library '%1': %2"). + arg(lib, why); +} + +static inline ULONG getInterruptTimeOutSecs(CIDebugControl *ctl) +{ + ULONG rc = 0; + ctl->GetInterruptTimeout(&rc); + return rc; +} + +namespace CdbCore { + +QString msgDebugEngineComResult(HRESULT hr) +{ + switch (hr) { + case S_OK: + return QLatin1String("S_OK"); + case S_FALSE: + return QLatin1String("S_FALSE"); + case E_FAIL: + break; + case E_INVALIDARG: + return QLatin1String("E_INVALIDARG"); + case E_NOINTERFACE: + return QLatin1String("E_NOINTERFACE"); + case E_OUTOFMEMORY: + return QLatin1String("E_OUTOFMEMORY"); + case E_UNEXPECTED: + return QLatin1String("E_UNEXPECTED"); + case 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 QLatin1String("E_FAIL ") + Utils::winErrorMessage(HRESULT_CODE(hr)); +} + +QString msgComFailed(const char *func, HRESULT hr) +{ + return QString::fromLatin1("%1 failed: %2").arg(QLatin1String(func), msgDebugEngineComResult(hr)); +} + +QString msgDebuggerCommandFailed(const QString &command, HRESULT hr) +{ + return QString::fromLatin1("Unable to execute '%1': %2").arg(command, msgDebugEngineComResult(hr)); +} + +const char *msgExecutionStatusString(ULONG executionStatus) +{ + switch (executionStatus) { + case DEBUG_STATUS_NO_CHANGE: + return "DEBUG_STATUS_NO_CHANGE"; + case DEBUG_STATUS_GO: + return "DEBUG_STATUS_GO"; + case DEBUG_STATUS_GO_HANDLED: + return "DEBUG_STATUS_GO_HANDLED"; + case DEBUG_STATUS_GO_NOT_HANDLED: + return "DEBUG_STATUS_GO_NOT_HANDLED"; + case DEBUG_STATUS_STEP_OVER: + return "DEBUG_STATUS_STEP_OVER"; + case DEBUG_STATUS_STEP_INTO: + return "DEBUG_STATUS_STEP_INTO"; + case DEBUG_STATUS_BREAK: + return "DEBUG_STATUS_BREAK"; + case DEBUG_STATUS_NO_DEBUGGEE: + return "DEBUG_STATUS_NO_DEBUGGEE"; + case DEBUG_STATUS_STEP_BRANCH: + return "DEBUG_STATUS_STEP_BRANCH"; + case DEBUG_STATUS_IGNORE_EVENT: + return "DEBUG_STATUS_IGNORE_EVENT"; + case DEBUG_STATUS_RESTART_REQUESTED: + return "DEBUG_STATUS_RESTART_REQUESTED"; + case DEBUG_STATUS_REVERSE_GO: + return "DEBUG_STATUS_REVERSE_GO"; + case DEBUG_STATUS_REVERSE_STEP_BRANCH: + return "DEBUG_STATUS_REVERSE_STEP_BRANCH"; + case DEBUG_STATUS_REVERSE_STEP_OVER: + return "DEBUG_STATUS_REVERSE_STEP_OVER"; + case DEBUG_STATUS_REVERSE_STEP_INTO: + return "DEBUG_STATUS_REVERSE_STEP_INTO"; + default: + break; + } + return "<Unknown execution status>"; +} + +// ComInterfaces +ComInterfaces::ComInterfaces() : + debugClient(0), + debugControl(0), + debugSystemObjects(0), + debugSymbols(0), + debugRegisters(0), + debugDataSpaces(0) +{ +} + +// Thin wrapper around the 'DBEng' debugger engine shared library +// which is loaded at runtime. + +class DebuggerEngineLibrary +{ +public: + DebuggerEngineLibrary(); + bool init(const QString &path, QString *dbgEngDLL, QString *errorMessage); + + inline HRESULT debugCreate(REFIID interfaceId, PVOID *interfaceHandle) const + { return m_debugCreate(interfaceId, interfaceHandle); } + +private: + // The exported functions of the library + typedef HRESULT (STDAPICALLTYPE *DebugCreateFunction)(REFIID, PVOID *); + + DebugCreateFunction m_debugCreate; +}; + +// --------- DebuggerEngineLibrary +DebuggerEngineLibrary::DebuggerEngineLibrary() : + m_debugCreate(0) +{ +} + +// Build a lib name as "Path\x.dll" +static inline QString libPath(const QString &libName, const QString &path = QString()) +{ + QString rc = path; + if (!rc.isEmpty()) + rc += QDir::separator(); + rc += libName; + rc += QLatin1String(".dll"); + return rc; +} + +bool DebuggerEngineLibrary::init(const QString &path, + QString *dbgEngDLL, + QString *errorMessage) +{ + // Load the dependent help lib first + const QString helpLibPath = libPath(QLatin1String(dbgHelpDllC), path); + QLibrary helpLib(helpLibPath, 0); + if (!helpLib.isLoaded() && !helpLib.load()) { + *errorMessage = msgLibLoadFailed(helpLibPath, helpLib.errorString()); + return false; + } + // Load dbgeng lib + const QString engineLibPath = libPath(QLatin1String(dbgEngineDllC), path); + QLibrary lib(engineLibPath, 0); + if (!lib.isLoaded() && !lib.load()) { + *errorMessage = msgLibLoadFailed(engineLibPath, lib.errorString()); + return false; + } + *dbgEngDLL = engineLibPath; + // Locate symbols + void *createFunc = lib.resolve(debugCreateFuncC); + if (!createFunc) { + *errorMessage = QCoreApplication::translate("Debugger::Cdb", "Unable to resolve '%1' in the debugger engine library '%2'"). + arg(QLatin1String(debugCreateFuncC), QLatin1String(dbgEngineDllC)); + return false; + } + m_debugCreate = static_cast<DebugCreateFunction>(createFunc); + return true; +} + +// ------ Engine +CoreEngine::CoreEngine(QObject *parent) : + QObject(parent), + m_watchTimer(-1) +{ +} + +CoreEngine::~CoreEngine() +{ + + if (m_cif.debugClient) { + m_cif.debugClient->SetOutputCallbacksWide(0); + m_cif.debugClient->SetEventCallbacksWide(0); + 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(); +} + +bool CoreEngine::init(const QString &dllEnginePath, QString *errorMessage) +{ + enum { bufLen = 10240 }; + // Load the DLL + DebuggerEngineLibrary lib; + if (!lib.init(dllEnginePath, &m_dbengDLL, errorMessage)) + return false; + // Initialize the COM interfaces + HRESULT hr; + 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; + } + + 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; + } + + 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_cif.debugSymbols)); + if (FAILED(hr)) { + *errorMessage = QString::fromLatin1("Creation of IDebugSymbols3 failed: %1").arg(msgDebugEngineComResult(hr)); + return false; + } + + WCHAR buf[bufLen]; + hr = m_cif.debugSymbols->GetImagePathWide(buf, bufLen, 0); + if (FAILED(hr)) { + *errorMessage = msgComFailed("GetImagePathWide", hr); + return false; + } + m_baseImagePath = QString::fromUtf16(buf); + + 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 (debug) + qDebug() << QString::fromLatin1("CDB Initialization succeeded, interrupt time out %1s.").arg(getInterruptTimeOutSecs(m_cif.debugControl)); + return true; +} + +CoreEngine::DebugOutputBasePtr + CoreEngine::setDebugOutput(const DebugOutputBasePtr &o) +{ + const CoreEngine::DebugOutputBasePtr old = m_debugOutput; + m_debugOutput = o; + m_cif.debugClient->SetOutputCallbacksWide(m_debugOutput.data()); + return old; +} + +CoreEngine::DebugEventCallbackBasePtr + CoreEngine::setDebugEventCallback(const DebugEventCallbackBasePtr &e) +{ + const CoreEngine::DebugEventCallbackBasePtr old = m_debugEventCallback; + m_debugEventCallback = e; + m_cif.debugClient->SetEventCallbacksWide(m_debugEventCallback.data()); + return old; +} + +void CoreEngine::startWatchTimer() +{ + if (debug) + qDebug() << Q_FUNC_INFO; + + if (m_watchTimer == -1) + m_watchTimer = startTimer(0); +} + +void CoreEngine::killWatchTimer() +{ + if (debug) + qDebug() << Q_FUNC_INFO; + + if (m_watchTimer != -1) { + killTimer(m_watchTimer); + m_watchTimer = -1; + } +} + +HRESULT CoreEngine::waitForEvent(int timeOutMS) +{ + const HRESULT hr = m_cif.debugControl->WaitForEvent(0, timeOutMS); + if (debug) + if (debug > 1 || hr != S_FALSE) + qDebug() << Q_FUNC_INFO << "WaitForEvent" << timeOutMS << msgDebugEngineComResult(hr); + return hr; +} + +void CoreEngine::timerEvent(QTimerEvent* te) +{ + // Fetch away the debug events and notify if debuggee + // stops. Note that IDebugEventCallback does not + // cover all cases of a debuggee stopping execution + // (such as step over,etc). + if (te->timerId() != m_watchTimer) + return; + + switch (waitForEvent(1)) { + case S_OK: + killWatchTimer(); + emit watchTimerDebugEvent(); + break; + case S_FALSE: + case E_PENDING: + case E_FAIL: + break; + case E_UNEXPECTED: // Occurs on ExitProcess. + killWatchTimer(); + break; + } +} + +bool CoreEngine::startDebuggerWithExecutable(const QString &workingDirectory, + const QString &filename, + const QStringList &args, + const QStringList &envList, + bool verboseSymbolLoading, + QString *errorMessage) +{ + DEBUG_CREATE_PROCESS_OPTIONS dbgopts; + memset(&dbgopts, 0, sizeof(dbgopts)); + dbgopts.CreateFlags = DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS; + + // Set image path + const QFileInfo fi(filename); + QString imagePath = QDir::toNativeSeparators(fi.absolutePath()); + if (!m_baseImagePath.isEmpty()) { + imagePath += QLatin1Char(';'); + imagePath += m_baseImagePath; + } + HRESULT hr = m_cif.debugSymbols->SetImagePathWide(reinterpret_cast<PCWSTR>(imagePath.utf16())); + if (FAILED(hr)) { + *errorMessage = tr("Unable to set the image path to %1: %2").arg(imagePath, msgComFailed("SetImagePathWide", hr)); + return false; + } + + if (debug) + qDebug() << Q_FUNC_INFO <<'\n' << filename << imagePath; + + ULONG symbolOptions = SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS; + if (verboseSymbolLoading) + symbolOptions |= SYMOPT_DEBUG; + m_cif.debugSymbols->SetSymbolOptions(symbolOptions); + + const QString cmd = Utils::AbstractProcess::createWinCommandline(filename, args); + if (debug) + qDebug() << "Starting " << cmd; + PCWSTR env = 0; + QByteArray envData; + if (!envList.empty()) { + envData = Utils::AbstractProcess::createWinEnvironment(Utils::AbstractProcess::fixWinEnvironment(envList)); + env = reinterpret_cast<PCWSTR>(envData.data()); + } + // The working directory cannot be empty. + PCWSTR workingDirC = 0; + const QString workingDirN = workingDirectory.isEmpty() ? QString() : QDir::toNativeSeparators(workingDirectory); + if (!workingDirN.isEmpty()) + workingDirC = workingDirN.utf16(); + hr = m_cif.debugClient->CreateProcess2Wide(NULL, + reinterpret_cast<PWSTR>(const_cast<ushort *>(cmd.utf16())), + &dbgopts, sizeof(dbgopts), + workingDirC, env); + if (FAILED(hr)) { + *errorMessage = tr("Unable to create a process '%1': %2").arg(cmd, msgDebugEngineComResult(hr)); + return false; + } + return true; +} + +bool CoreEngine::startAttachDebugger(qint64 pid, + bool suppressInitialBreakPoint, + QString *errorMessage) +{ + // Need to attrach invasively, otherwise, no notification signals + // for for CreateProcess/ExitProcess occur. + // Initial breakpoint occur: + // 1) Desired: When attaching to a crashed process + // 2) Undesired: When starting up a console process, in conjunction + // with the 32bit Wow-engine + // As of version 6.11, the flag only affects 1). 2) Still needs to be suppressed + // by lookup at the state of the application (startup trap). However, + // there is no startup trap when attaching to a process that has been + // running for a while. (see notifyException). + ULONG flags = DEBUG_ATTACH_INVASIVE_RESUME_PROCESS; + if (suppressInitialBreakPoint) + flags |= DEBUG_ATTACH_INVASIVE_NO_INITIAL_BREAK; + const HRESULT hr = m_cif.debugClient->AttachProcess(NULL, pid, flags); + if (debug) + qDebug() << "Attaching to " << pid << " using flags" << flags << " returns " << hr; + if (FAILED(hr)) { + *errorMessage = tr("Attaching to a process failed for process id %1: %2").arg(pid).arg(msgDebugEngineComResult(hr)); + return false; + } + return true; +} + +static inline QString pathString(const QStringList &s) +{ return s.join(QString(QLatin1Char(';'))); } + +QStringList CoreEngine::sourcePaths() const +{ + WCHAR wszBuf[MAX_PATH]; + if (SUCCEEDED(m_cif.debugSymbols->GetSourcePathWide(wszBuf, MAX_PATH, 0))) + return QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf)).split(QLatin1Char(';')); + return QStringList(); +} + +bool CoreEngine::setSourcePaths(const QStringList &s, QString *errorMessage) +{ + const HRESULT hr = m_cif.debugSymbols->SetSourcePathWide(reinterpret_cast<PCWSTR>(pathString(s).utf16())); + if (FAILED(hr)) { + if (errorMessage) + *errorMessage = msgComFailed("SetSourcePathWide", hr); + return false; + } + return true; +} + +QStringList CoreEngine::symbolPaths() const +{ + WCHAR wszBuf[MAX_PATH]; + if (SUCCEEDED(m_cif.debugSymbols->GetSymbolPathWide(wszBuf, MAX_PATH, 0))) + return QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf)).split(QLatin1Char(';')); + return QStringList(); +} + +bool CoreEngine::setSymbolPaths(const QStringList &s, QString *errorMessage) +{ + const HRESULT hr = m_cif.debugSymbols->SetSymbolPathWide(reinterpret_cast<PCWSTR>(pathString(s).utf16())); + if (FAILED(hr)) { + if (errorMessage) + *errorMessage = msgComFailed("SetSymbolPathWide", hr); + return false; + } + return true; +} + +bool CoreEngine::executeDebuggerCommand(const QString &command, QString *errorMessage) +{ + // output to all clients, else we do not see anything + const HRESULT hr = m_cif.debugControl->ExecuteWide(DEBUG_OUTCTL_ALL_CLIENTS, reinterpret_cast<PCWSTR>(command.utf16()), 0); + if (debug) + qDebug() << "executeDebuggerCommand" << command << SUCCEEDED(hr); + if (FAILED(hr)) { + *errorMessage = QString::fromLatin1("Unable to execute '%1': %2"). + arg(command, msgDebugEngineComResult(hr)); + return false; + } + return true; +} + +// Those should not fail +CoreEngine::ExpressionSyntax CoreEngine::expressionSyntax() const +{ + ULONG expressionSyntax = DEBUG_EXPR_MASM; + const HRESULT hr = m_cif.debugControl->GetExpressionSyntax(&expressionSyntax); + if (FAILED(hr)) + qWarning("Unable to retrieve expression syntax: %s", qPrintable(msgComFailed("GetExpressionSyntax", hr))); + return expressionSyntax == DEBUG_EXPR_MASM ? AssemblerExpressionSyntax : CppExpressionSyntax; +} + +CoreEngine::ExpressionSyntax CoreEngine::setExpressionSyntax(ExpressionSyntax es) +{ + const ExpressionSyntax old = expressionSyntax(); + if (old != es) { + if (debug) + qDebug() << "Setting expression syntax" << es; + const HRESULT hr = m_cif.debugControl->SetExpressionSyntax(es == AssemblerExpressionSyntax ? DEBUG_EXPR_MASM : DEBUG_EXPR_CPLUSPLUS); + if (FAILED(hr)) + qWarning("Unable to set expression syntax: %s", qPrintable(msgComFailed("SetExpressionSyntax", hr))); + } + return old; +} + +CoreEngine::CodeLevel CoreEngine::codeLevel() const +{ + ULONG currentCodeLevel = DEBUG_LEVEL_ASSEMBLY; + HRESULT hr = m_cif.debugControl->GetCodeLevel(¤tCodeLevel); + if (FAILED(hr)) { + qWarning("Cannot determine code level: %s", qPrintable(msgComFailed("GetCodeLevel", hr))); + } + return currentCodeLevel == DEBUG_LEVEL_ASSEMBLY ? CodeLevelAssembly : CodeLevelSource; +} + +CoreEngine::CodeLevel CoreEngine::setCodeLevel(CodeLevel cl) +{ + const CodeLevel old = codeLevel(); + if (old != cl) { + if (debug) + qDebug() << "Setting code level" << cl; + const HRESULT hr = m_cif.debugControl->SetCodeLevel(cl == CodeLevelAssembly ? DEBUG_LEVEL_ASSEMBLY : DEBUG_LEVEL_SOURCE); + if (FAILED(hr)) + qWarning("Unable to set code level: %s", qPrintable(msgComFailed("SetCodeLevel", hr))); + } + return old; +} + +bool CoreEngine::evaluateExpression(const QString &expression, + QString *value, + QString *type, + QString *errorMessage) +{ + DEBUG_VALUE debugValue; + if (!evaluateExpression(expression, &debugValue, errorMessage)) + return false; + *value = debugValueToString(debugValue, type, 10, m_cif.debugControl); + return true; +} + +bool CoreEngine::evaluateExpression(const QString &expression, + DEBUG_VALUE *debugValue, + QString *errorMessage) +{ + if (debug > 1) + qDebug() << Q_FUNC_INFO << expression; + + memset(debugValue, 0, sizeof(DEBUG_VALUE)); + // Use CPP syntax, original syntax must be restored, else setting breakpoints will fail. + SyntaxSetter syntaxSetter(this, CppExpressionSyntax); + Q_UNUSED(syntaxSetter) + ULONG errorPosition = 0; + const HRESULT hr = m_cif.debugControl->EvaluateWide(reinterpret_cast<PCWSTR>(expression.utf16()), + DEBUG_VALUE_INVALID, debugValue, + &errorPosition); + if (FAILED(hr)) { + if (HRESULT_CODE(hr) == 517) { + *errorMessage = QString::fromLatin1("Unable to evaluate '%1': Expression out of scope."). + arg(expression); + } else { + *errorMessage = QString::fromLatin1("Unable to evaluate '%1': Error at %2: %3"). + arg(expression).arg(errorPosition).arg(msgDebugEngineComResult(hr)); + } + return false; + } + return true; +} + +ULONG CoreEngine::executionStatus() const +{ + ULONG ex = DEBUG_STATUS_NO_CHANGE; + const HRESULT hr = m_cif.debugControl->GetExecutionStatus(&ex); + if (FAILED(hr)) + qWarning("Cannot determine execution status: %s", qPrintable(msgComFailed("GetExecutionStatus", hr))); + return ex; +} + +bool CoreEngine::setExecutionStatus(ULONG ex, QString *errorMessage) +{ + const HRESULT hr = m_cif.debugControl->SetExecutionStatus(ex); + if (FAILED(hr)) { + *errorMessage = QString::fromLatin1("Cannot set execution status to %1: %2") + .arg(QLatin1String(msgExecutionStatusString(ex)), msgComFailed("SetExecutionStatus", hr)); + return false; + } + return true; +} + +static inline void appendError(const QString &what, QString *appendableErrorMessage) +{ + if (!appendableErrorMessage->isEmpty()) + appendableErrorMessage->append(QLatin1Char('\n')); + appendableErrorMessage->append(what); +} + +bool CoreEngine::detachCurrentProcess(QString *appendableErrorMessage) +{ + const HRESULT hr = m_cif.debugClient->DetachCurrentProcess(); + if (FAILED(hr)) { + appendError(msgComFailed("DetachCurrentProcess", hr), appendableErrorMessage); + return false; + } + return true; +} + +bool CoreEngine::terminateCurrentProcess(QString *appendableErrorMessage) +{ + const HRESULT hr = m_cif.debugClient->TerminateCurrentProcess(); + if (FAILED(hr)) { + appendError(msgComFailed("TerminateCurrentProcess", hr), appendableErrorMessage); + return false; + } + return true; +} + +bool CoreEngine::terminateProcesses(QString *appendableErrorMessage) +{ + const HRESULT hr = m_cif.debugClient->TerminateProcesses(); + if (FAILED(hr)) { + appendError(msgComFailed("TerminateProcesses", hr), appendableErrorMessage); + return false; + } + return true; +} + +bool CoreEngine::endSession(QString *appendableErrorMessage) +{ + const HRESULT hr = m_cif.debugClient->EndSession(DEBUG_END_PASSIVE); + if (FAILED(hr)) { + appendError(msgComFailed("EndSession", hr), appendableErrorMessage); + return false; + } + return true; +} + +bool CoreEngine::allocDebuggeeMemory(int size, ULONG64 *address, QString *errorMessage) +{ + *address = 0; + const QString allocCmd = QLatin1String(".dvalloc ") + QString::number(size); + + QSharedPointer<StringOutputHandler> outputHandler(new StringOutputHandler); + OutputRedirector redir(this, outputHandler); + Q_UNUSED(redir) + if (!executeDebuggerCommand(allocCmd, errorMessage)) + return false; + // "Allocated 1000 bytes starting at 003a0000" .. hopefully never localized + bool ok = false; + const QString output = outputHandler->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 (debug > 1) + qDebug() << Q_FUNC_INFO << '\n' << output << *address << ok; + if (!ok) { + *errorMessage = QString::fromLatin1("Failed to parse output '%1'").arg(output); + return false; + } + return true; +} + +// Alloc an AscII string in debuggee +bool CoreEngine::createDebuggeeAscIIString(const QString &s, + ULONG64 *address, + QString *errorMessage) +{ + QByteArray sAsciiData = s.toLocal8Bit(); + sAsciiData += '\0'; + if (!allocDebuggeeMemory(sAsciiData.size(), address, errorMessage)) + return false; + const HRESULT hr = m_cif.debugDataSpaces->WriteVirtual(*address, sAsciiData.data(), sAsciiData.size(), 0); + if (FAILED(hr)) { + *errorMessage= msgComFailed("WriteVirtual", hr); + return false; + } + return true; +} + +// Write to debuggee memory in chunks +bool CoreEngine::writeToDebuggee(const QByteArray &buffer, quint64 address, QString *errorMessage) +{ + char *ptr = const_cast<char*>(buffer.data()); + ULONG bytesToWrite = buffer.size(); + while (bytesToWrite > 0) { + ULONG bytesWritten = 0; + const HRESULT hr = m_cif.debugDataSpaces->WriteVirtual(address, ptr, bytesToWrite, &bytesWritten); + if (FAILED(hr)) { + *errorMessage = msgComFailed("WriteVirtual", hr); + return false; + } + bytesToWrite -= bytesWritten; + ptr += bytesWritten; + } + return true; +} + +bool CoreEngine::dissassemble(ULONG64 offset, + unsigned long beforeLines, + unsigned long afterLines, + QString *target, + QString *errorMessage) +{ + const ULONG flags = DEBUG_DISASM_MATCHING_SYMBOLS|DEBUG_DISASM_SOURCE_LINE_NUMBER|DEBUG_DISASM_SOURCE_FILE_NAME; + // Catch the output by temporarily setting another handler. + // We use the method that outputs to the output handler as it + // conveniently provides the 'beforeLines' context (stepping back + // in assembler code). We build a complete string first as line breaks + // may occur in-between messages. + QSharedPointer<StringOutputHandler> outputHandler(new StringOutputHandler); + OutputRedirector redir(this, outputHandler); + // For some reason, we need to output to "all clients" + const HRESULT hr = m_cif.debugControl->OutputDisassemblyLines(DEBUG_OUTCTL_ALL_CLIENTS, + beforeLines, beforeLines + afterLines, + offset, flags, 0, 0, 0, 0); + if (FAILED(hr)) { + *errorMessage= QString::fromLatin1("Unable to dissamble at 0x%1: %2"). + arg(offset, 0, 16).arg(msgComFailed("OutputDisassemblyLines", hr)); + return false; + } + *target = outputHandler->result(); + return true; +} + +bool CoreEngine::autoDetectPath(QString *outPath, + QStringList *checkedDirectories /* = 0 */) +{ + // Look for $ProgramFiles/"Debugging Tools For Windows <bit-idy>" and its + // " (x86)", " (x64)" variations. Qt Creator needs 64/32 bit depending + // on how it was built. +#ifdef Q_OS_WIN64 + static const char *postFixes[] = {" (x64)", " 64-bit" }; +#else + static const char *postFixes[] = { " (x86)", " (x32)" }; +#endif + + outPath->clear(); + const QByteArray programDirB = qgetenv("ProgramFiles"); + if (programDirB.isEmpty()) + return false; + + const QString programDir = QString::fromLocal8Bit(programDirB) + QDir::separator(); + const QString installDir = QLatin1String("Debugging Tools For Windows"); + + QString path = programDir + installDir; + if (checkedDirectories) + checkedDirectories->push_back(path); + if (QFileInfo(path).isDir()) { + *outPath = QDir::toNativeSeparators(path); + return true; + } + const int rootLength = path.size(); + for (int i = 0; i < sizeof(postFixes)/sizeof(const char*); i++) { + path.truncate(rootLength); + path += QLatin1String(postFixes[i]); + if (checkedDirectories) + checkedDirectories->push_back(path); + if (QFileInfo(path).isDir()) { + *outPath = QDir::toNativeSeparators(path); + return true; + } + } + return false; +} + +bool CoreEngine::debugBreakProcess(HANDLE hProcess, QString *errorMessage) +{ + const bool rc = DebugBreakProcess(hProcess); + if (!rc) + *errorMessage = QString::fromLatin1("DebugBreakProcess failed: %1").arg(Utils::winErrorMessage(GetLastError())); + return rc; +} + +bool CoreEngine::setInterrupt(QString *errorMessage) +{ + const HRESULT hr = interfaces().debugControl->SetInterrupt(DEBUG_INTERRUPT_ACTIVE|DEBUG_INTERRUPT_EXIT); + if (FAILED(hr)) { + *errorMessage = QString::fromLatin1("Unable to interrupt debuggee after %1s: %2"). + arg(getInterruptTimeOutSecs(interfaces().debugControl)).arg(msgComFailed("SetInterrupt", hr)); + return false; + } + return true; +} + +// ------------- DEBUG_VALUE formatting helpers + +// format an array of integers as "0x323, 0x2322, ..." +template <class Integer> +static QString formatArrayHelper(const Integer *array, int size, int base = 10) +{ + QString rc; + const QString hexPrefix = QLatin1String("0x"); + const QString separator = QLatin1String(", "); + const bool hex = base == 16; + for (int i = 0; i < size; i++) { + if (i) + rc += separator; + if (hex) + rc += hexPrefix; + rc += QString::number(array[i], base); + } + return rc; +} + +QString hexFormatArray(const unsigned short *array, int size) +{ + return formatArrayHelper(array, size, 16); +} + +// Helper to format an integer with +// a hex prefix in case base = 16 +template <class Integer> + inline QString formatInteger(Integer value, int base) +{ + QString rc; + if (base == 16) + rc = QLatin1String("0x"); + rc += QString::number(value, base); + return rc; +} + +QString debugValueToString(const DEBUG_VALUE &dv, + QString *qType /* =0 */, + int integerBase, + CIDebugControl *ctl /* =0 */) +{ + switch (dv.Type) { + case DEBUG_VALUE_INT8: + if (qType) + *qType = QLatin1String("char"); + return formatInteger(dv.I8, integerBase); + case DEBUG_VALUE_INT16: + if (qType) + *qType = QLatin1String("short"); + return formatInteger(static_cast<short>(dv.I16), integerBase); + case DEBUG_VALUE_INT32: + if (qType) + *qType = QLatin1String("long"); + return formatInteger(static_cast<long>(dv.I32), integerBase); + case DEBUG_VALUE_INT64: + if (qType) + *qType = QLatin1String("long long"); + return formatInteger(static_cast<long long>(dv.I64), integerBase); + case DEBUG_VALUE_FLOAT32: + if (qType) + *qType = QLatin1String("float"); + return QString::number(dv.F32); + case DEBUG_VALUE_FLOAT64: + if (qType) + *qType = QLatin1String("double"); + return QString::number(dv.F64); + case DEBUG_VALUE_FLOAT80: + case DEBUG_VALUE_FLOAT128: { // Convert to double + DEBUG_VALUE doubleValue; + double d = 0.0; + if (ctl && SUCCEEDED(ctl->CoerceValue(const_cast<DEBUG_VALUE*>(&dv), DEBUG_VALUE_FLOAT64, &doubleValue))) { + d = dv.F64; + } else { + qWarning("Unable to convert DEBUG_VALUE_FLOAT80/DEBUG_VALUE_FLOAT128 values"); + } + if (qType) + *qType = QLatin1String(dv.Type == DEBUG_VALUE_FLOAT80 ? "80bit-float" : "128bit-float"); + return QString::number(d); + } + case DEBUG_VALUE_VECTOR64: { + if (qType) + *qType = QLatin1String("64bit-vector"); + QString rc = QLatin1String("bytes: "); + rc += formatArrayHelper(dv.VI8, 8, integerBase); + rc += QLatin1String(" long: "); + rc += formatArrayHelper(dv.VI32, 2, integerBase); + return rc; + } + case DEBUG_VALUE_VECTOR128: { + if (qType) + *qType = QLatin1String("128bit-vector"); + QString rc = QLatin1String("bytes: "); + rc += formatArrayHelper(dv.VI8, 16, integerBase); + rc += QLatin1String(" long long: "); + rc += formatArrayHelper(dv.VI64, 2, integerBase); + return rc; + } + } + if (qType) + *qType = QString::fromLatin1("Unknown type #%1:").arg(dv.Type); + return formatArrayHelper(dv.RawBytes, 24, integerBase); +} + +bool debugValueToInteger(const DEBUG_VALUE &dv, qint64 *value) +{ + *value = 0; + switch (dv.Type) { + case DEBUG_VALUE_INT8: + *value = dv.I8; + return true; + case DEBUG_VALUE_INT16: + *value = static_cast<short>(dv.I16); + return true; + case DEBUG_VALUE_INT32: + *value = static_cast<long>(dv.I32); + return true; + case DEBUG_VALUE_INT64: + *value = static_cast<long long>(dv.I64); + return true; + default: + break; + } + return false; +} + +} // namespace CdbCore diff --git a/src/plugins/debugger/cdb/coreengine.h b/src/plugins/debugger/cdb/coreengine.h new file mode 100644 index 0000000000000000000000000000000000000000..00c8dd0096e9594d4f054865fe40ddf87eb05a69 --- /dev/null +++ b/src/plugins/debugger/cdb/coreengine.h @@ -0,0 +1,191 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (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 http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef COREENGINE_H +#define COREENGINE_H + +#include "cdbcom.h" + +#include <QtCore/QObject> +#include <QtCore/QString> +#include <QtCore/QSharedPointer> + +namespace CdbCore { + +class DebugOutputBase; +class DebugEventCallbackBase; + +// helper struct to pass interfaces around +struct ComInterfaces +{ + ComInterfaces(); + + CIDebugClient* debugClient; + CIDebugControl* debugControl; + CIDebugSystemObjects* debugSystemObjects; + CIDebugSymbols* debugSymbols; + CIDebugRegisters* debugRegisters; + CIDebugDataSpaces* debugDataSpaces; +}; + +class CoreEngine : public QObject +{ + Q_DISABLE_COPY(CoreEngine) + Q_OBJECT +public: + enum ExpressionSyntax { AssemblerExpressionSyntax, CppExpressionSyntax }; + enum CodeLevel { CodeLevelAssembly, CodeLevelSource }; + + typedef QSharedPointer<DebugOutputBase> DebugOutputBasePtr; + typedef QSharedPointer<DebugEventCallbackBase> DebugEventCallbackBasePtr; + + explicit CoreEngine(QObject *parent = 0); + virtual ~CoreEngine(); + + bool init(const QString &dllEnginePath, QString *errorMessage); + // code level/output + + inline const ComInterfaces &interfaces() const { return m_cif; } + + // Set handlers + DebugOutputBasePtr setDebugOutput(const DebugOutputBasePtr &); + DebugEventCallbackBasePtr setDebugEventCallback(const DebugEventCallbackBasePtr &); + + // Start functions + bool startDebuggerWithExecutable(const QString &workingDirectory, + const QString &filename, + const QStringList &args, + const QStringList &env, + bool verboseSymbolLoading, + QString *errorMessage); + + bool startAttachDebugger(qint64 pid, bool suppressInitialBreakPoint, + QString *errorMessage); + + ULONG executionStatus() const; + bool setExecutionStatus(ULONG ex, QString *errorMessage); + + // break & interrupt + bool debugBreakProcess(HANDLE hProcess, QString *errorMessage); + // Currently does not interrupt debuggee + bool setInterrupt(QString *errorMessage); + + // Helpers for terminating the debuggees and ending the session + bool detachCurrentProcess(QString *appendableErrorMessage); + bool terminateCurrentProcess(QString *appendableErrorMessage); + bool terminateProcesses(QString *appendableErrorMessage); + bool endSession(QString *appendableErrorMessage); + + // Watch timer: Listen for debug events and emit watchTimerDebugEvent() should one + // occur. + void startWatchTimer(); + void killWatchTimer(); + inline bool isWatchTimerRunning() const { return m_watchTimer != -1; } + // Synchronous wait + HRESULT waitForEvent(int timeOutMS); + + // Commands and expressions + bool executeDebuggerCommand(const QString &command, QString *errorMessage); + + bool evaluateExpression(const QString &expression, + DEBUG_VALUE *debugValue, + QString *errorMessage); + bool evaluateExpression(const QString &expression, QString *value, + QString *type /* =0 */, QString *errorMessage); + + // Path getters/setters + QStringList sourcePaths() const; + bool setSourcePaths(const QStringList &s, QString *errorMessage); + QStringList symbolPaths() const; + bool setSymbolPaths(const QStringList &s, QString *errorMessage); + + // Options + ExpressionSyntax expressionSyntax() const; + ExpressionSyntax setExpressionSyntax(ExpressionSyntax es); + + CodeLevel codeLevel() const; + CodeLevel setCodeLevel(CodeLevel); + + QString dbengDLL() const { return m_dbengDLL; } + + // Debuggee memory conveniences + bool allocDebuggeeMemory(int size, ULONG64 *address, QString *errorMessage); + bool createDebuggeeAscIIString(const QString &s, ULONG64 *address, QString *errorMessage); + bool writeToDebuggee(const QByteArray &buffer, quint64 address, QString *errorMessage); + // Write to debuggee memory in chunks + bool dissassemble(ULONG64 offset, unsigned long beforeLines, unsigned long afterLines, + QString *target, QString *errorMessage); + + static bool autoDetectPath(QString *outPath, + QStringList *checkedDirectories = 0); + +signals: + void watchTimerDebugEvent(); + +protected: + virtual void timerEvent(QTimerEvent* te); + +private: + ComInterfaces m_cif; + DebugOutputBasePtr m_debugOutput; + DebugEventCallbackBasePtr m_debugEventCallback; + QString m_dbengDLL; + QString m_baseImagePath; + int m_watchTimer; +}; + +// Utility messages +QString msgDebugEngineComResult(HRESULT hr); +QString msgComFailed(const char *func, HRESULT hr); +QString msgDebuggerCommandFailed(const QString &command, HRESULT hr); +const char *msgExecutionStatusString(ULONG executionStatus); + +// 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(CoreEngine *engine, CoreEngine::ExpressionSyntax es) : + m_oldSyntax(engine->setExpressionSyntax(es)), + m_engine(engine) {} + inline ~SyntaxSetter() { m_engine->setExpressionSyntax(m_oldSyntax); } + +private: + const CoreEngine::ExpressionSyntax m_oldSyntax; + CoreEngine *m_engine; +}; + +// Helpers to convert DEBUG_VALUE structs. The optional control is required to +// convert large floating values. +QString debugValueToString(const DEBUG_VALUE &dv, QString *qType =0, int integerBase = 10, CIDebugControl *ctl = 0); +bool debugValueToInteger(const DEBUG_VALUE &dv, qint64 *value); + +} // namespace CdbCore + +#endif // COREENGINE_H diff --git a/src/plugins/debugger/cdb/debugeventcallbackbase.cpp b/src/plugins/debugger/cdb/debugeventcallbackbase.cpp new file mode 100644 index 0000000000000000000000000000000000000000..36e5e0d45c9cc85576582147c70b5aee4f476097 --- /dev/null +++ b/src/plugins/debugger/cdb/debugeventcallbackbase.cpp @@ -0,0 +1,219 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (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 http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "debugeventcallbackbase.h" +#include "coreengine.h" + +namespace CdbCore { + +// DebugEventCallbackBase +DebugEventCallbackBase::DebugEventCallbackBase() +{ +} + +DebugEventCallbackBase::~DebugEventCallbackBase() +{ +} + +STDMETHODIMP DebugEventCallbackBase::QueryInterface( + THIS_ + IN REFIID InterfaceId, + OUT PVOID* Interface) +{ + *Interface = NULL; + + if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) || + IsEqualIID(InterfaceId, __uuidof(IDebugOutputCallbacks))) { + *Interface = (IDebugOutputCallbacks *)this; + AddRef(); + return S_OK; + } else { + return E_NOINTERFACE; + } +} + +STDMETHODIMP_(ULONG) DebugEventCallbackBase::AddRef(THIS) +{ + // This class is designed to be static so + // there's no true refcount. + return 1; +} + +STDMETHODIMP_(ULONG) DebugEventCallbackBase::Release(THIS) +{ + // This class is designed to be static so + // there's no true refcount. + return 0; +} + +STDMETHODIMP DebugEventCallbackBase::Breakpoint(THIS_ __in PDEBUG_BREAKPOINT2) +{ + return S_OK; +} +STDMETHODIMP DebugEventCallbackBase::Exception( + THIS_ + __in PEXCEPTION_RECORD64, + __in ULONG /* FirstChance */ + ) +{ + return S_OK; +} + +STDMETHODIMP DebugEventCallbackBase::CreateThread( + THIS_ + __in ULONG64 /* Handle */, + __in ULONG64 /* DataOffset */, + __in ULONG64 /* StartOffset */ + ) +{ + return S_OK; +} + +STDMETHODIMP DebugEventCallbackBase::ExitThread( + THIS_ + __in ULONG /* ExitCode */ + ) +{ + return S_OK; +} + +STDMETHODIMP DebugEventCallbackBase::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 DebugEventCallbackBase::ExitProcess( + THIS_ + __in ULONG /* ExitCode */ + ) +{ + return S_OK; +} + +STDMETHODIMP DebugEventCallbackBase::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 DebugEventCallbackBase::UnloadModule( + THIS_ + __in_opt PCWSTR /* ImageBaseName */, + __in ULONG64 /* BaseOffset */ + ) +{ + return S_OK; +} + +STDMETHODIMP DebugEventCallbackBase::SystemError( + THIS_ + __in ULONG /* Error */, + __in ULONG /* Level */ + ) +{ + return S_OK; +} + +STDMETHODIMP DebugEventCallbackBase::SessionStatus( + THIS_ + __in ULONG /* Status */ + ) +{ + return S_OK; +} + +STDMETHODIMP DebugEventCallbackBase::ChangeDebuggeeState( + THIS_ + __in ULONG /* Flags */, + __in ULONG64 /* Argument */ + ) +{ + return S_OK; +} + +STDMETHODIMP DebugEventCallbackBase::ChangeEngineState( + THIS_ + __in ULONG /* Flags */, + __in ULONG64 /* Argument */ + ) +{ + return S_OK; +} + +STDMETHODIMP DebugEventCallbackBase::ChangeSymbolState( + THIS_ + __in ULONG /* Flags */, + __in ULONG64 /* Argument */ + ) +{ + return S_OK; +} + +IDebugEventCallbacksWide *DebugEventCallbackBase::getEventCallback(CIDebugClient *clnt) +{ + IDebugEventCallbacksWide *rc = 0; + if (SUCCEEDED(clnt->GetEventCallbacksWide(&rc))) + return rc; + return 0; +} + +EventCallbackRedirector::EventCallbackRedirector(CoreEngine *engine, + const DebugEventCallbackBasePtr &cb) : + m_engine(engine), + m_oldCallback(engine->setDebugEventCallback(cb)) +{ +} + +EventCallbackRedirector::~EventCallbackRedirector() +{ + m_engine->setDebugEventCallback(m_oldCallback); +} + +} // namespace CdbCore diff --git a/src/plugins/debugger/cdb/debugeventcallbackbase.h b/src/plugins/debugger/cdb/debugeventcallbackbase.h new file mode 100644 index 0000000000000000000000000000000000000000..78832c4c0affe2d0be5188a04ec92b4db3f5a689 --- /dev/null +++ b/src/plugins/debugger/cdb/debugeventcallbackbase.h @@ -0,0 +1,174 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (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 http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef DEBUGEVENTCALLBACKBASE_H +#define DEBUGEVENTCALLBACKBASE_H + +#include "cdbcom.h" + +#include <QtCore/QSharedPointer> + +namespace CdbCore { + +class CoreEngine; + +// Base class for event callbacks that takes care +// Active X magic. Provides base implementations with +// the exception of GetInterestMask +class DebugEventCallbackBase : public IDebugEventCallbacksWide +{ +protected: + DebugEventCallbackBase(); +public: + virtual ~DebugEventCallbackBase(); + // IUnknown. + STDMETHOD(QueryInterface)( + THIS_ + IN REFIID InterfaceId, + OUT PVOID* Interface + ); + STDMETHOD_(ULONG, AddRef)( + THIS + ); + STDMETHOD_(ULONG, Release)( + THIS + ); + + // IDebugEventCallbacks. + + 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 + ); + + STDMETHOD(SessionStatus)( + THIS_ + __in ULONG Status + ); + + STDMETHOD(ChangeDebuggeeState)( + THIS_ + __in ULONG Flags, + __in ULONG64 Argument + ); + + STDMETHOD(ChangeEngineState)( + THIS_ + __in ULONG Flags, + __in ULONG64 Argument + ); + + STDMETHOD(ChangeSymbolState)( + THIS_ + __in ULONG Flags, + __in ULONG64 Argument + ); + + + static IDebugEventCallbacksWide *getEventCallback(CIDebugClient *clnt); +}; + +// Utility class to temporarily redirect events to another handler +// as long as in scope +class EventCallbackRedirector { + Q_DISABLE_COPY(EventCallbackRedirector) +public: + typedef QSharedPointer<DebugEventCallbackBase> DebugEventCallbackBasePtr; + + explicit EventCallbackRedirector(CoreEngine *engine, const DebugEventCallbackBasePtr &cb); + ~EventCallbackRedirector(); + +private: + CoreEngine *m_engine; + const DebugEventCallbackBasePtr m_oldCallback; +}; + +} // namespace CdbCore + +#endif // DEBUGEVENTCALLBACKBASE_H diff --git a/src/plugins/debugger/cdb/debugoutputbase.cpp b/src/plugins/debugger/cdb/debugoutputbase.cpp new file mode 100644 index 0000000000000000000000000000000000000000..25e278c1c8e70aedd93a97e8a84f6489e348a425 --- /dev/null +++ b/src/plugins/debugger/cdb/debugoutputbase.cpp @@ -0,0 +1,134 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (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 http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "debugoutputbase.h" +#include "coreengine.h" +#include <QtCore/QDebug> + +namespace CdbCore { + +DebugOutputBase::DebugOutputBase() +{ +} + +DebugOutputBase::~DebugOutputBase() // must be present to avoid exit crashes +{ +} + +STDMETHODIMP DebugOutputBase::QueryInterface( + THIS_ + IN REFIID InterfaceId, + OUT PVOID* Interface + ) +{ + *Interface = NULL; + + if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) || + IsEqualIID(InterfaceId, __uuidof(IDebugOutputCallbacksWide))) + { + *Interface = (IDebugOutputCallbacksWide*)this; + AddRef(); + return S_OK; + } else { + return E_NOINTERFACE; + } +} + +STDMETHODIMP_(ULONG) DebugOutputBase::AddRef(THIS) +{ + // This class is designed to be static so + // there's no true refcount. + return 1; +} + +STDMETHODIMP_(ULONG) DebugOutputBase::Release(THIS) +{ + // This class is designed to be static so + // there's no true refcount. + return 0; +} + +STDMETHODIMP DebugOutputBase::Output( + THIS_ + IN ULONG mask, + IN PCWSTR text + ) +{ + const QString msg = QString::fromUtf16(reinterpret_cast<const ushort *>(text)); + output(mask, msg); + return S_OK; +} + +IDebugOutputCallbacksWide *DebugOutputBase::getOutputCallback(CIDebugClient *client) +{ + IDebugOutputCallbacksWide *rc; + if (FAILED(client->GetOutputCallbacksWide(&rc))) + return 0; + return rc; +} + +const char *DebugOutputBase::maskDescription(ULONG m) +{ + switch (m) { + case DEBUG_OUTPUT_NORMAL: + break; + case DEBUG_OUTPUT_ERROR: + return "error"; + case DEBUG_OUTPUT_WARNING: + return "warn"; + case DEBUG_OUTPUT_VERBOSE: + return "verbose"; + case DEBUG_OUTPUT_PROMPT_REGISTERS: + return "register"; + case DEBUG_OUTPUT_EXTENSION_WARNING: + return "extwarn"; + case DEBUG_OUTPUT_DEBUGGEE: + return "target"; + case DEBUG_OUTPUT_DEBUGGEE_PROMPT: + return "input"; + case DEBUG_OUTPUT_SYMBOLS: + return "symbol"; + default: + break; + } + return "misc"; +} + +OutputRedirector::OutputRedirector(CoreEngine *engine, const DebugOutputBasePtr &o) : + m_engine(engine), + m_oldOutput(engine->setDebugOutput(o)) +{ +} + +OutputRedirector::~OutputRedirector() +{ + m_engine->setDebugOutput(m_oldOutput); +} + +} // namespace CdbCore diff --git a/src/plugins/debugger/cdb/debugoutputbase.h b/src/plugins/debugger/cdb/debugoutputbase.h new file mode 100644 index 0000000000000000000000000000000000000000..029c3c4e37b85cce3cb23aea47680bda1abbe541 --- /dev/null +++ b/src/plugins/debugger/cdb/debugoutputbase.h @@ -0,0 +1,111 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (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 http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef DEBUGOUTPUTBASE_H +#define DEBUGOUTPUTBASE_H + +#include "cdbcom.h" + +#include <QtCore/QString> +#include <QtCore/QSharedPointer> + +namespace CdbCore { + +class CoreEngine; + +// CdbDebugOutputBase is a base class for output handlers +// that takes care of the Active X magic and conversion to QString. + +class DebugOutputBase : public IDebugOutputCallbacksWide +{ +public: + virtual ~DebugOutputBase(); + // IUnknown. + STDMETHOD(QueryInterface)( + THIS_ + IN REFIID InterfaceId, + OUT PVOID* Interface + ); + STDMETHOD_(ULONG, AddRef)( + THIS + ); + STDMETHOD_(ULONG, Release)( + THIS + ); + + // IDebugOutputCallbacks. + STDMETHOD(Output)( + THIS_ + IN ULONG mask, + IN PCWSTR text + ); + + // Helpers to retrieve the output callbacks IF + static IDebugOutputCallbacksWide *getOutputCallback(CIDebugClient *client); + static const char *maskDescription(ULONG m); + +protected: + DebugOutputBase(); + virtual void output(ULONG mask, const QString &message) = 0; +}; + +// An output handler that adds lines to a string (to be +// used for cases in which linebreaks occur in-between calls +// to output). +class StringOutputHandler : public DebugOutputBase +{ +public: + StringOutputHandler() {} + QString result() const { return m_result; } + +protected: + virtual void output(ULONG, const QString &message) { m_result += message; } + +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: + typedef QSharedPointer<DebugOutputBase> DebugOutputBasePtr; + + explicit OutputRedirector(CoreEngine *engine, const DebugOutputBasePtr &o); + ~OutputRedirector(); + +private: + CoreEngine *m_engine; + const DebugOutputBasePtr m_oldOutput; +}; + +} // namespace CdbCore + +#endif // DEBUGOUTPUTBASE_H diff --git a/src/plugins/debugger/debuggermanager.h b/src/plugins/debugger/debuggermanager.h index 9763b362d92a5246ebf768ce3309361d8865a20e..bda011154345fad13ac70b2223088eac3aa952c1 100644 --- a/src/plugins/debugger/debuggermanager.h +++ b/src/plugins/debugger/debuggermanager.h @@ -85,7 +85,7 @@ class IDebuggerEngine; class GdbEngine; class ScriptEngine; class CdbDebugEngine; -struct CdbDebugEnginePrivate; +class CdbDebugEnginePrivate; struct DebuggerManagerActions; class DebuggerPlugin; class CdbDebugEventCallback; @@ -94,7 +94,7 @@ class CdbDumperInitThread; class CdbExceptionLoggerEventCallback; class GdbEngine; class CdbDebugEngine; -struct CdbDebugEnginePrivate; +class CdbDebugEnginePrivate; } // namespace Internal class DEBUGGER_EXPORT DebuggerStartParameters @@ -169,7 +169,7 @@ public: friend class Internal::GdbEngine; friend class Internal::ScriptEngine; friend class Internal::CdbDebugEngine; - friend struct Internal::CdbDebugEnginePrivate; + friend class Internal::CdbDebugEnginePrivate; DebuggerState state() const; QList<Core::IOptionsPage*> initializeEngines(unsigned enabledTypeFlags); diff --git a/tests/manual/ccdb/README b/tests/manual/ccdb/README new file mode 100644 index 0000000000000000000000000000000000000000..56e3426f2f4241dd4cf39b4424ccd67203eb7e0e --- /dev/null +++ b/tests/manual/ccdb/README @@ -0,0 +1,2 @@ +This directory contains a command line tool to the Debugging Tools for +Windows for testing/scripting purposes. diff --git a/tests/manual/ccdb/ccdb.pro b/tests/manual/ccdb/ccdb.pro new file mode 100644 index 0000000000000000000000000000000000000000..6021878be3e2b5ef48b931166e79332a8f3b5eb8 --- /dev/null +++ b/tests/manual/ccdb/ccdb.pro @@ -0,0 +1,29 @@ +# ------------------------------------------------- +# Project created by QtCreator 2010-01-22T10:11:10 +# ------------------------------------------------- +QT += core +TARGET = ccdb +CONFIG += console +CONFIG -= app_bundle +TEMPLATE = app + +# -- Add CDB core engine +CDB_CORE = ../../../src/plugins/debugger/cdb +include($$CDB_CORE/cdbcore.pri) +INCLUDEPATH *= $$CDB_CORE + +# -- Add creator 'utils' lib +CREATOR_LIB_LIB = ../../../lib/qtcreator +LIBS *= -L$$CREATOR_LIB_LIB +LIBS *= -l$$qtLibraryTarget(Utilsd) +CREATOR_LIB_SRC = ../../../src/libs +INCLUDEPATH *= $$CREATOR_LIB_SRC + +# -- App sources +SOURCES += main.cpp \ + cdbapplication.cpp \ + debugeventcallback.cpp \ + cdbpromptthread.cpp +HEADERS += cdbapplication.h \ + debugeventcallback.h \ + cdbpromptthread.h diff --git a/tests/manual/ccdb/cdbapplication.cpp b/tests/manual/ccdb/cdbapplication.cpp new file mode 100644 index 0000000000000000000000000000000000000000..426cd0172adce174e17e0973f57111d223baa092 --- /dev/null +++ b/tests/manual/ccdb/cdbapplication.cpp @@ -0,0 +1,213 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (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 http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "cdbapplication.h" +#include "coreengine.h" +#include "cdbdebugoutput.h" +#include "cdbpromptthread.h" +#include "debugeventcallback.h" + +#include <QtCore/QStringList> +#include <QtCore/QTimer> + +#include <cstdio> + +const char usage[] = +"CDB command line test tool\n\n" +"ccdb <Options>\n" +"Options: -p engine path\n"; + +class PrintfOutputHandler : public CdbCore::DebugOutputBase +{ +public: + PrintfOutputHandler() {} + +protected: + virtual void output(ULONG mask, const QString &message) + { std::printf("%10s: %s\n", maskDescription(mask), qPrintable(message)); } +}; + + +// -------------- CdbApplication +CdbApplication::CdbApplication(int argc, char *argv[]) : + QCoreApplication(argc, argv), + m_engine(new CdbCore::CoreEngine), + m_promptThread(0), + m_processHandle(0) +{ +} + +CdbApplication::~CdbApplication() +{ +} + +CdbApplication::InitResult CdbApplication::init() +{ + if (!parseOptions()) { + printf(usage); + return InitUsageShown; + } + QString errorMessage; + std::printf("Initializing engine %s...\n", qPrintable(m_engineDll)); + if (!m_engine->init(m_engineDll, &errorMessage)) { + std::fprintf(stderr, "Failed: %s\n", qPrintable(errorMessage)); + return InitFailed; + } + m_engine->setDebugOutput(CdbCore::CoreEngine::DebugOutputBasePtr(new PrintfOutputHandler)); + DebugEventCallback *evt = new DebugEventCallback; + connect(evt, SIGNAL(processAttached(void*)), this, SLOT(processAttached(void*))); + m_engine->setDebugEventCallback(CdbCore::CoreEngine::DebugEventCallbackBasePtr(evt)); + m_engine->setExpressionSyntax(CdbCore::CoreEngine::CppExpressionSyntax); + m_engine->setCodeLevel(CdbCore::CoreEngine::CodeLevelSource); + connect(m_engine.data(), SIGNAL(watchTimerDebugEvent()), this, SLOT(debugEvent())); + std::printf("Succeded.\n"); + // Prompt + m_promptThread = new CdbPromptThread(this); + connect(m_promptThread, SIGNAL(finished()), this, SLOT(promptThreadTerminated())); + connect(m_promptThread, SIGNAL(asyncCommand(int,QString)), + this, SLOT(asyncCommand(int,QString)), Qt::QueuedConnection); + connect(m_promptThread, SIGNAL(syncCommand(int,QString)), + this, SLOT(syncCommand(int,QString)), Qt::BlockingQueuedConnection); + connect(m_promptThread, SIGNAL(executionCommand(int,QString)), + this, SLOT(executionCommand(int,QString)), Qt::BlockingQueuedConnection); + + m_promptThread->start(); + return InitOk; +} + +void CdbApplication::promptThreadTerminated() +{ + QString errorMessage; + m_engine->endSession(&errorMessage); + std::printf("Terminating.\n"); + m_promptThread->wait(); + quit(); +} + +bool CdbApplication::parseOptions() +{ + const QStringList args = QCoreApplication::arguments(); + const QStringList::const_iterator cend = args.constEnd(); + QStringList::const_iterator it = args.constBegin(); + for (++it; it != cend ; ++it) { + const QString &a = *it; + if (a == QLatin1String("-p")) { + ++it; + if (it == cend) { + std::fprintf(stderr, "Option -p is missing an argument.\n"); + return false; + } + m_engineDll = *it; + } else { + std::fprintf(stderr, "Invalid option %s\n", qPrintable(a)); + return false; + } + } + return true; +} + +void CdbApplication::asyncCommand(int command, const QString &arg) +{ + Q_UNUSED(arg) + QString errorMessage; + switch (command) { + case Async_Interrupt: + if (m_processHandle) { + if (m_engine->debugBreakProcess(m_processHandle, &errorMessage)) { + std::printf("Stopped\n"); + } else { + std::printf("%s\n", qPrintable(errorMessage)); + } + } + break; + } +} + +void CdbApplication::syncCommand(int command, const QString &arg) +{ + QString errorMessage; + switch (command) { + case Sync_EvalExpression: { + QString value; + QString type; + if (m_engine->evaluateExpression(arg, &value, &type, &errorMessage)) { + std::printf("[%s] %s\n", qPrintable(type), qPrintable(value)); + } else { + std::printf("%s\n", qPrintable(errorMessage)); + } + } + break; + case Unknown: + if (!m_engine->executeDebuggerCommand(arg, &errorMessage)) + std::printf("%s\n", qPrintable(errorMessage)); + break; + } +} + +void CdbApplication::executionCommand(int command, const QString &arg) +{ + bool ok = false; + QString errorMessage; + switch (command) { + case Execution_StartBinary: { + QStringList args = arg.split(QLatin1Char(' '), QString::SkipEmptyParts); + if (args.isEmpty()) { + errorMessage = QLatin1String("Specify executable."); + } else { + std::printf("Starting\n"); + const QString binary = args.front(); + args.pop_front(); + ok = m_engine->startDebuggerWithExecutable(QString(), binary, args, + QStringList(), false, + &errorMessage); + } + } + break; + case Execution_Go: + std::printf("Go\n"); + ok = m_engine->setExecutionStatus(DEBUG_STATUS_GO, &errorMessage); + break; + } + if (ok) { + m_engine->startWatchTimer(); + } else { + std::fprintf(stderr, "%s\n", qPrintable(errorMessage)); + } +} + +void CdbApplication::debugEvent() +{ + std::printf("Debug event\n"); +} + +void CdbApplication::processAttached(void *handle) +{ + std::printf("pe\n"); + m_processHandle = handle; +} diff --git a/tests/manual/ccdb/cdbapplication.h b/tests/manual/ccdb/cdbapplication.h new file mode 100644 index 0000000000000000000000000000000000000000..5ee35b24c253bb9c8808a8ac4091ed447a8c879d --- /dev/null +++ b/tests/manual/ccdb/cdbapplication.h @@ -0,0 +1,71 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (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 http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef CDBAPPLICATION_H +#define CDBAPPLICATION_H + +#include <QtCore/QCoreApplication> +#include <QtCore/QSharedPointer> + +namespace CdbCore { + class CoreEngine; +} + +class CdbPromptThread; + +class CdbApplication : public QCoreApplication +{ + Q_OBJECT + Q_DISABLE_COPY(CdbApplication) +public: + enum InitResult { InitFailed, InitUsageShown, InitOk }; + + CdbApplication(int argc, char *argv[]); + ~CdbApplication(); + + InitResult init(); + +private slots: + void promptThreadTerminated(); + void asyncCommand(int command, const QString &arg); + void syncCommand(int command, const QString &arg); + void executionCommand(int command, const QString &arg); + void debugEvent(); + void processAttached(void *handle); + +private: + bool parseOptions(); + + QString m_engineDll; + QSharedPointer<CdbCore::CoreEngine> m_engine; + CdbPromptThread *m_promptThread; + void *m_processHandle; +}; + +#endif // CDBAPPLICATION_H diff --git a/tests/manual/ccdb/cdbpromptthread.cpp b/tests/manual/ccdb/cdbpromptthread.cpp new file mode 100644 index 0000000000000000000000000000000000000000..66a13b28942f48f092ece474508838f939b7b259 --- /dev/null +++ b/tests/manual/ccdb/cdbpromptthread.cpp @@ -0,0 +1,135 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (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 http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "cdbpromptthread.h" + +#include <cstdio> +#include <QtCore/QDebug> + +static const char help[] = +"Special commands:\n\n" +"H Display Help\n" +"q Quit\n" +"E expression Evaluate C++expression\n" +"S binary args Start binary\n" +"I Interrupt\n" +"G Go\n" +"\nThe remaining commands are passed to CDB.\n"; + +CdbPromptThread::CdbPromptThread(QObject *parent) : + QThread(parent) +{ +} + +void CdbPromptThread::run() +{ + enum { bufSize =1024 }; + + QString cmd; + char buf[bufSize]; + std::putc('>', stdout); + while (true) { + if (std::fgets(buf, bufSize, stdin) == NULL) + break; + cmd += QString::fromLatin1(buf); + if (cmd.endsWith(QLatin1Char('\n'))) { + cmd.truncate(cmd.size() - 1); + if (!cmd.isEmpty() && !handleCommand(cmd.trimmed())) + break; + cmd.clear(); + } + std::putc('>', stdout); + } +} + +// Determine the command +static Command evaluateCommand(const QString &cmdToken) +{ + if (cmdToken.size() == 1) { + switch(cmdToken.at(0).toAscii()) { + case 'I': + return Async_Interrupt; + case 'E': + return Sync_EvalExpression; + case 'G': + return Execution_Go; + case 'S': + return Execution_StartBinary; + default: + break; + } + return UnknownCommand; + } + return UnknownCommand; +} + +// Chop off command and return argument list +static Command parseCommand(QString *s) +{ + if (s->isEmpty()) + return UnknownCommand; + int firstBlank = s->indexOf(QLatin1Char(' ')); + // No further arguments + if (firstBlank == -1) { + const Command rc1 = evaluateCommand(*s); + if (rc1 != UnknownCommand) // pass through debugger cmds + s->clear(); + return rc1; + } + // Chop + const Command rc = evaluateCommand(s->left(firstBlank)); + if (rc != UnknownCommand) { // pass through debugger cmds) + int nextToken = firstBlank + 1; + for ( ; nextToken < s->size() && s->at(nextToken).isSpace(); nextToken++) ; + s->remove(0, nextToken); + } + return rc; +} + +bool CdbPromptThread::handleCommand(QString cmd) +{ + if (cmd == QLatin1String("q")) + return false; + if (cmd == QLatin1String("H")) { + std::fputs(help, stdout); + return true; + } + const Command c = parseCommand(&cmd); + if (c & AsyncCommand) { + emit asyncCommand(c, cmd); + return true; + } + if (c & ExecutionCommand) { + emit executionCommand(c, cmd); + return true; + } + // Let Unknown default to sync exeute + emit syncCommand(c, cmd); + return true; +} diff --git a/tests/manual/ccdb/cdbpromptthread.h b/tests/manual/ccdb/cdbpromptthread.h new file mode 100644 index 0000000000000000000000000000000000000000..09df8452a597f334bfaed1b20bb7995858ce7854 --- /dev/null +++ b/tests/manual/ccdb/cdbpromptthread.h @@ -0,0 +1,71 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (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 http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef PROMPTTHREAD_H +#define PROMPTTHREAD_H + +#include <QtCore/QThread> + +enum CommandTypeFlags { + // Interrupt or something. + AsyncCommand = 0x0010000, + // Synchronous execution before next prompt, + // eg eval expression. Connect with blocking slot. + SyncCommand = 0x0020000, + // Starts debuggee. Requires starting the debug event + // watch timer afterwards. + ExecutionCommand = 0x0040000 +}; + +enum Command { + UnknownCommand = 0, + Async_Interrupt = AsyncCommand|1, + Sync_EvalExpression = SyncCommand|1, + Execution_Go = ExecutionCommand|1, + Execution_StartBinary = ExecutionCommand|2 +}; + +class CdbPromptThread : public QThread +{ + Q_OBJECT +public: + explicit CdbPromptThread(QObject *parent = 0); + + virtual void run(); + +signals: + void asyncCommand(int command, const QString &arg); + void syncCommand(int command, const QString &arg); + void executionCommand(int command, const QString &arg); + +private: + bool handleCommand(QString); +}; + +#endif // PROMPTTHREAD_H diff --git a/tests/manual/ccdb/debugeventcallback.cpp b/tests/manual/ccdb/debugeventcallback.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1caaf2f8069cf5b3b67ba8926b128633cd4ec0cb --- /dev/null +++ b/tests/manual/ccdb/debugeventcallback.cpp @@ -0,0 +1,156 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (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 http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "debugeventcallback.h" + +#include <cstdio> + +DebugEventCallback::DebugEventCallback() +{ +} + +STDMETHODIMP DebugEventCallback::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; + return S_OK; +} + +STDMETHODIMP DebugEventCallback::Breakpoint(THIS_ __in PDEBUG_BREAKPOINT2) +{ + printf("Breakpoint hit\n"); + return S_OK; +} +STDMETHODIMP DebugEventCallback::Exception( + THIS_ + __in PEXCEPTION_RECORD64 exc, + __in ULONG firstChance + ) +{ + printf("Exception %ul occurred first-chance: %ul\n", exc->ExceptionCode, firstChance); + return S_OK; +} + +STDMETHODIMP DebugEventCallback::CreateThread( + THIS_ + __in ULONG64 /* Handle */, + __in ULONG64 /* DataOffset */, + __in ULONG64 /* StartOffset */ + ) +{ + printf("Thread created\n"); + return S_OK; +} + +STDMETHODIMP DebugEventCallback::ExitThread( + THIS_ + __in ULONG /* ExitCode */ + ) +{ + printf("Thread quit\n"); + return S_OK; +} + +STDMETHODIMP DebugEventCallback::CreateProcess( + THIS_ + __in ULONG64 /* ImageFileHandle */, + __in ULONG64 Handle, + __in ULONG64 /* Offset */, + __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 */ + ) +{ + printf("Process created %Ld\n", Handle); + emit processAttached(reinterpret_cast<void*>(Handle)); + return S_OK; +} + +STDMETHODIMP DebugEventCallback::ExitProcess( + THIS_ + __in ULONG /* ExitCode */ + ) +{ + printf("Process quit\n"); + return S_OK; +} + +STDMETHODIMP DebugEventCallback::LoadModule( + THIS_ + __in ULONG64 /* ImageFileHandle */, + __in ULONG64 /* Offset */, + __in ULONG /* ModuleSize */, + __in_opt PCWSTR /* ModuleName */, + __in_opt PCWSTR /* ImageName */, + __in ULONG /* CheckSum */, + __in ULONG /* TimeDateStamp */ + ) +{ + printf("Module loaded\n"); + return S_OK; +} + +STDMETHODIMP DebugEventCallback::UnloadModule( + THIS_ + __in_opt PCWSTR /* ImageName */, + __in ULONG64 /* Offset */ + ) +{ + printf("Module unloaded\n"); + return S_OK; +} + +STDMETHODIMP DebugEventCallback::SystemError( + THIS_ + __in ULONG Error, + __in ULONG Level + ) +{ + printf("System error %ul at %ul\n", Error, Level); + return S_OK; +} + + +STDMETHODIMP DebugEventCallback::ChangeDebuggeeState( + THIS_ + __in ULONG Flags, + __in ULONG64 Argument + ) +{ + printf("Debuggee state changed %ul %ul\n", Flags, Argument); + return S_OK; +} diff --git a/tests/manual/ccdb/debugeventcallback.h b/tests/manual/ccdb/debugeventcallback.h new file mode 100644 index 0000000000000000000000000000000000000000..357224eadcc0cfea7b729035e98a198abd7a5266 --- /dev/null +++ b/tests/manual/ccdb/debugeventcallback.h @@ -0,0 +1,125 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (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 http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef DEBUGEVENTCALLBACK_H +#define DEBUGEVENTCALLBACK_H + +#include "debugeventcallbackbase.h" +#include <QtCore/QObject> + +class DebugEventCallback : + public QObject, + public CdbCore::DebugEventCallbackBase + +{ + Q_OBJECT +public: + DebugEventCallback(); + + 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 + ); + + STDMETHOD(ChangeDebuggeeState)( + THIS_ + __in ULONG Flags, + __in ULONG64 Argument + ); + +signals: + void processAttached(void *h); +}; + +#endif // DEBUGEVENTCALLBACK_H diff --git a/tests/manual/ccdb/main.cpp b/tests/manual/ccdb/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a9ccb6d93ca1ee2313c690971eea13b3f43c43d0 --- /dev/null +++ b/tests/manual/ccdb/main.cpp @@ -0,0 +1,49 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (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 http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "cdbapplication.h" +#include <QtCore/QString> + +int main(int argc, char *argv[]) +{ + CdbApplication app(argc, argv); + int rc = 0; + + switch (app.init()) { + case CdbApplication::InitOk: + rc = app.exec(); + break; + case CdbApplication::InitFailed: + rc = 1; + break; + case CdbApplication::InitUsageShown: + break; + } + return rc; +}