From 95d8b844044ec4893ad9478a533c81545eb6cf4c Mon Sep 17 00:00:00 2001 From: Banana Joe <qtc-committer@nokia.com> Date: Mon, 9 Feb 2009 11:35:43 +0100 Subject: [PATCH] experimental dbgengine debugger --- src/plugins/debugger/debugger.pro | 11 + src/plugins/debugger/msvcdebugengine.cpp | 519 ++++++++++++++++++ src/plugins/debugger/msvcdebugengine.h | 98 ++++ .../debugger/msvcdebugeventcallback.cpp | 217 ++++++++ src/plugins/debugger/msvcdebugeventcallback.h | 131 +++++ src/plugins/debugger/msvcdebugoutput.cpp | 65 +++ src/plugins/debugger/msvcdebugoutput.h | 43 ++ 7 files changed, 1084 insertions(+) create mode 100644 src/plugins/debugger/msvcdebugengine.cpp create mode 100644 src/plugins/debugger/msvcdebugengine.h create mode 100644 src/plugins/debugger/msvcdebugeventcallback.cpp create mode 100644 src/plugins/debugger/msvcdebugeventcallback.h create mode 100644 src/plugins/debugger/msvcdebugoutput.cpp create mode 100644 src/plugins/debugger/msvcdebugoutput.h diff --git a/src/plugins/debugger/debugger.pro b/src/plugins/debugger/debugger.pro index 469ea346011..b2498747161 100644 --- a/src/plugins/debugger/debugger.pro +++ b/src/plugins/debugger/debugger.pro @@ -83,3 +83,14 @@ SOURCES += $$PWD/modeltest.cpp HEADERS += $$PWD/modeltest.h DEFINES += USE_MODEL_TEST=1 } + +false { + HEADERS += msvcdebugengine.h \ + msvcdebugeventcallback.h \ + msvcdebugoutput.h + SOURCES += msvcdebugengine.cpp \ + msvcdebugeventcallback.cpp \ + msvcdebugoutput.cpp + LIBS += dbgeng.lib +} + diff --git a/src/plugins/debugger/msvcdebugengine.cpp b/src/plugins/debugger/msvcdebugengine.cpp new file mode 100644 index 00000000000..e2216644405 --- /dev/null +++ b/src/plugins/debugger/msvcdebugengine.cpp @@ -0,0 +1,519 @@ +#include "msvcdebugengine.h" + +#include "assert.h" +#include "debuggermanager.h" +#include "breakhandler.h" +#include "stackhandler.h" + +#include <QDebug> +#include <QTimerEvent> +#include <QFileInfo> + +#define DBGHELP_TRANSLATE_TCHAR +#include <Dbghelp.h> + +using namespace Debugger; +using namespace Debugger::Internal; + + +MSVCDebugEngine::MSVCDebugEngine(DebuggerManager *parent) +: IDebuggerEngine(parent), + m_hDebuggeeProcess(0), + m_hDebuggeeThread(0), + //m_hDebuggeeImage(0), + m_bIgnoreNextDebugEvent(false), + m_watchTimer(-1), + m_debugEventCallBack(this), + m_debugOutputCallBack(this) +{ + q = parent; + qq = parent->engineInterface(); + + HRESULT hr; + hr = DebugCreate( __uuidof(IDebugClient5), reinterpret_cast<void**>(&m_pDebugClient)); + if (FAILED(hr)) m_pDebugClient = 0; + hr = DebugCreate( __uuidof(IDebugControl4), reinterpret_cast<void**>(&m_pDebugControl)); + if (FAILED(hr)) m_pDebugControl = 0; + hr = DebugCreate( __uuidof(IDebugSystemObjects4), reinterpret_cast<void**>(&m_pDebugSystemObjects)); + if (FAILED(hr)) m_pDebugSystemObjects = 0; + hr = DebugCreate( __uuidof(IDebugSymbols3), reinterpret_cast<void**>(&m_pDebugSymbols)); + if (FAILED(hr)) m_pDebugSymbols = 0; + hr = DebugCreate( __uuidof(IDebugRegisters2), reinterpret_cast<void**>(&m_pDebugRegisters)); + if (FAILED(hr)) m_pDebugRegisters = 0; + + if (m_pDebugControl) { + m_pDebugControl->SetCodeLevel(DEBUG_LEVEL_SOURCE); + } + if (m_pDebugClient) { + m_pDebugClient->SetOutputCallbacks(&m_debugOutputCallBack); + m_pDebugClient->SetEventCallbacks(&m_debugEventCallBack); + } +} + +MSVCDebugEngine::~MSVCDebugEngine() +{ + if (m_pDebugClient) + m_pDebugClient->Release(); + if (m_pDebugControl) + m_pDebugControl->Release(); + if (m_pDebugSystemObjects) + m_pDebugSystemObjects->Release(); + if (m_pDebugSymbols) + m_pDebugSymbols->Release(); + if (m_pDebugRegisters) + m_pDebugRegisters->Release(); +} + +void MSVCDebugEngine::startWatchTimer() +{ + if (m_watchTimer == -1) + m_watchTimer = startTimer(0); +} + +void MSVCDebugEngine::killWatchTimer() +{ + if (m_watchTimer != -1) { + killTimer(m_watchTimer); + m_watchTimer = -1; + } +} + +void MSVCDebugEngine::shutdown() +{ + exitDebugger(); +} + +void MSVCDebugEngine::setToolTipExpression(const QPoint &pos, const QString &exp) +{ +} + +bool MSVCDebugEngine::startDebugger() +{ + q->showStatusMessage("Starting Debugger", -1); + + //if (!q->m_workingDir.isEmpty()) + // m_gdbProc.setWorkingDirectory(q->m_workingDir); + //if (!q->m_environment.isEmpty()) + // m_gdbProc.setEnvironment(q->m_environment); + + DEBUG_CREATE_PROCESS_OPTIONS dbgopts; + memset(&dbgopts, 0, sizeof(dbgopts)); + dbgopts.CreateFlags = DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS; + + HRESULT hr; + + QString filename(q->m_executable); + QFileInfo fi(filename); + m_pDebugSymbols->AppendImagePathWide(fi.absolutePath().replace('/','\\').utf16()); + //m_pDebugSymbols->SetSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_DEBUG | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS); + m_pDebugSymbols->SetSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS); + //m_pDebugSymbols->AddSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_DEBUG | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS | SYMOPT_NO_IMAGE_SEARCH); + + if (q->startMode() == q->attachExternal) { + qWarning("MSVCDebugEngine: attach to process not yet implemented!"); + } else { + hr = m_pDebugClient->CreateProcess2Wide(NULL, + const_cast<PWSTR>(filename.utf16()), + &dbgopts, + sizeof(dbgopts), + NULL, // TODO: think about the initial directory + NULL); // TODO: think about setting the environment + if (FAILED(hr)) { + //qWarning("CreateProcess2Wide failed"); + qq->notifyInferiorExited(); + return false; + } + } + + q->showStatusMessage(tr("Debugger Running"), -1); + startWatchTimer(); + return true; +} + +void MSVCDebugEngine::exitDebugger() +{ + m_pDebugClient->TerminateCurrentProcess(); + killWatchTimer(); +} + +void MSVCDebugEngine::updateWatchModel() +{ +} + +void MSVCDebugEngine::stepExec() +{ + //qDebug() << "MSVCDebugEngine::stepExec()"; + //m_pDebugControl->Execute(DEBUG_OUTCTL_THIS_CLIENT, "p", 0); + HRESULT hr; + hr = m_pDebugControl->SetExecutionStatus(DEBUG_STATUS_STEP_INTO); + m_bIgnoreNextDebugEvent = true; + startWatchTimer(); +} + +void MSVCDebugEngine::stepOutExec() +{ + //qDebug() << "MSVCDebugEngine::stepOutExec()"; + StackHandler* sh = qq->stackHandler(); + const int idx = sh->currentIndex() + 1; + QList<StackFrame> stackframes = sh->frames(); + if (idx < 0 || idx >= stackframes.size()) { + qWarning("cannot step out"); + return; + } + + const StackFrame& frame = stackframes.at(idx); + bool ok; + ULONG64 address = frame.address.toULongLong(&ok, 16); + if (!ok) { + qWarning("stepOutExec: cannot obtain address from stack frame"); + return; + } + + IDebugBreakpoint2* pBP; + HRESULT hr = m_pDebugControl->AddBreakpoint2(DEBUG_BREAKPOINT_CODE, DEBUG_ANY_ID, &pBP); + if (FAILED(hr) || !pBP) { + qWarning("stepOutExec: cannot create temporary breakpoint"); + return; + } + + pBP->SetOffset(address); + + //QString str = '`' + frame.file + ':' + frame.line + '`'; + //hr = pBP->SetOffsetExpressionWide(str.utf16()); + //if (FAILED(hr)) { + // qWarning("SetOffsetExpressionWide failed"); + // return; + //} + + pBP->AddFlags(DEBUG_BREAKPOINT_ENABLED); + pBP->AddFlags(DEBUG_BREAKPOINT_ONE_SHOT); + //hr = m_pDebugControl->SetExecutionStatus(DEBUG_STATUS_GO); + continueInferior(); +} + +void MSVCDebugEngine::nextExec() +{ + //qDebug() << "MSVCDebugEngine::nextExec()"; + HRESULT hr; + hr = m_pDebugControl->SetExecutionStatus(DEBUG_STATUS_STEP_OVER); + startWatchTimer(); +} + +void MSVCDebugEngine::stepIExec() +{ + qWarning("MSVCDebugEngine::stepIExec() not implemented"); +} + +void MSVCDebugEngine::nextIExec() +{ + m_pDebugControl->Execute(DEBUG_OUTCTL_THIS_CLIENT, "p", 0); + startWatchTimer(); +} + +void MSVCDebugEngine::continueInferior() +{ + killWatchTimer(); + q->resetLocation(); + + ULONG executionStatus; + HRESULT hr = m_pDebugControl->GetExecutionStatus(&executionStatus); + if (SUCCEEDED(hr) && executionStatus != DEBUG_STATUS_GO) + m_pDebugControl->SetExecutionStatus(DEBUG_STATUS_GO); + + startWatchTimer(); + qq->notifyInferiorRunning(); +} + +void MSVCDebugEngine::runInferior() +{ + continueInferior(); +} + +void MSVCDebugEngine::interruptInferior() +{ + //TODO: better use IDebugControl::SetInterrupt? + if (!m_hDebuggeeProcess) + return; + if (!DebugBreakProcess(m_hDebuggeeProcess)) { + qWarning("DebugBreakProcess failed."); + return; + } + qq->notifyInferiorStopped(); +} + +void MSVCDebugEngine::runToLineExec(const QString &fileName, int lineNumber) +{ +} + +void MSVCDebugEngine::runToFunctionExec(const QString &functionName) +{ +} + +void MSVCDebugEngine::jumpToLineExec(const QString &fileName, int lineNumber) +{ +} + +void MSVCDebugEngine::assignValueInDebugger(const QString &expr, const QString &value) +{ +} + +void MSVCDebugEngine::executeDebuggerCommand(const QString &/*command*/) +{ +} + +void MSVCDebugEngine::activateFrame(int frameIndex) +{ + if (q->status() != DebuggerInferiorStopped) + return; + + StackHandler *stackHandler = qq->stackHandler(); + int oldIndex = stackHandler->currentIndex(); + //qDebug() << "ACTIVATE FRAME: " << frameIndex << oldIndex + // << stackHandler->currentIndex(); + + QWB_ASSERT(frameIndex < stackHandler->stackSize(), return); + + if (oldIndex != frameIndex) { + stackHandler->setCurrentIndex(frameIndex); + //updateLocals(); + } + + const StackFrame &frame = stackHandler->currentFrame(); + + bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable(); + if (usable) + q->gotoLocation(frame.file, frame.line, true); + else + qDebug() << "FULL NAME NOT USABLE: " << frame.file; +} + +void MSVCDebugEngine::selectThread(int index) +{ + //reset location arrow + q->resetLocation(); + + ThreadsHandler *threadsHandler = qq->threadsHandler(); + threadsHandler->setCurrentThread(index); + m_currentThreadId = index; + updateStackTrace(); +} + +void MSVCDebugEngine::attemptBreakpointSynchronization() +{ + BreakHandler *handler = qq->breakHandler(); + //qDebug() << "attemptBreakpointSynchronization"; + for (int i=0; i < handler->size(); ++i) { + BreakpointData* breakpoint = handler->at(i); + if (breakpoint->pending) { + IDebugBreakpoint2* pBP = 0; + HRESULT hr = m_pDebugControl->AddBreakpoint2(DEBUG_BREAKPOINT_CODE, DEBUG_ANY_ID, &pBP); + if (FAILED(hr) || !pBP) { + qWarning("m_pDebugControl->AddBreakpoint2 failed"); + continue; + } + + QString str = '`' + breakpoint->fileName + ':' + breakpoint->lineNumber + '`'; + hr = pBP->SetOffsetExpressionWide(str.utf16()); + if (FAILED(hr)) { + qWarning("SetOffsetExpressionWide failed"); + continue; + } + + bool ok; + ULONG ul; + ul = breakpoint->ignoreCount.toULong(&ok); + if (ok) pBP->SetPassCount(ul); + + //TODO: handle breakpoint->condition + + pBP->AddFlags(DEBUG_BREAKPOINT_ENABLED); + //pBP->AddFlags(DEBUG_BREAKPOINT_GO_ONLY); + breakpoint->pending = false; + } + } +} + +void MSVCDebugEngine::loadSessionData() +{ +} + +void MSVCDebugEngine::saveSessionData() +{ +} + +void MSVCDebugEngine::reloadDisassembler() +{ +} + +void MSVCDebugEngine::reloadModules() +{ +} + +void MSVCDebugEngine::loadSymbols(const QString &moduleName) +{ +} + +void MSVCDebugEngine::loadAllSymbols() +{ +} + +void MSVCDebugEngine::reloadRegisters() +{ +} + +void MSVCDebugEngine::timerEvent(QTimerEvent* te) +{ + if (te->timerId() != m_watchTimer) + return; + + HRESULT hr; + hr = m_pDebugControl->WaitForEvent(0, 1); + switch (hr) { + case S_OK: + //qDebug() << "WaitForEvent S_OK"; + killWatchTimer(); + handleDebugEvent(); + break; + case S_FALSE: + //qDebug() << "S_FALSE"; + break; + case E_PENDING: + qDebug() << "S_PENDING"; + break; + case E_UNEXPECTED: + killWatchTimer(); + break; + case E_FAIL: + qDebug() << "E_FAIL"; + break; + //default: + // qDebug() << "asser welljuh, schuddnt heppn"; + } +} + +void MSVCDebugEngine::handleDebugEvent() +{ + if (m_bIgnoreNextDebugEvent) { + startWatchTimer(); + m_bIgnoreNextDebugEvent = false; + } else { + qq->notifyInferiorStopped(); + updateThreadList(); + updateStackTrace(); + } + + //ULONG executionStatus; + //HRESULT hr = m_pDebugControl->GetExecutionStatus(&executionStatus); + //if (SUCCEEDED(hr) && executionStatus == DEBUG_STATUS_STEP_INTO) { + // // check if stack trace is valid + // StackHandler* sh = qq->stackHandler(); + // QList<StackFrame> frames = sh->frames(); + // if (frames.size() > 0 && frames.first().file.isEmpty()) { + // stepExec(); + // } + //} +} + +void MSVCDebugEngine::updateThreadList() +{ + ThreadsHandler* th = qq->threadsHandler(); + QList<ThreadData> threads; + + HRESULT hr; + ULONG numberOfThreads; + hr = m_pDebugSystemObjects->GetNumberThreads(&numberOfThreads); + const ULONG maxThreadIds = 256; + ULONG threadIds[maxThreadIds]; + ULONG biggestThreadId = qMin(maxThreadIds, numberOfThreads - 1); + hr = m_pDebugSystemObjects->GetThreadIdsByIndex(0, biggestThreadId, threadIds, 0); + for (ULONG threadId = 0; threadId <= biggestThreadId; ++threadId) { + ThreadData thread; + thread.id = threadId; + threads.append(thread); + } + + th->setThreads(threads); +} + +void MSVCDebugEngine::updateStackTrace() +{ + //qDebug() << "updateStackTrace()"; + HRESULT hr; + hr = m_pDebugSystemObjects->SetCurrentThreadId(m_currentThreadId); + + //ULONG64 frameOffset, instructionOffset, stackOffset; + //if (FAILED(m_pDebugRegisters->GetFrameOffset2(DEBUG_REGSRC_DEBUGGEE, &frameOffset)) || + // FAILED(m_pDebugRegisters->GetInstructionOffset2(DEBUG_REGSRC_DEBUGGEE, &instructionOffset)) || + // FAILED(m_pDebugRegisters->GetStackOffset2(DEBUG_REGSRC_DEBUGGEE, &stackOffset))) + //{ + // frameOffset = instructionOffset = stackOffset = 0; + //} + //frameOffset = instructionOffset = stackOffset = 0; + + const ULONG numFrames = 100; + ULONG numFramesFilled = 0; + DEBUG_STACK_FRAME frames[numFrames]; + //hr = m_pDebugControl->GetStackTrace(frameOffset, stackOffset, instructionOffset, frames, numFrames, &numFramesFilled); + hr = m_pDebugControl->GetStackTrace(0, 0, 0, frames, numFrames, &numFramesFilled); + if (FAILED(hr)) + qDebug() << "GetStackTrace failed"; + + QList<StackFrame> stackFrames; + + WCHAR wszBuf[MAX_PATH]; + for (ULONG i=0; i < numFramesFilled; ++i) { + StackFrame frame; + frame.line = 0; + frame.level = i; + frame.address = QString("0x%1").arg(frames[i].InstructionOffset, 0, 16); + + m_pDebugSymbols->GetNameByOffsetWide(frames[i].InstructionOffset, wszBuf, MAX_PATH, 0, 0); + frame.function = QString::fromUtf16(wszBuf); + + ULONG ulLine; + ULONG ulFileNameSize; + ULONG64 ul64Displacement; + hr = m_pDebugSymbols->GetLineByOffsetWide(frames[i].InstructionOffset, &ulLine, wszBuf, MAX_PATH, &ulFileNameSize, &ul64Displacement); + if (SUCCEEDED(hr)) { + frame.line = ulLine; + frame.file = QString::fromUtf16(wszBuf, ulFileNameSize); + } + stackFrames.append(frame); + } + + qq->stackHandler()->setFrames(stackFrames); + + // find the first usable frame and select it + for (int i=0; i < stackFrames.count(); ++i) { + const StackFrame &frame = stackFrames.at(i); + bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable(); + if (usable) { + qq->stackHandler()->setCurrentIndex(i); + q->gotoLocation(frame.file, frame.line, true); + break; + } + } + + //m_pDebugSymbols->GetImagePathWide(wszBuf, buflen, 0); + //qDebug() << "ImagePath" << QString::fromUtf16(wszBuf); + //m_pDebugSymbols->GetSymbolPathWide(wszBuf, buflen, 0); + //qDebug() << "SymbolPath" << QString::fromUtf16(wszBuf); + + //m_pDebugControl->OutputStackTrace(DEBUG_OUTCTL_THIS_CLIENT, 0, 2, DEBUG_STACK_FRAME_ADDRESSES | DEBUG_STACK_COLUMN_NAMES | DEBUG_STACK_FRAME_NUMBERS); + //m_pDebugControl->OutputStackTrace(DEBUG_OUTCTL_THIS_CLIENT, frames, numFramesFilled, DEBUG_STACK_SOURCE_LINE); +} + +void MSVCDebugEngine::handleDebugOutput(const char* szOutputString) +{ + qq->showApplicationOutput("app-dbgoutput", QString::fromLocal8Bit(szOutputString)); +} + +void MSVCDebugEngine::handleBreakpointEvent(PDEBUG_BREAKPOINT pBP) +{ + qDebug() << "MSVCDebugEngine::handleBreakpointEvent()"; +} + +IDebuggerEngine *createWinEngine(DebuggerManager *parent) +{ + return new MSVCDebugEngine(parent); +} diff --git a/src/plugins/debugger/msvcdebugengine.h b/src/plugins/debugger/msvcdebugengine.h new file mode 100644 index 00000000000..3a358e30462 --- /dev/null +++ b/src/plugins/debugger/msvcdebugengine.h @@ -0,0 +1,98 @@ +#ifndef __MSVCDEBUGENGINE_H__ +#define __MSVCDEBUGENGINE_H__ + +#include "idebuggerengine.h" +#include "msvcdebugeventcallback.h" +#include "msvcdebugoutput.h" +#include <windows.h> + +namespace Debugger { +namespace Internal { + +class DebuggerManager; +class IDebuggerManagerAccessForEngines; + +class MSVCDebugEngine : public IDebuggerEngine +{ + Q_OBJECT +public: + MSVCDebugEngine(DebuggerManager *parent); + ~MSVCDebugEngine(); + + virtual void shutdown(); + virtual void setToolTipExpression(const QPoint &pos, const QString &exp); + virtual bool startDebugger(); + virtual void exitDebugger(); + virtual void updateWatchModel(); + + virtual void stepExec(); + virtual void stepOutExec(); + virtual void nextExec(); + virtual void stepIExec(); + virtual void nextIExec(); + + virtual void continueInferior(); + virtual void runInferior(); + virtual void interruptInferior(); + + virtual void runToLineExec(const QString &fileName, int lineNumber); + virtual void runToFunctionExec(const QString &functionName); + virtual void jumpToLineExec(const QString &fileName, int lineNumber); + virtual void assignValueInDebugger(const QString &expr, const QString &value); + virtual void executeDebuggerCommand(const QString &command); + + virtual void activateFrame(int index); + virtual void selectThread(int index); + + virtual void attemptBreakpointSynchronization(); + + virtual void loadSessionData(); + virtual void saveSessionData(); + + virtual void reloadDisassembler(); + + virtual void reloadModules(); + virtual void loadSymbols(const QString &moduleName); + virtual void loadAllSymbols(); + + virtual void reloadRegisters(); + +protected: + void timerEvent(QTimerEvent*); + +private: + void startWatchTimer(); + void killWatchTimer(); + bool isDebuggeeRunning() { return m_watchTimer != -1; } + void handleDebugEvent(); + void updateThreadList(); + void updateStackTrace(); + void handleDebugOutput(const char* szOutputString); + void handleBreakpointEvent(PDEBUG_BREAKPOINT pBP); + +private: + HANDLE m_hDebuggeeProcess; + HANDLE m_hDebuggeeThread; + int m_currentThreadId; + bool m_bIgnoreNextDebugEvent; + + int m_watchTimer; + IDebugClient5* m_pDebugClient; + IDebugControl4* m_pDebugControl; + IDebugSystemObjects4* m_pDebugSystemObjects; + IDebugSymbols3* m_pDebugSymbols; + IDebugRegisters2* m_pDebugRegisters; + MSVCDebugEventCallback m_debugEventCallBack; + MSVCDebugOutput m_debugOutputCallBack; + + DebuggerManager *q; + IDebuggerManagerAccessForEngines *qq; + + friend class MSVCDebugEventCallback; + friend class MSVCDebugOutput; +}; + +} // namespace Internal +} // namespace Debugger + +#endif diff --git a/src/plugins/debugger/msvcdebugeventcallback.cpp b/src/plugins/debugger/msvcdebugeventcallback.cpp new file mode 100644 index 00000000000..9ef74e63ef7 --- /dev/null +++ b/src/plugins/debugger/msvcdebugeventcallback.cpp @@ -0,0 +1,217 @@ +#include "msvcdebugeventcallback.h" +#include "msvcdebugengine.h" +#include "debuggermanager.h" + +#include <QDebug> + +namespace Debugger { +namespace Internal { + +STDMETHODIMP +MSVCDebugEventCallback::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) +MSVCDebugEventCallback::AddRef( + THIS + ) +{ + // This class is designed to be static so + // there's no true refcount. + return 1; +} + +STDMETHODIMP_(ULONG) +MSVCDebugEventCallback::Release( + THIS + ) +{ + // This class is designed to be static so + // there's no true refcount. + return 0; +} + +STDMETHODIMP MSVCDebugEventCallback::GetInterestMask( + THIS_ + __out PULONG mask + ) +{ + *mask = DEBUG_EVENT_CREATE_PROCESS | DEBUG_EVENT_EXIT_PROCESS + //| DEBUG_EVENT_CREATE_THREAD | DEBUG_EVENT_EXIT_THREAD + | DEBUG_EVENT_BREAKPOINT + | DEBUG_EVENT_EXCEPTION + ; + return S_OK; +} + +STDMETHODIMP MSVCDebugEventCallback::Breakpoint( + THIS_ + __in PDEBUG_BREAKPOINT Bp + ) +{ + qDebug() << "MSVCDebugEventCallback::Breakpoint"; + m_pEngine->handleBreakpointEvent(Bp); + return S_OK; +} + +STDMETHODIMP MSVCDebugEventCallback::Exception( + THIS_ + __in PEXCEPTION_RECORD64 Exception, + __in ULONG FirstChance + ) +{ + qDebug() << "MSVCDebugEventCallback::Exception"; + return S_OK; +} + +STDMETHODIMP MSVCDebugEventCallback::CreateThread( + THIS_ + __in ULONG64 Handle, + __in ULONG64 DataOffset, + __in ULONG64 StartOffset + ) +{ + //Debugger::ThreadInfo ti; + //ti.handle = Handle; + //ti.dataOffset = DataOffset; + //ti.startOffset = StartOffset; + return S_OK; +} + +STDMETHODIMP MSVCDebugEventCallback::ExitThread( + THIS_ + __in ULONG ExitCode + ) +{ + return S_OK; +} + +STDMETHODIMP MSVCDebugEventCallback::CreateProcess( + THIS_ + __in ULONG64 ImageFileHandle, + __in ULONG64 Handle, + __in ULONG64 BaseOffset, + __in ULONG ModuleSize, + __in_opt PCSTR ModuleName, + __in_opt PCSTR ImageName, + __in ULONG CheckSum, + __in ULONG TimeDateStamp, + __in ULONG64 InitialThreadHandle, + __in ULONG64 ThreadDataOffset, + __in ULONG64 StartOffset + ) +{ + m_pEngine->m_hDebuggeeProcess = (HANDLE)Handle; + m_pEngine->m_hDebuggeeThread = (HANDLE)InitialThreadHandle; + m_pEngine->qq->notifyStartupFinished(); + m_pEngine->qq->notifyInferiorRunning(); + + ULONG currentThreadId; + if (SUCCEEDED(m_pEngine->m_pDebugSystemObjects->GetThreadIdByHandle(InitialThreadHandle, ¤tThreadId))) + m_pEngine->m_currentThreadId = currentThreadId; + else + m_pEngine->m_currentThreadId = 0; + + m_pEngine->attemptBreakpointSynchronization(); + return S_OK; +} + +STDMETHODIMP MSVCDebugEventCallback::ExitProcess( + THIS_ + __in ULONG ExitCode + ) +{ + UNREFERENCED_PARAMETER(ExitCode); + m_pEngine->m_hDebuggeeProcess = 0; + m_pEngine->m_hDebuggeeThread = 0; + m_pEngine->qq->notifyInferiorExited(); + return S_OK; +} + +STDMETHODIMP MSVCDebugEventCallback::LoadModule( + THIS_ + __in ULONG64 ImageFileHandle, + __in ULONG64 BaseOffset, + __in ULONG ModuleSize, + __in_opt PCSTR ModuleName, + __in_opt PCSTR ImageName, + __in ULONG CheckSum, + __in ULONG TimeDateStamp + ) +{ + return S_OK; +} + +STDMETHODIMP MSVCDebugEventCallback::UnloadModule( + THIS_ + __in_opt PCSTR ImageBaseName, + __in ULONG64 BaseOffset + ) +{ + return S_OK; +} + +STDMETHODIMP MSVCDebugEventCallback::SystemError( + THIS_ + __in ULONG Error, + __in ULONG Level + ) +{ + return S_OK; +} + +STDMETHODIMP MSVCDebugEventCallback::SessionStatus( + THIS_ + __in ULONG Status + ) +{ + return S_OK; +} + +STDMETHODIMP MSVCDebugEventCallback::ChangeDebuggeeState( + THIS_ + __in ULONG Flags, + __in ULONG64 Argument + ) +{ + return S_OK; +} + +STDMETHODIMP MSVCDebugEventCallback::ChangeEngineState( + THIS_ + __in ULONG Flags, + __in ULONG64 Argument + ) +{ + return S_OK; +} + +STDMETHODIMP MSVCDebugEventCallback::ChangeSymbolState( + THIS_ + __in ULONG Flags, + __in ULONG64 Argument + ) +{ + return S_OK; +} + +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/msvcdebugeventcallback.h b/src/plugins/debugger/msvcdebugeventcallback.h new file mode 100644 index 00000000000..fa5633ca791 --- /dev/null +++ b/src/plugins/debugger/msvcdebugeventcallback.h @@ -0,0 +1,131 @@ +#pragma once + +#include <windows.h> +#include <dbgeng.h> + +namespace Debugger { +namespace Internal { + +class MSVCDebugEngine; + +class MSVCDebugEventCallback : public IDebugEventCallbacks +{ +public: + MSVCDebugEventCallback(MSVCDebugEngine* dbg) + : m_pEngine(dbg) + {} + + // IUnknown. + STDMETHOD(QueryInterface)( + THIS_ + IN REFIID InterfaceId, + OUT PVOID* Interface + ); + STDMETHOD_(ULONG, AddRef)( + THIS + ); + STDMETHOD_(ULONG, Release)( + THIS + ); + + // IDebugEventCallbacks. + + STDMETHOD(GetInterestMask)( + THIS_ + __out PULONG mask + ); + + STDMETHOD(Breakpoint)( + THIS_ + __in PDEBUG_BREAKPOINT 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 PCSTR ModuleName, + __in_opt PCSTR 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 PCSTR ModuleName, + __in_opt PCSTR ImageName, + __in ULONG CheckSum, + __in ULONG TimeDateStamp + ); + + STDMETHOD(UnloadModule)( + THIS_ + __in_opt PCSTR 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 + ); + +private: + MSVCDebugEngine* m_pEngine; +}; + +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/msvcdebugoutput.cpp b/src/plugins/debugger/msvcdebugoutput.cpp new file mode 100644 index 00000000000..4f121fb52fd --- /dev/null +++ b/src/plugins/debugger/msvcdebugoutput.cpp @@ -0,0 +1,65 @@ +#include <windows.h> +#include <dbgeng.h> + +#include "msvcdebugoutput.h" +#include "msvcdebugengine.h" + +namespace Debugger { +namespace Internal { + +STDMETHODIMP +MSVCDebugOutput::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) +MSVCDebugOutput::AddRef( + THIS + ) +{ + // This class is designed to be static so + // there's no true refcount. + return 1; +} + +STDMETHODIMP_(ULONG) +MSVCDebugOutput::Release( + THIS + ) +{ + // This class is designed to be static so + // there's no true refcount. + return 0; +} + +STDMETHODIMP +MSVCDebugOutput::Output( + THIS_ + IN ULONG mask, + IN PCSTR text + ) +{ + UNREFERENCED_PARAMETER(mask); + m_pEngine->handleDebugOutput(text); + return S_OK; +} + +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/msvcdebugoutput.h b/src/plugins/debugger/msvcdebugoutput.h new file mode 100644 index 00000000000..f0e1f32d79f --- /dev/null +++ b/src/plugins/debugger/msvcdebugoutput.h @@ -0,0 +1,43 @@ +#ifndef __MSVCDEBUGOUTPUT_H__ +#define __MSVCDEBUGOUTPUT_H__ + +namespace Debugger { +namespace Internal { + +class MSVCDebugEngine; + +class MSVCDebugOutput : public IDebugOutputCallbacks +{ +public: + MSVCDebugOutput(MSVCDebugEngine* engine) + : m_pEngine(engine) + {} + + // 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 PCSTR text + ); + +private: + MSVCDebugEngine* m_pEngine; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // #ifndef __MSVCDEBUGOUTPUT_H__ -- GitLab