From: Igor Kulaychuk Date: Thu, 18 Jan 2018 19:53:45 +0000 (+0300) Subject: Refactor ManagedDebugger implementation into separate file X-Git-Tag: submit/tizen/20180620.071641~39 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=3eec1bd114e4e4c1f05c1a1e98d0cad6d2d2c28e;p=sdk%2Ftools%2Fnetcoredbg.git Refactor ManagedDebugger implementation into separate file --- diff --git a/src/debug/netcoredbg/CMakeLists.txt b/src/debug/netcoredbg/CMakeLists.txt index fa3aa81..b6b3c12 100644 --- a/src/debug/netcoredbg/CMakeLists.txt +++ b/src/debug/netcoredbg/CMakeLists.txt @@ -1,5 +1,6 @@ set(netcoredbg_SRC main.cpp + manageddebugger.cpp symbolreader.cpp platform.cpp breakpoints.cpp diff --git a/src/debug/netcoredbg/main.cpp b/src/debug/netcoredbg/main.cpp index 5cc3456..73e16fe 100644 --- a/src/debug/netcoredbg/main.cpp +++ b/src/debug/netcoredbg/main.cpp @@ -3,1063 +3,9 @@ // See the LICENSE file in the project root for more information. #include "common.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "symbolreader.h" -#include "cputil.h" -#include "platform.h" -#include "typeprinter.h" #include "debugger.h" -#include "frames.h" - -#define __in -#define __out -#include "dbgshim.h" -#undef __in -#undef __out - - -void ManagedDebugger::NotifyProcessCreated() -{ - std::lock_guard lock(m_processAttachedMutex); - m_processAttachedState = ProcessAttached; -} - -void ManagedDebugger::NotifyProcessExited() -{ - std::lock_guard lock(m_processAttachedMutex); - m_processAttachedState = ProcessUnattached; - m_processAttachedMutex.unlock(); - m_processAttachedCV.notify_one(); -} - -void ManagedDebugger::WaitProcessExited() -{ - std::unique_lock lock(m_processAttachedMutex); - if (m_processAttachedState != ProcessUnattached) - m_processAttachedCV.wait(lock, [this]{return m_processAttachedState == ProcessUnattached;}); -} - -size_t NextOSPageAddress (size_t addr) -{ - size_t pageSize = OSPageSize(); - return (addr+pageSize)&(~(pageSize-1)); -} - -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to read memory from the debugee's * -* address space. If the initial read fails, it attempts to read * -* only up to the edge of the page containing "offset". * -* * -\**********************************************************************/ -BOOL SafeReadMemory (TADDR offset, PVOID lpBuffer, ULONG cb, - PULONG lpcbBytesRead) -{ - return FALSE; - // TODO: In-memory PDB? - // std::lock_guard lock(g_processMutex); - - // if (!g_process) - // return FALSE; - - // BOOL bRet = FALSE; - - // SIZE_T bytesRead = 0; - - // bRet = SUCCEEDED(g_process->ReadMemory(TO_CDADDR(offset), cb, (BYTE*)lpBuffer, - // &bytesRead)); - - // if (!bRet) - // { - // cb = (ULONG)(NextOSPageAddress(offset) - offset); - // bRet = SUCCEEDED(g_process->ReadMemory(TO_CDADDR(offset), cb, (BYTE*)lpBuffer, - // &bytesRead)); - // } - - // *lpcbBytesRead = bytesRead; - // return bRet; -} - -std::mutex MIProtocol::m_outMutex; - -void MIProtocol::Printf(const char *fmt, ...) -{ - std::lock_guard lock(m_outMutex); - va_list arg; - - va_start(arg, fmt); - vfprintf(stdout, fmt, arg); - va_end(arg); - - fflush(stdout); -} - -std::string MIProtocol::EscapeMIValue(const std::string &str) -{ - std::string s(str); - - for (std::size_t i = 0; i < s.size(); ++i) - { - int count = 0; - char c = s.at(i); - switch (c) - { - case '\"': count = 1; s.insert(i, count, '\\'); s[i + count] = '\"'; break; - case '\\': count = 1; s.insert(i, count, '\\'); s[i + count] = '\\'; break; - case '\0': count = 1; s.insert(i, count, '\\'); s[i + count] = '0'; break; - case '\a': count = 1; s.insert(i, count, '\\'); s[i + count] = 'a'; break; - case '\b': count = 1; s.insert(i, count, '\\'); s[i + count] = 'b'; break; - case '\f': count = 1; s.insert(i, count, '\\'); s[i + count] = 'f'; break; - case '\n': count = 1; s.insert(i, count, '\\'); s[i + count] = 'n'; break; - case '\r': count = 1; s.insert(i, count, '\\'); s[i + count] = 'r'; break; - case '\t': count = 1; s.insert(i, count, '\\'); s[i + count] = 't'; break; - case '\v': count = 1; s.insert(i, count, '\\'); s[i + count] = 'v'; break; - } - i += count; - } - - return s; -} - -static HRESULT DisableAllSteppersInAppDomain(ICorDebugAppDomain *pAppDomain) -{ - HRESULT Status; - ToRelease steppers; - IfFailRet(pAppDomain->EnumerateSteppers(&steppers)); - - ICorDebugStepper *curStepper; - ULONG steppersFetched; - while (SUCCEEDED(steppers->Next(1, &curStepper, &steppersFetched)) && steppersFetched == 1) - { - ToRelease pStepper(curStepper); - pStepper->Deactivate(); - } - - return S_OK; -} - -HRESULT ManagedDebugger::DisableAllSteppers(ICorDebugProcess *pProcess) -{ - HRESULT Status; - - ToRelease domains; - IfFailRet(pProcess->EnumerateAppDomains(&domains)); - - ICorDebugAppDomain *curDomain; - ULONG domainsFetched; - while (SUCCEEDED(domains->Next(1, &curDomain, &domainsFetched)) && domainsFetched == 1) - { - ToRelease pDomain(curDomain); - DisableAllSteppersInAppDomain(pDomain); - } - return S_OK; -} - -static HRESULT DisableAllBreakpointsAndSteppersInAppDomain(ICorDebugAppDomain *pAppDomain) -{ - HRESULT Status; - - ToRelease breakpoints; - if (SUCCEEDED(pAppDomain->EnumerateBreakpoints(&breakpoints))) - { - ICorDebugBreakpoint *curBreakpoint; - ULONG breakpointsFetched; - while (SUCCEEDED(breakpoints->Next(1, &curBreakpoint, &breakpointsFetched)) && breakpointsFetched == 1) - { - ToRelease pBreakpoint(curBreakpoint); - pBreakpoint->Activate(FALSE); - } - } - - DisableAllSteppersInAppDomain(pAppDomain); - - return S_OK; -} - -HRESULT DisableAllBreakpointsAndSteppers(ICorDebugProcess *pProcess) -{ - HRESULT Status; - - ToRelease domains; - IfFailRet(pProcess->EnumerateAppDomains(&domains)); - - ICorDebugAppDomain *curDomain; - ULONG domainsFetched; - while (SUCCEEDED(domains->Next(1, &curDomain, &domainsFetched)) && domainsFetched == 1) - { - ToRelease pDomain(curDomain); - DisableAllBreakpointsAndSteppersInAppDomain(pDomain); - } - return S_OK; -} - -void ManagedDebugger::SetLastStoppedThread(ICorDebugThread *pThread) -{ - DWORD threadId = 0; - pThread->GetID(&threadId); - - std::lock_guard lock(m_lastStoppedThreadIdMutex); - m_lastStoppedThreadId = threadId; -} - -int ManagedDebugger::GetLastStoppedThreadId() -{ - std::lock_guard lock(m_lastStoppedThreadIdMutex); - return m_lastStoppedThreadId; -} - -static HRESULT GetExceptionInfo(ICorDebugThread *pThread, - std::string &excType, - std::string &excModule) -{ - HRESULT Status; - - ToRelease pExceptionValue; - IfFailRet(pThread->GetCurrentException(&pExceptionValue)); - - TypePrinter::GetTypeOfValue(pExceptionValue, excType); - - ToRelease pFrame; - IfFailRet(pThread->GetActiveFrame(&pFrame)); - if (pFrame == nullptr) - return E_FAIL; - ToRelease pFunc; - IfFailRet(pFrame->GetFunction(&pFunc)); - - ToRelease pModule; - IfFailRet(pFunc->GetModule(&pModule)); - - ToRelease pMDUnknown; - ToRelease pMDImport; - IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown)); - IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMDImport)); - - WCHAR mdName[mdNameLen]; - ULONG nameLen; - IfFailRet(pMDImport->GetScopeProps(mdName, _countof(mdName), &nameLen, nullptr)); - excModule = to_utf8(mdName); - return S_OK; -} - -class ManagedCallback : public ICorDebugManagedCallback, ICorDebugManagedCallback2 -{ - ULONG m_refCount; - ManagedDebugger &m_debugger; -public: - - void HandleEvent(ICorDebugController *controller, const std::string &eventName) - { - std::string text = "Event received: '" + eventName + "'\n"; - m_debugger.m_protocol->EmitOutputEvent(OutputEvent(OutputConsole, text)); - controller->Continue(0); - } - - ManagedCallback(ManagedDebugger &debugger) : m_refCount(1), m_debugger(debugger) {} - virtual ~ManagedCallback() {} - - // IUnknown - - virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppInterface) - { - if(riid == __uuidof(ICorDebugManagedCallback)) - { - *ppInterface = static_cast(this); - AddRef(); - return S_OK; - } - else if(riid == __uuidof(ICorDebugManagedCallback2)) - { - *ppInterface = static_cast(this); - AddRef(); - return S_OK; - } - else if(riid == __uuidof(IUnknown)) - { - *ppInterface = static_cast(static_cast(this)); - AddRef(); - return S_OK; - } - else - { - return E_NOINTERFACE; - } - } - - virtual ULONG STDMETHODCALLTYPE AddRef() - { - return InterlockedIncrement((volatile LONG *) &m_refCount); - } - - virtual ULONG STDMETHODCALLTYPE Release() - { - ULONG count = InterlockedDecrement((volatile LONG *) &m_refCount); - if(count == 0) - { - delete this; - } - return count; - } - - // ICorDebugManagedCallback - - virtual HRESULT STDMETHODCALLTYPE Breakpoint( - /* [in] */ ICorDebugAppDomain *pAppDomain, - /* [in] */ ICorDebugThread *pThread, - /* [in] */ ICorDebugBreakpoint *pBreakpoint) - { - if (m_debugger.m_evaluator.IsEvalRunning()) - { - pAppDomain->Continue(0); - return S_OK; - } - - DWORD threadId = 0; - pThread->GetID(&threadId); - - StoppedEvent event(StopBreakpoint, threadId); - m_debugger.m_breakpoints.HitBreakpoint(pThread, event.breakpoint); - - ToRelease pFrame; - if (SUCCEEDED(pThread->GetActiveFrame(&pFrame)) && pFrame != nullptr) - m_debugger.GetFrameLocation(pFrame, threadId, 0, event.frame); - - m_debugger.SetLastStoppedThread(pThread); - m_debugger.m_protocol->EmitStoppedEvent(event); - - return S_OK; - } - - virtual HRESULT STDMETHODCALLTYPE StepComplete( - /* [in] */ ICorDebugAppDomain *pAppDomain, - /* [in] */ ICorDebugThread *pThread, - /* [in] */ ICorDebugStepper *pStepper, - /* [in] */ CorDebugStepReason reason) - { - DWORD threadId = 0; - pThread->GetID(&threadId); - - StackFrame stackFrame; - ToRelease pFrame; - HRESULT Status = S_FALSE; - if (SUCCEEDED(pThread->GetActiveFrame(&pFrame)) && pFrame != nullptr) - Status = m_debugger.GetFrameLocation(pFrame, threadId, 0, stackFrame); - - const bool no_source = Status == S_FALSE; - - if (m_debugger.IsJustMyCode() && no_source) - { - m_debugger.SetupStep(pThread, Debugger::STEP_OVER); - pAppDomain->Continue(0); - } - else - { - StoppedEvent event(StopStep, threadId); - event.frame = stackFrame; - - m_debugger.SetLastStoppedThread(pThread); - m_debugger.m_protocol->EmitStoppedEvent(event); - } - return S_OK; - } - - virtual HRESULT STDMETHODCALLTYPE Break( - /* [in] */ ICorDebugAppDomain *pAppDomain, - /* [in] */ ICorDebugThread *thread) { HandleEvent(pAppDomain, "Break"); return S_OK; } - - virtual HRESULT STDMETHODCALLTYPE Exception( - /* [in] */ ICorDebugAppDomain *pAppDomain, - /* [in] */ ICorDebugThread *pThread, - /* [in] */ BOOL unhandled) - { - std::string excType; - std::string excModule; - GetExceptionInfo(pThread, excType, excModule); - - if (unhandled) - { - DWORD threadId = 0; - pThread->GetID(&threadId); - - StackFrame stackFrame; - ToRelease pFrame; - if (SUCCEEDED(pThread->GetActiveFrame(&pFrame)) && pFrame != nullptr) - m_debugger.GetFrameLocation(pFrame, threadId, 0, stackFrame); - - m_debugger.SetLastStoppedThread(pThread); - - std::string details = "An unhandled exception of type '" + excType + "' occurred in " + excModule; - - ToRelease pExceptionValue; - - auto emitFunc = [=](const std::string &message) { - StoppedEvent event(StopException, threadId); - event.text = excType; - event.description = message.empty() ? details : message; - event.frame = stackFrame; - m_debugger.m_protocol->EmitStoppedEvent(event); - }; - - if (FAILED(pThread->GetCurrentException(&pExceptionValue)) || - FAILED(m_debugger.m_evaluator.ObjectToString(pThread, pExceptionValue, emitFunc))) - { - emitFunc(details); - } - } - else - { - std::string text = "Exception thrown: '" + excType + "' in " + excModule + "\n"; - OutputEvent event(OutputConsole, text); - event.source = "target-exception"; - m_debugger.m_protocol->EmitOutputEvent(event); - pAppDomain->Continue(0); - } - - return S_OK; - } - - virtual HRESULT STDMETHODCALLTYPE EvalComplete( - /* [in] */ ICorDebugAppDomain *pAppDomain, - /* [in] */ ICorDebugThread *pThread, - /* [in] */ ICorDebugEval *pEval) - { - m_debugger.m_evaluator.NotifyEvalComplete(pThread, pEval); - return S_OK; - } - - virtual HRESULT STDMETHODCALLTYPE EvalException( - /* [in] */ ICorDebugAppDomain *pAppDomain, - /* [in] */ ICorDebugThread *pThread, - /* [in] */ ICorDebugEval *pEval) - { - m_debugger.m_evaluator.NotifyEvalComplete(pThread, pEval); - return S_OK; - } - - virtual HRESULT STDMETHODCALLTYPE CreateProcess( - /* [in] */ ICorDebugProcess *pProcess) - { - //HandleEvent(pProcess, "CreateProcess"); - m_debugger.NotifyProcessCreated(); - pProcess->Continue(0); - return S_OK; - } - - virtual HRESULT STDMETHODCALLTYPE ExitProcess( - /* [in] */ ICorDebugProcess *pProcess) - { - m_debugger.m_evaluator.NotifyEvalComplete(nullptr, nullptr); - m_debugger.m_protocol->EmitExitedEvent(ExitedEvent(0)); - m_debugger.NotifyProcessExited(); - return S_OK; - } - - virtual HRESULT STDMETHODCALLTYPE CreateThread( - /* [in] */ ICorDebugAppDomain *pAppDomain, - /* [in] */ ICorDebugThread *thread) - { - DWORD threadId = 0; - thread->GetID(&threadId); - m_debugger.m_protocol->EmitThreadEvent(ThreadEvent(ThreadStarted, threadId)); - pAppDomain->Continue(0); - return S_OK; - } - - virtual HRESULT STDMETHODCALLTYPE ExitThread( - /* [in] */ ICorDebugAppDomain *pAppDomain, - /* [in] */ ICorDebugThread *thread) - { - m_debugger.m_evaluator.NotifyEvalComplete(thread, nullptr); - DWORD threadId = 0; - thread->GetID(&threadId); - m_debugger.m_protocol->EmitThreadEvent(ThreadEvent(ThreadExited, threadId)); - pAppDomain->Continue(0); - return S_OK; - } - - virtual HRESULT STDMETHODCALLTYPE LoadModule( - /* [in] */ ICorDebugAppDomain *pAppDomain, - /* [in] */ ICorDebugModule *pModule) - { - std::string id; - std::string name; - bool symbolsLoaded = false; - CORDB_ADDRESS baseAddress = 0; - ULONG32 size = 0; - - m_debugger.m_modules.TryLoadModuleSymbols(pModule, id, name, symbolsLoaded, baseAddress, size); - - std::stringstream ss; - ss << "id=\"{" << id << "}\"," - << "target-name=\"" << MIProtocol::EscapeMIValue(name) << "\"," - << "host-name=\"" << MIProtocol::EscapeMIValue(name) << "\"," - << "symbols-loaded=\"" << symbolsLoaded << "\"," - << "base-address=\"0x" << std::hex << baseAddress << "\"," - << "size=\"" << std::dec << size << "\""; - MIProtocol::Printf("=library-loaded,%s\n", ss.str().c_str()); - - if (symbolsLoaded) - { - std::vector events; - m_debugger.m_breakpoints.TryResolveBreakpointsForModule(pModule, events); - for (const BreakpointEvent &event : events) - m_debugger.m_protocol->EmitBreakpointEvent(event); - } - - pAppDomain->Continue(0); - return S_OK; - } - - virtual HRESULT STDMETHODCALLTYPE UnloadModule( - /* [in] */ ICorDebugAppDomain *pAppDomain, - /* [in] */ ICorDebugModule *pModule) { HandleEvent(pAppDomain, "UnloadModule"); return S_OK; } - - virtual HRESULT STDMETHODCALLTYPE LoadClass( - /* [in] */ ICorDebugAppDomain *pAppDomain, - /* [in] */ ICorDebugClass *c) { HandleEvent(pAppDomain, "LoadClass"); return S_OK; } - - virtual HRESULT STDMETHODCALLTYPE UnloadClass( - /* [in] */ ICorDebugAppDomain *pAppDomain, - /* [in] */ ICorDebugClass *c) { HandleEvent(pAppDomain, "UnloadClass"); return S_OK; } - - virtual HRESULT STDMETHODCALLTYPE DebuggerError( - /* [in] */ ICorDebugProcess *pProcess, - /* [in] */ HRESULT errorHR, - /* [in] */ DWORD errorCode) { printf("DebuggerError\n"); return S_OK; } - - virtual HRESULT STDMETHODCALLTYPE LogMessage( - /* [in] */ ICorDebugAppDomain *pAppDomain, - /* [in] */ ICorDebugThread *pThread, - /* [in] */ LONG lLevel, - /* [in] */ WCHAR *pLogSwitchName, - /* [in] */ WCHAR *pMessage) { pAppDomain->Continue(0); return S_OK; } - - virtual HRESULT STDMETHODCALLTYPE LogSwitch( - /* [in] */ ICorDebugAppDomain *pAppDomain, - /* [in] */ ICorDebugThread *pThread, - /* [in] */ LONG lLevel, - /* [in] */ ULONG ulReason, - /* [in] */ WCHAR *pLogSwitchName, - /* [in] */ WCHAR *pParentName) { pAppDomain->Continue(0); return S_OK; } - - virtual HRESULT STDMETHODCALLTYPE CreateAppDomain( - /* [in] */ ICorDebugProcess *pProcess, - /* [in] */ ICorDebugAppDomain *pAppDomain) - { - //HandleEvent(pProcess, "CreateAppDomain"); - pProcess->Continue(0); - return S_OK; - } - - virtual HRESULT STDMETHODCALLTYPE ExitAppDomain( - /* [in] */ ICorDebugProcess *pProcess, - /* [in] */ ICorDebugAppDomain *pAppDomain) { HandleEvent(pAppDomain, "ExitAppDomain"); return S_OK; } - - virtual HRESULT STDMETHODCALLTYPE LoadAssembly( - /* [in] */ ICorDebugAppDomain *pAppDomain, - /* [in] */ ICorDebugAssembly *pAssembly) - { - //HandleEvent(pAppDomain, "LoadAssembly"); - pAppDomain->Continue(0); - return S_OK; - } - - virtual HRESULT STDMETHODCALLTYPE UnloadAssembly( - /* [in] */ ICorDebugAppDomain *pAppDomain, - /* [in] */ ICorDebugAssembly *pAssembly) { return S_OK; } - - virtual HRESULT STDMETHODCALLTYPE ControlCTrap( - /* [in] */ ICorDebugProcess *pProcess) { HandleEvent(pProcess, "ControlCTrap"); return S_OK; } - - virtual HRESULT STDMETHODCALLTYPE NameChange( - /* [in] */ ICorDebugAppDomain *pAppDomain, - /* [in] */ ICorDebugThread *pThread) - { - pAppDomain->Continue(0); - return S_OK; - } - - virtual HRESULT STDMETHODCALLTYPE UpdateModuleSymbols( - /* [in] */ ICorDebugAppDomain *pAppDomain, - /* [in] */ ICorDebugModule *pModule, - /* [in] */ IStream *pSymbolStream) { HandleEvent(pAppDomain, "UpdateModuleSymbols"); return S_OK; } - - virtual HRESULT STDMETHODCALLTYPE EditAndContinueRemap( - /* [in] */ ICorDebugAppDomain *pAppDomain, - /* [in] */ ICorDebugThread *pThread, - /* [in] */ ICorDebugFunction *pFunction, - /* [in] */ BOOL fAccurate) { return S_OK; } - - virtual HRESULT STDMETHODCALLTYPE BreakpointSetError( - /* [in] */ ICorDebugAppDomain *pAppDomain, - /* [in] */ ICorDebugThread *pThread, - /* [in] */ ICorDebugBreakpoint *pBreakpoint, - /* [in] */ DWORD dwError) {return S_OK; } - - - // ICorDebugManagedCallback2 - - virtual HRESULT STDMETHODCALLTYPE FunctionRemapOpportunity( - /* [in] */ ICorDebugAppDomain *pAppDomain, - /* [in] */ ICorDebugThread *pThread, - /* [in] */ ICorDebugFunction *pOldFunction, - /* [in] */ ICorDebugFunction *pNewFunction, - /* [in] */ ULONG32 oldILOffset) {return S_OK; } - - virtual HRESULT STDMETHODCALLTYPE CreateConnection( - /* [in] */ ICorDebugProcess *pProcess, - /* [in] */ CONNID dwConnectionId, - /* [in] */ WCHAR *pConnName) { HandleEvent(pProcess, "CreateConnection"); return S_OK; } - - virtual HRESULT STDMETHODCALLTYPE ChangeConnection( - /* [in] */ ICorDebugProcess *pProcess, - /* [in] */ CONNID dwConnectionId) { HandleEvent(pProcess, "ChangeConnection"); return S_OK; } - - virtual HRESULT STDMETHODCALLTYPE DestroyConnection( - /* [in] */ ICorDebugProcess *pProcess, - /* [in] */ CONNID dwConnectionId) {return S_OK; } - - virtual HRESULT STDMETHODCALLTYPE Exception( - /* [in] */ ICorDebugAppDomain *pAppDomain, - /* [in] */ ICorDebugThread *pThread, - /* [in] */ ICorDebugFrame *pFrame, - /* [in] */ ULONG32 nOffset, - /* [in] */ CorDebugExceptionCallbackType dwEventType, - /* [in] */ DWORD dwFlags) - { - // const char *cbTypeName; - // switch(dwEventType) - // { - // case DEBUG_EXCEPTION_FIRST_CHANCE: cbTypeName = "FIRST_CHANCE"; break; - // case DEBUG_EXCEPTION_USER_FIRST_CHANCE: cbTypeName = "USER_FIRST_CHANCE"; break; - // case DEBUG_EXCEPTION_CATCH_HANDLER_FOUND: cbTypeName = "CATCH_HANDLER_FOUND"; break; - // case DEBUG_EXCEPTION_UNHANDLED: cbTypeName = "UNHANDLED"; break; - // default: cbTypeName = "?"; break; - // } - // ManagedDebugger::Printf("*stopped,reason=\"exception-received2\",exception-stage=\"%s\"\n", - // cbTypeName); - pAppDomain->Continue(0); - return S_OK; - } - - virtual HRESULT STDMETHODCALLTYPE ExceptionUnwind( - /* [in] */ ICorDebugAppDomain *pAppDomain, - /* [in] */ ICorDebugThread *pThread, - /* [in] */ CorDebugExceptionUnwindCallbackType dwEventType, - /* [in] */ DWORD dwFlags) {return S_OK; } - - virtual HRESULT STDMETHODCALLTYPE FunctionRemapComplete( - /* [in] */ ICorDebugAppDomain *pAppDomain, - /* [in] */ ICorDebugThread *pThread, - /* [in] */ ICorDebugFunction *pFunction) {return S_OK; } - - virtual HRESULT STDMETHODCALLTYPE MDANotification( - /* [in] */ ICorDebugController *pController, - /* [in] */ ICorDebugThread *pThread, - /* [in] */ ICorDebugMDA *pMDA) { HandleEvent(pController, "MDANotification"); return S_OK; } -}; - -ManagedDebugger::ManagedDebugger() : - m_processAttachedState(ProcessUnattached), - m_evaluator(m_modules), - m_breakpoints(m_modules), - m_variables(m_evaluator), - m_managedCallback(new ManagedCallback(*this)), - m_pDebug(nullptr), - m_pProcess(nullptr), - m_justMyCode(true), - m_startupReady(false), - m_startupResult(S_OK), - m_unregisterToken(nullptr), - m_processId(0) -{ -} - -ManagedDebugger::~ManagedDebugger() -{ -} - -HRESULT ManagedDebugger::SetupStep(ICorDebugThread *pThread, Debugger::StepType stepType) -{ - HRESULT Status; - - ToRelease pStepper; - IfFailRet(pThread->CreateStepper(&pStepper)); - - CorDebugIntercept mask = (CorDebugIntercept)(INTERCEPT_ALL & ~(INTERCEPT_SECURITY | INTERCEPT_CLASS_INIT)); - IfFailRet(pStepper->SetInterceptMask(mask)); - - CorDebugUnmappedStop stopMask = STOP_NONE; - IfFailRet(pStepper->SetUnmappedStopMask(stopMask)); - - ToRelease pStepper2; - IfFailRet(pStepper->QueryInterface(IID_ICorDebugStepper2, (LPVOID *)&pStepper2)); - - IfFailRet(pStepper2->SetJMC(IsJustMyCode())); - - if (stepType == STEP_OUT) - { - IfFailRet(pStepper->StepOut()); - return S_OK; - } - - BOOL bStepIn = stepType == STEP_IN; - - COR_DEBUG_STEP_RANGE range; - if (SUCCEEDED(m_modules.GetStepRangeFromCurrentIP(pThread, &range))) - { - IfFailRet(pStepper->StepRange(bStepIn, &range, 1)); - } else { - IfFailRet(pStepper->Step(bStepIn)); - } - - return S_OK; -} - -HRESULT ManagedDebugger::StepCommand(int threadId, StepType stepType) -{ - HRESULT Status; - ToRelease pThread; - IfFailRet(m_pProcess->GetThread(threadId, &pThread)); - DisableAllSteppers(m_pProcess); - IfFailRet(SetupStep(pThread, stepType)); - IfFailRet(m_pProcess->Continue(0)); - return S_OK; -} - -HRESULT ManagedDebugger::Continue() -{ - if (!m_pProcess) - return E_FAIL; - return m_pProcess->Continue(0); -} - -HRESULT ManagedDebugger::Pause() -{ - if (!m_pProcess) - return E_FAIL; - HRESULT Status = m_pProcess->Stop(0); - if (Status == S_OK) - m_protocol->EmitStoppedEvent(StoppedEvent(StopPause, 0)); - return Status; -} - -HRESULT ManagedDebugger::GetThreads(std::vector &threads) -{ - if (!m_pProcess) - return E_FAIL; - return GetThreadsState(m_pProcess, threads); -} - -HRESULT ManagedDebugger::GetStackTrace(int threadId, int lowFrame, int highFrame, std::vector &stackFrames) -{ - HRESULT Status; - if (!m_pProcess) - return E_FAIL; - ToRelease pThread; - IfFailRet(m_pProcess->GetThread(threadId, &pThread)); - return GetStackTrace(pThread, lowFrame, highFrame, stackFrames); -} - -VOID ManagedDebugger::StartupCallback(IUnknown *pCordb, PVOID parameter, HRESULT hr) -{ - ManagedDebugger *self = static_cast(parameter); - - std::unique_lock lock(self->m_startupMutex); - - self->m_startupResult = FAILED(hr) ? hr : self->Startup(pCordb, self->m_processId); - self->m_startupReady = true; - - if (self->m_unregisterToken) - { - UnregisterForRuntimeStartup(self->m_unregisterToken); - self->m_unregisterToken = nullptr; - } - - lock.unlock(); - self->m_startupCV.notify_one(); -} - -// From dbgshim.cpp -static bool AreAllHandlesValid(HANDLE *handleArray, DWORD arrayLength) -{ - for (DWORD i = 0; i < arrayLength; i++) - { - HANDLE h = handleArray[i]; - if (h == INVALID_HANDLE_VALUE) - { - return false; - } - } - return true; -} - -static HRESULT InternalEnumerateCLRs(int pid, HANDLE **ppHandleArray, LPWSTR **ppStringArray, DWORD *pdwArrayLength) -{ - int numTries = 0; - HRESULT hr; - - while (numTries < 25) - { - hr = EnumerateCLRs(pid, ppHandleArray, ppStringArray, pdwArrayLength); - - // EnumerateCLRs uses the OS API CreateToolhelp32Snapshot which can return ERROR_BAD_LENGTH or - // ERROR_PARTIAL_COPY. If we get either of those, we try wait 1/10th of a second try again (that - // is the recommendation of the OS API owners). - if ((hr != HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY)) && (hr != HRESULT_FROM_WIN32(ERROR_BAD_LENGTH))) - { - // Just return any other error or if no handles were found (which means the coreclr module wasn't found yet). - if (FAILED(hr) || *ppHandleArray == NULL || *pdwArrayLength <= 0) - { - return hr; - } - // If EnumerateCLRs succeeded but any of the handles are INVALID_HANDLE_VALUE, then sleep and retry - // also. This fixes a race condition where dbgshim catches the coreclr module just being loaded but - // before g_hContinueStartupEvent has been initialized. - if (AreAllHandlesValid(*ppHandleArray, *pdwArrayLength)) - { - return hr; - } - // Clean up memory allocated in EnumerateCLRs since this path it succeeded - CloseCLREnumeration(*ppHandleArray, *ppStringArray, *pdwArrayLength); - - *ppHandleArray = NULL; - *ppStringArray = NULL; - *pdwArrayLength = 0; - } - - // Sleep and retry enumerating the runtimes - Sleep(100); - numTries++; - - // if (m_canceled) - // { - // break; - // } - } - - // Indicate a timeout - hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); - - return hr; -} - -static std::string GetCLRPath(int pid) -{ - HANDLE* pHandleArray; - LPWSTR* pStringArray; - DWORD dwArrayLength; - if (FAILED(InternalEnumerateCLRs(pid, &pHandleArray, &pStringArray, &dwArrayLength)) || dwArrayLength == 0) - return std::string(); - - std::string result = to_utf8(pStringArray[0]); - - CloseCLREnumeration(pHandleArray, pStringArray, dwArrayLength); - - return result; -} - -HRESULT ManagedDebugger::Startup(IUnknown *punk, int pid) -{ - HRESULT Status; - - Cleanup(); - - ToRelease pCorDebug; - IfFailRet(punk->QueryInterface(IID_ICorDebug, (void **)&pCorDebug)); - - IfFailRet(pCorDebug->Initialize()); - - Status = pCorDebug->SetManagedHandler(m_managedCallback); - if (FAILED(Status)) - { - pCorDebug->Terminate(); - return Status; - } - - if (m_clrPath.empty()) - m_clrPath = GetCLRPath(pid); - - SymbolReader::SetCoreCLRPath(m_clrPath); - - ToRelease pProcess; - Status = pCorDebug->DebugActiveProcess(pid, FALSE, &pProcess); - if (FAILED(Status)) - { - pCorDebug->Terminate(); - return Status; - } - - m_pProcess = pProcess.Detach(); - m_pDebug = pCorDebug.Detach(); - - m_processId = pid; - - return S_OK; -} - -HRESULT ManagedDebugger::RunProcess(std::string fileExec, std::vector execArgs) -{ - static const auto startupCallbackWaitTimeout = std::chrono::milliseconds(5000); - HRESULT Status; - - IfFailRet(CheckNoProcess()); - - std::stringstream ss; - ss << "\"" << fileExec << "\""; - for (std::string &arg : execArgs) - { - ss << " \"" << MIProtocol::EscapeMIValue(arg) << "\""; - } - std::string cmdString = ss.str(); - std::unique_ptr cmd(new WCHAR[cmdString.size() + 1]); - - MultiByteToWideChar(CP_UTF8, 0, cmdString.c_str(), cmdString.size() + 1, &cmd[0], cmdString.size() + 1); - - m_startupReady = false; - m_clrPath.clear(); - - BOOL bSuspendProcess = TRUE; - LPVOID lpEnvironment = nullptr; // as current - LPCWSTR lpCurrentDirectory = nullptr; // as current - HANDLE resumeHandle; - IfFailRet(CreateProcessForLaunch(&cmd[0], bSuspendProcess, lpEnvironment, lpCurrentDirectory, &m_processId, &resumeHandle)); - - IfFailRet(RegisterForRuntimeStartup(m_processId, ManagedDebugger::StartupCallback, this, &m_unregisterToken)); - - // Resume the process so that StartupCallback can run - IfFailRet(ResumeProcess(resumeHandle)); - CloseResumeHandle(resumeHandle); - - // Wait for ManagedDebugger::StartupCallback to complete - - // FIXME: if the process exits too soon the ManagedDebugger::StartupCallback() - // is never called (bug in dbgshim?). - // The workaround is to wait with timeout. - auto now = std::chrono::system_clock::now(); - - std::unique_lock lock(m_startupMutex); - if (!m_startupCV.wait_until(lock, now + startupCallbackWaitTimeout, [this](){return m_startupReady;})) - { - // Timed out - UnregisterForRuntimeStartup(m_unregisterToken); - m_unregisterToken = nullptr; - return E_FAIL; - } - - return m_startupResult; -} - -HRESULT ManagedDebugger::CheckNoProcess() -{ - if (m_pProcess || m_pDebug) - { - std::lock_guard lock(m_processAttachedMutex); - if (m_processAttachedState == ProcessAttached) - return E_FAIL; // Already attached - m_processAttachedMutex.unlock(); - - TerminateProcess(); - } - return S_OK; -} - -HRESULT ManagedDebugger::DetachFromProcess() -{ - if (!m_pProcess || !m_pDebug) - return E_FAIL; - - if (SUCCEEDED(m_pProcess->Stop(0))) - { - m_breakpoints.DeleteAllBreakpoints(); - DisableAllBreakpointsAndSteppers(m_pProcess); - m_pProcess->Detach(); - } - - Cleanup(); - - m_pProcess->Release(); - m_pProcess = nullptr; - - m_pDebug->Terminate(); - m_pDebug = nullptr; - - return S_OK; -} - -HRESULT ManagedDebugger::TerminateProcess() -{ - if (!m_pProcess || !m_pDebug) - return E_FAIL; - - if (SUCCEEDED(m_pProcess->Stop(0))) - { - DisableAllBreakpointsAndSteppers(m_pProcess); - //pProcess->Detach(); - } - - Cleanup(); - - m_pProcess->Terminate(0); - WaitProcessExited(); - - m_pProcess->Release(); - m_pProcess = nullptr; - - m_pDebug->Terminate(); - m_pDebug = nullptr; - - return S_OK; -} - -void ManagedDebugger::Cleanup() -{ - m_modules.CleanupAllModules(); - m_evaluator.Cleanup(); - m_protocol->Cleanup(); - // TODO: Cleanup libcoreclr.so instance -} - -HRESULT ManagedDebugger::AttachToProcess(int pid) -{ - HRESULT Status; - - IfFailRet(CheckNoProcess()); - - std::string m_clrPath = GetCLRPath(pid); - if (m_clrPath.empty()) - return E_INVALIDARG; // Unable to find libcoreclr.so - - WCHAR szModuleName[MAX_LONGPATH]; - MultiByteToWideChar(CP_UTF8, 0, m_clrPath.c_str(), m_clrPath.size() + 1, szModuleName, MAX_LONGPATH); - - WCHAR pBuffer[100]; - DWORD dwLength; - IfFailRet(CreateVersionStringFromModule( - pid, - szModuleName, - pBuffer, - _countof(pBuffer), - &dwLength)); - - ToRelease pCordb; - - IfFailRet(CreateDebuggingInterfaceFromVersionEx(4, pBuffer, &pCordb)); - - m_unregisterToken = nullptr; - return Startup(pCordb, pid); -} -void print_help() +static void print_help() { fprintf(stderr, ".NET Core debugger for Linux/macOS.\n" diff --git a/src/debug/netcoredbg/manageddebugger.cpp b/src/debug/netcoredbg/manageddebugger.cpp new file mode 100644 index 0000000..b88d50b --- /dev/null +++ b/src/debug/netcoredbg/manageddebugger.cpp @@ -0,0 +1,1060 @@ +// Copyright (c) 2017 Samsung Electronics Co., LTD +// Distributed under the MIT License. +// See the LICENSE file in the project root for more information. + +#include "common.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "symbolreader.h" +#include "cputil.h" +#include "platform.h" +#include "typeprinter.h" +#include "debugger.h" +#include "frames.h" + +#define __in +#define __out +#include "dbgshim.h" +#undef __in +#undef __out + + +void ManagedDebugger::NotifyProcessCreated() +{ + std::lock_guard lock(m_processAttachedMutex); + m_processAttachedState = ProcessAttached; +} + +void ManagedDebugger::NotifyProcessExited() +{ + std::lock_guard lock(m_processAttachedMutex); + m_processAttachedState = ProcessUnattached; + m_processAttachedMutex.unlock(); + m_processAttachedCV.notify_one(); +} + +void ManagedDebugger::WaitProcessExited() +{ + std::unique_lock lock(m_processAttachedMutex); + if (m_processAttachedState != ProcessUnattached) + m_processAttachedCV.wait(lock, [this]{return m_processAttachedState == ProcessUnattached;}); +} + +size_t NextOSPageAddress (size_t addr) +{ + size_t pageSize = OSPageSize(); + return (addr+pageSize)&(~(pageSize-1)); +} + +/**********************************************************************\ +* Routine Description: * +* * +* This function is called to read memory from the debugee's * +* address space. If the initial read fails, it attempts to read * +* only up to the edge of the page containing "offset". * +* * +\**********************************************************************/ +BOOL SafeReadMemory (TADDR offset, PVOID lpBuffer, ULONG cb, + PULONG lpcbBytesRead) +{ + return FALSE; + // TODO: In-memory PDB? + // std::lock_guard lock(g_processMutex); + + // if (!g_process) + // return FALSE; + + // BOOL bRet = FALSE; + + // SIZE_T bytesRead = 0; + + // bRet = SUCCEEDED(g_process->ReadMemory(TO_CDADDR(offset), cb, (BYTE*)lpBuffer, + // &bytesRead)); + + // if (!bRet) + // { + // cb = (ULONG)(NextOSPageAddress(offset) - offset); + // bRet = SUCCEEDED(g_process->ReadMemory(TO_CDADDR(offset), cb, (BYTE*)lpBuffer, + // &bytesRead)); + // } + + // *lpcbBytesRead = bytesRead; + // return bRet; +} + +std::mutex MIProtocol::m_outMutex; + +void MIProtocol::Printf(const char *fmt, ...) +{ + std::lock_guard lock(m_outMutex); + va_list arg; + + va_start(arg, fmt); + vfprintf(stdout, fmt, arg); + va_end(arg); + + fflush(stdout); +} + +std::string MIProtocol::EscapeMIValue(const std::string &str) +{ + std::string s(str); + + for (std::size_t i = 0; i < s.size(); ++i) + { + int count = 0; + char c = s.at(i); + switch (c) + { + case '\"': count = 1; s.insert(i, count, '\\'); s[i + count] = '\"'; break; + case '\\': count = 1; s.insert(i, count, '\\'); s[i + count] = '\\'; break; + case '\0': count = 1; s.insert(i, count, '\\'); s[i + count] = '0'; break; + case '\a': count = 1; s.insert(i, count, '\\'); s[i + count] = 'a'; break; + case '\b': count = 1; s.insert(i, count, '\\'); s[i + count] = 'b'; break; + case '\f': count = 1; s.insert(i, count, '\\'); s[i + count] = 'f'; break; + case '\n': count = 1; s.insert(i, count, '\\'); s[i + count] = 'n'; break; + case '\r': count = 1; s.insert(i, count, '\\'); s[i + count] = 'r'; break; + case '\t': count = 1; s.insert(i, count, '\\'); s[i + count] = 't'; break; + case '\v': count = 1; s.insert(i, count, '\\'); s[i + count] = 'v'; break; + } + i += count; + } + + return s; +} + +static HRESULT DisableAllSteppersInAppDomain(ICorDebugAppDomain *pAppDomain) +{ + HRESULT Status; + ToRelease steppers; + IfFailRet(pAppDomain->EnumerateSteppers(&steppers)); + + ICorDebugStepper *curStepper; + ULONG steppersFetched; + while (SUCCEEDED(steppers->Next(1, &curStepper, &steppersFetched)) && steppersFetched == 1) + { + ToRelease pStepper(curStepper); + pStepper->Deactivate(); + } + + return S_OK; +} + +HRESULT ManagedDebugger::DisableAllSteppers(ICorDebugProcess *pProcess) +{ + HRESULT Status; + + ToRelease domains; + IfFailRet(pProcess->EnumerateAppDomains(&domains)); + + ICorDebugAppDomain *curDomain; + ULONG domainsFetched; + while (SUCCEEDED(domains->Next(1, &curDomain, &domainsFetched)) && domainsFetched == 1) + { + ToRelease pDomain(curDomain); + DisableAllSteppersInAppDomain(pDomain); + } + return S_OK; +} + +static HRESULT DisableAllBreakpointsAndSteppersInAppDomain(ICorDebugAppDomain *pAppDomain) +{ + HRESULT Status; + + ToRelease breakpoints; + if (SUCCEEDED(pAppDomain->EnumerateBreakpoints(&breakpoints))) + { + ICorDebugBreakpoint *curBreakpoint; + ULONG breakpointsFetched; + while (SUCCEEDED(breakpoints->Next(1, &curBreakpoint, &breakpointsFetched)) && breakpointsFetched == 1) + { + ToRelease pBreakpoint(curBreakpoint); + pBreakpoint->Activate(FALSE); + } + } + + DisableAllSteppersInAppDomain(pAppDomain); + + return S_OK; +} + +HRESULT DisableAllBreakpointsAndSteppers(ICorDebugProcess *pProcess) +{ + HRESULT Status; + + ToRelease domains; + IfFailRet(pProcess->EnumerateAppDomains(&domains)); + + ICorDebugAppDomain *curDomain; + ULONG domainsFetched; + while (SUCCEEDED(domains->Next(1, &curDomain, &domainsFetched)) && domainsFetched == 1) + { + ToRelease pDomain(curDomain); + DisableAllBreakpointsAndSteppersInAppDomain(pDomain); + } + return S_OK; +} + +void ManagedDebugger::SetLastStoppedThread(ICorDebugThread *pThread) +{ + DWORD threadId = 0; + pThread->GetID(&threadId); + + std::lock_guard lock(m_lastStoppedThreadIdMutex); + m_lastStoppedThreadId = threadId; +} + +int ManagedDebugger::GetLastStoppedThreadId() +{ + std::lock_guard lock(m_lastStoppedThreadIdMutex); + return m_lastStoppedThreadId; +} + +static HRESULT GetExceptionInfo(ICorDebugThread *pThread, + std::string &excType, + std::string &excModule) +{ + HRESULT Status; + + ToRelease pExceptionValue; + IfFailRet(pThread->GetCurrentException(&pExceptionValue)); + + TypePrinter::GetTypeOfValue(pExceptionValue, excType); + + ToRelease pFrame; + IfFailRet(pThread->GetActiveFrame(&pFrame)); + if (pFrame == nullptr) + return E_FAIL; + ToRelease pFunc; + IfFailRet(pFrame->GetFunction(&pFunc)); + + ToRelease pModule; + IfFailRet(pFunc->GetModule(&pModule)); + + ToRelease pMDUnknown; + ToRelease pMDImport; + IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown)); + IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMDImport)); + + WCHAR mdName[mdNameLen]; + ULONG nameLen; + IfFailRet(pMDImport->GetScopeProps(mdName, _countof(mdName), &nameLen, nullptr)); + excModule = to_utf8(mdName); + return S_OK; +} + +class ManagedCallback : public ICorDebugManagedCallback, ICorDebugManagedCallback2 +{ + ULONG m_refCount; + ManagedDebugger &m_debugger; +public: + + void HandleEvent(ICorDebugController *controller, const std::string &eventName) + { + std::string text = "Event received: '" + eventName + "'\n"; + m_debugger.m_protocol->EmitOutputEvent(OutputEvent(OutputConsole, text)); + controller->Continue(0); + } + + ManagedCallback(ManagedDebugger &debugger) : m_refCount(1), m_debugger(debugger) {} + virtual ~ManagedCallback() {} + + // IUnknown + + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppInterface) + { + if(riid == __uuidof(ICorDebugManagedCallback)) + { + *ppInterface = static_cast(this); + AddRef(); + return S_OK; + } + else if(riid == __uuidof(ICorDebugManagedCallback2)) + { + *ppInterface = static_cast(this); + AddRef(); + return S_OK; + } + else if(riid == __uuidof(IUnknown)) + { + *ppInterface = static_cast(static_cast(this)); + AddRef(); + return S_OK; + } + else + { + return E_NOINTERFACE; + } + } + + virtual ULONG STDMETHODCALLTYPE AddRef() + { + return InterlockedIncrement((volatile LONG *) &m_refCount); + } + + virtual ULONG STDMETHODCALLTYPE Release() + { + ULONG count = InterlockedDecrement((volatile LONG *) &m_refCount); + if(count == 0) + { + delete this; + } + return count; + } + + // ICorDebugManagedCallback + + virtual HRESULT STDMETHODCALLTYPE Breakpoint( + /* [in] */ ICorDebugAppDomain *pAppDomain, + /* [in] */ ICorDebugThread *pThread, + /* [in] */ ICorDebugBreakpoint *pBreakpoint) + { + if (m_debugger.m_evaluator.IsEvalRunning()) + { + pAppDomain->Continue(0); + return S_OK; + } + + DWORD threadId = 0; + pThread->GetID(&threadId); + + StoppedEvent event(StopBreakpoint, threadId); + m_debugger.m_breakpoints.HitBreakpoint(pThread, event.breakpoint); + + ToRelease pFrame; + if (SUCCEEDED(pThread->GetActiveFrame(&pFrame)) && pFrame != nullptr) + m_debugger.GetFrameLocation(pFrame, threadId, 0, event.frame); + + m_debugger.SetLastStoppedThread(pThread); + m_debugger.m_protocol->EmitStoppedEvent(event); + + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE StepComplete( + /* [in] */ ICorDebugAppDomain *pAppDomain, + /* [in] */ ICorDebugThread *pThread, + /* [in] */ ICorDebugStepper *pStepper, + /* [in] */ CorDebugStepReason reason) + { + DWORD threadId = 0; + pThread->GetID(&threadId); + + StackFrame stackFrame; + ToRelease pFrame; + HRESULT Status = S_FALSE; + if (SUCCEEDED(pThread->GetActiveFrame(&pFrame)) && pFrame != nullptr) + Status = m_debugger.GetFrameLocation(pFrame, threadId, 0, stackFrame); + + const bool no_source = Status == S_FALSE; + + if (m_debugger.IsJustMyCode() && no_source) + { + m_debugger.SetupStep(pThread, Debugger::STEP_OVER); + pAppDomain->Continue(0); + } + else + { + StoppedEvent event(StopStep, threadId); + event.frame = stackFrame; + + m_debugger.SetLastStoppedThread(pThread); + m_debugger.m_protocol->EmitStoppedEvent(event); + } + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE Break( + /* [in] */ ICorDebugAppDomain *pAppDomain, + /* [in] */ ICorDebugThread *thread) { HandleEvent(pAppDomain, "Break"); return S_OK; } + + virtual HRESULT STDMETHODCALLTYPE Exception( + /* [in] */ ICorDebugAppDomain *pAppDomain, + /* [in] */ ICorDebugThread *pThread, + /* [in] */ BOOL unhandled) + { + std::string excType; + std::string excModule; + GetExceptionInfo(pThread, excType, excModule); + + if (unhandled) + { + DWORD threadId = 0; + pThread->GetID(&threadId); + + StackFrame stackFrame; + ToRelease pFrame; + if (SUCCEEDED(pThread->GetActiveFrame(&pFrame)) && pFrame != nullptr) + m_debugger.GetFrameLocation(pFrame, threadId, 0, stackFrame); + + m_debugger.SetLastStoppedThread(pThread); + + std::string details = "An unhandled exception of type '" + excType + "' occurred in " + excModule; + + ToRelease pExceptionValue; + + auto emitFunc = [=](const std::string &message) { + StoppedEvent event(StopException, threadId); + event.text = excType; + event.description = message.empty() ? details : message; + event.frame = stackFrame; + m_debugger.m_protocol->EmitStoppedEvent(event); + }; + + if (FAILED(pThread->GetCurrentException(&pExceptionValue)) || + FAILED(m_debugger.m_evaluator.ObjectToString(pThread, pExceptionValue, emitFunc))) + { + emitFunc(details); + } + } + else + { + std::string text = "Exception thrown: '" + excType + "' in " + excModule + "\n"; + OutputEvent event(OutputConsole, text); + event.source = "target-exception"; + m_debugger.m_protocol->EmitOutputEvent(event); + pAppDomain->Continue(0); + } + + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE EvalComplete( + /* [in] */ ICorDebugAppDomain *pAppDomain, + /* [in] */ ICorDebugThread *pThread, + /* [in] */ ICorDebugEval *pEval) + { + m_debugger.m_evaluator.NotifyEvalComplete(pThread, pEval); + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE EvalException( + /* [in] */ ICorDebugAppDomain *pAppDomain, + /* [in] */ ICorDebugThread *pThread, + /* [in] */ ICorDebugEval *pEval) + { + m_debugger.m_evaluator.NotifyEvalComplete(pThread, pEval); + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE CreateProcess( + /* [in] */ ICorDebugProcess *pProcess) + { + //HandleEvent(pProcess, "CreateProcess"); + m_debugger.NotifyProcessCreated(); + pProcess->Continue(0); + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE ExitProcess( + /* [in] */ ICorDebugProcess *pProcess) + { + m_debugger.m_evaluator.NotifyEvalComplete(nullptr, nullptr); + m_debugger.m_protocol->EmitExitedEvent(ExitedEvent(0)); + m_debugger.NotifyProcessExited(); + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE CreateThread( + /* [in] */ ICorDebugAppDomain *pAppDomain, + /* [in] */ ICorDebugThread *thread) + { + DWORD threadId = 0; + thread->GetID(&threadId); + m_debugger.m_protocol->EmitThreadEvent(ThreadEvent(ThreadStarted, threadId)); + pAppDomain->Continue(0); + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE ExitThread( + /* [in] */ ICorDebugAppDomain *pAppDomain, + /* [in] */ ICorDebugThread *thread) + { + m_debugger.m_evaluator.NotifyEvalComplete(thread, nullptr); + DWORD threadId = 0; + thread->GetID(&threadId); + m_debugger.m_protocol->EmitThreadEvent(ThreadEvent(ThreadExited, threadId)); + pAppDomain->Continue(0); + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE LoadModule( + /* [in] */ ICorDebugAppDomain *pAppDomain, + /* [in] */ ICorDebugModule *pModule) + { + std::string id; + std::string name; + bool symbolsLoaded = false; + CORDB_ADDRESS baseAddress = 0; + ULONG32 size = 0; + + m_debugger.m_modules.TryLoadModuleSymbols(pModule, id, name, symbolsLoaded, baseAddress, size); + + std::stringstream ss; + ss << "id=\"{" << id << "}\"," + << "target-name=\"" << MIProtocol::EscapeMIValue(name) << "\"," + << "host-name=\"" << MIProtocol::EscapeMIValue(name) << "\"," + << "symbols-loaded=\"" << symbolsLoaded << "\"," + << "base-address=\"0x" << std::hex << baseAddress << "\"," + << "size=\"" << std::dec << size << "\""; + MIProtocol::Printf("=library-loaded,%s\n", ss.str().c_str()); + + if (symbolsLoaded) + { + std::vector events; + m_debugger.m_breakpoints.TryResolveBreakpointsForModule(pModule, events); + for (const BreakpointEvent &event : events) + m_debugger.m_protocol->EmitBreakpointEvent(event); + } + + pAppDomain->Continue(0); + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE UnloadModule( + /* [in] */ ICorDebugAppDomain *pAppDomain, + /* [in] */ ICorDebugModule *pModule) { HandleEvent(pAppDomain, "UnloadModule"); return S_OK; } + + virtual HRESULT STDMETHODCALLTYPE LoadClass( + /* [in] */ ICorDebugAppDomain *pAppDomain, + /* [in] */ ICorDebugClass *c) { HandleEvent(pAppDomain, "LoadClass"); return S_OK; } + + virtual HRESULT STDMETHODCALLTYPE UnloadClass( + /* [in] */ ICorDebugAppDomain *pAppDomain, + /* [in] */ ICorDebugClass *c) { HandleEvent(pAppDomain, "UnloadClass"); return S_OK; } + + virtual HRESULT STDMETHODCALLTYPE DebuggerError( + /* [in] */ ICorDebugProcess *pProcess, + /* [in] */ HRESULT errorHR, + /* [in] */ DWORD errorCode) { printf("DebuggerError\n"); return S_OK; } + + virtual HRESULT STDMETHODCALLTYPE LogMessage( + /* [in] */ ICorDebugAppDomain *pAppDomain, + /* [in] */ ICorDebugThread *pThread, + /* [in] */ LONG lLevel, + /* [in] */ WCHAR *pLogSwitchName, + /* [in] */ WCHAR *pMessage) { pAppDomain->Continue(0); return S_OK; } + + virtual HRESULT STDMETHODCALLTYPE LogSwitch( + /* [in] */ ICorDebugAppDomain *pAppDomain, + /* [in] */ ICorDebugThread *pThread, + /* [in] */ LONG lLevel, + /* [in] */ ULONG ulReason, + /* [in] */ WCHAR *pLogSwitchName, + /* [in] */ WCHAR *pParentName) { pAppDomain->Continue(0); return S_OK; } + + virtual HRESULT STDMETHODCALLTYPE CreateAppDomain( + /* [in] */ ICorDebugProcess *pProcess, + /* [in] */ ICorDebugAppDomain *pAppDomain) + { + //HandleEvent(pProcess, "CreateAppDomain"); + pProcess->Continue(0); + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE ExitAppDomain( + /* [in] */ ICorDebugProcess *pProcess, + /* [in] */ ICorDebugAppDomain *pAppDomain) { HandleEvent(pAppDomain, "ExitAppDomain"); return S_OK; } + + virtual HRESULT STDMETHODCALLTYPE LoadAssembly( + /* [in] */ ICorDebugAppDomain *pAppDomain, + /* [in] */ ICorDebugAssembly *pAssembly) + { + //HandleEvent(pAppDomain, "LoadAssembly"); + pAppDomain->Continue(0); + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE UnloadAssembly( + /* [in] */ ICorDebugAppDomain *pAppDomain, + /* [in] */ ICorDebugAssembly *pAssembly) { return S_OK; } + + virtual HRESULT STDMETHODCALLTYPE ControlCTrap( + /* [in] */ ICorDebugProcess *pProcess) { HandleEvent(pProcess, "ControlCTrap"); return S_OK; } + + virtual HRESULT STDMETHODCALLTYPE NameChange( + /* [in] */ ICorDebugAppDomain *pAppDomain, + /* [in] */ ICorDebugThread *pThread) + { + pAppDomain->Continue(0); + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE UpdateModuleSymbols( + /* [in] */ ICorDebugAppDomain *pAppDomain, + /* [in] */ ICorDebugModule *pModule, + /* [in] */ IStream *pSymbolStream) { HandleEvent(pAppDomain, "UpdateModuleSymbols"); return S_OK; } + + virtual HRESULT STDMETHODCALLTYPE EditAndContinueRemap( + /* [in] */ ICorDebugAppDomain *pAppDomain, + /* [in] */ ICorDebugThread *pThread, + /* [in] */ ICorDebugFunction *pFunction, + /* [in] */ BOOL fAccurate) { return S_OK; } + + virtual HRESULT STDMETHODCALLTYPE BreakpointSetError( + /* [in] */ ICorDebugAppDomain *pAppDomain, + /* [in] */ ICorDebugThread *pThread, + /* [in] */ ICorDebugBreakpoint *pBreakpoint, + /* [in] */ DWORD dwError) {return S_OK; } + + + // ICorDebugManagedCallback2 + + virtual HRESULT STDMETHODCALLTYPE FunctionRemapOpportunity( + /* [in] */ ICorDebugAppDomain *pAppDomain, + /* [in] */ ICorDebugThread *pThread, + /* [in] */ ICorDebugFunction *pOldFunction, + /* [in] */ ICorDebugFunction *pNewFunction, + /* [in] */ ULONG32 oldILOffset) {return S_OK; } + + virtual HRESULT STDMETHODCALLTYPE CreateConnection( + /* [in] */ ICorDebugProcess *pProcess, + /* [in] */ CONNID dwConnectionId, + /* [in] */ WCHAR *pConnName) { HandleEvent(pProcess, "CreateConnection"); return S_OK; } + + virtual HRESULT STDMETHODCALLTYPE ChangeConnection( + /* [in] */ ICorDebugProcess *pProcess, + /* [in] */ CONNID dwConnectionId) { HandleEvent(pProcess, "ChangeConnection"); return S_OK; } + + virtual HRESULT STDMETHODCALLTYPE DestroyConnection( + /* [in] */ ICorDebugProcess *pProcess, + /* [in] */ CONNID dwConnectionId) {return S_OK; } + + virtual HRESULT STDMETHODCALLTYPE Exception( + /* [in] */ ICorDebugAppDomain *pAppDomain, + /* [in] */ ICorDebugThread *pThread, + /* [in] */ ICorDebugFrame *pFrame, + /* [in] */ ULONG32 nOffset, + /* [in] */ CorDebugExceptionCallbackType dwEventType, + /* [in] */ DWORD dwFlags) + { + // const char *cbTypeName; + // switch(dwEventType) + // { + // case DEBUG_EXCEPTION_FIRST_CHANCE: cbTypeName = "FIRST_CHANCE"; break; + // case DEBUG_EXCEPTION_USER_FIRST_CHANCE: cbTypeName = "USER_FIRST_CHANCE"; break; + // case DEBUG_EXCEPTION_CATCH_HANDLER_FOUND: cbTypeName = "CATCH_HANDLER_FOUND"; break; + // case DEBUG_EXCEPTION_UNHANDLED: cbTypeName = "UNHANDLED"; break; + // default: cbTypeName = "?"; break; + // } + // ManagedDebugger::Printf("*stopped,reason=\"exception-received2\",exception-stage=\"%s\"\n", + // cbTypeName); + pAppDomain->Continue(0); + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE ExceptionUnwind( + /* [in] */ ICorDebugAppDomain *pAppDomain, + /* [in] */ ICorDebugThread *pThread, + /* [in] */ CorDebugExceptionUnwindCallbackType dwEventType, + /* [in] */ DWORD dwFlags) {return S_OK; } + + virtual HRESULT STDMETHODCALLTYPE FunctionRemapComplete( + /* [in] */ ICorDebugAppDomain *pAppDomain, + /* [in] */ ICorDebugThread *pThread, + /* [in] */ ICorDebugFunction *pFunction) {return S_OK; } + + virtual HRESULT STDMETHODCALLTYPE MDANotification( + /* [in] */ ICorDebugController *pController, + /* [in] */ ICorDebugThread *pThread, + /* [in] */ ICorDebugMDA *pMDA) { HandleEvent(pController, "MDANotification"); return S_OK; } +}; + +ManagedDebugger::ManagedDebugger() : + m_processAttachedState(ProcessUnattached), + m_evaluator(m_modules), + m_breakpoints(m_modules), + m_variables(m_evaluator), + m_managedCallback(new ManagedCallback(*this)), + m_pDebug(nullptr), + m_pProcess(nullptr), + m_justMyCode(true), + m_startupReady(false), + m_startupResult(S_OK), + m_unregisterToken(nullptr), + m_processId(0) +{ +} + +ManagedDebugger::~ManagedDebugger() +{ +} + +HRESULT ManagedDebugger::SetupStep(ICorDebugThread *pThread, Debugger::StepType stepType) +{ + HRESULT Status; + + ToRelease pStepper; + IfFailRet(pThread->CreateStepper(&pStepper)); + + CorDebugIntercept mask = (CorDebugIntercept)(INTERCEPT_ALL & ~(INTERCEPT_SECURITY | INTERCEPT_CLASS_INIT)); + IfFailRet(pStepper->SetInterceptMask(mask)); + + CorDebugUnmappedStop stopMask = STOP_NONE; + IfFailRet(pStepper->SetUnmappedStopMask(stopMask)); + + ToRelease pStepper2; + IfFailRet(pStepper->QueryInterface(IID_ICorDebugStepper2, (LPVOID *)&pStepper2)); + + IfFailRet(pStepper2->SetJMC(IsJustMyCode())); + + if (stepType == STEP_OUT) + { + IfFailRet(pStepper->StepOut()); + return S_OK; + } + + BOOL bStepIn = stepType == STEP_IN; + + COR_DEBUG_STEP_RANGE range; + if (SUCCEEDED(m_modules.GetStepRangeFromCurrentIP(pThread, &range))) + { + IfFailRet(pStepper->StepRange(bStepIn, &range, 1)); + } else { + IfFailRet(pStepper->Step(bStepIn)); + } + + return S_OK; +} + +HRESULT ManagedDebugger::StepCommand(int threadId, StepType stepType) +{ + HRESULT Status; + ToRelease pThread; + IfFailRet(m_pProcess->GetThread(threadId, &pThread)); + DisableAllSteppers(m_pProcess); + IfFailRet(SetupStep(pThread, stepType)); + IfFailRet(m_pProcess->Continue(0)); + return S_OK; +} + +HRESULT ManagedDebugger::Continue() +{ + if (!m_pProcess) + return E_FAIL; + return m_pProcess->Continue(0); +} + +HRESULT ManagedDebugger::Pause() +{ + if (!m_pProcess) + return E_FAIL; + HRESULT Status = m_pProcess->Stop(0); + if (Status == S_OK) + m_protocol->EmitStoppedEvent(StoppedEvent(StopPause, 0)); + return Status; +} + +HRESULT ManagedDebugger::GetThreads(std::vector &threads) +{ + if (!m_pProcess) + return E_FAIL; + return GetThreadsState(m_pProcess, threads); +} + +HRESULT ManagedDebugger::GetStackTrace(int threadId, int lowFrame, int highFrame, std::vector &stackFrames) +{ + HRESULT Status; + if (!m_pProcess) + return E_FAIL; + ToRelease pThread; + IfFailRet(m_pProcess->GetThread(threadId, &pThread)); + return GetStackTrace(pThread, lowFrame, highFrame, stackFrames); +} + +VOID ManagedDebugger::StartupCallback(IUnknown *pCordb, PVOID parameter, HRESULT hr) +{ + ManagedDebugger *self = static_cast(parameter); + + std::unique_lock lock(self->m_startupMutex); + + self->m_startupResult = FAILED(hr) ? hr : self->Startup(pCordb, self->m_processId); + self->m_startupReady = true; + + if (self->m_unregisterToken) + { + UnregisterForRuntimeStartup(self->m_unregisterToken); + self->m_unregisterToken = nullptr; + } + + lock.unlock(); + self->m_startupCV.notify_one(); +} + +// From dbgshim.cpp +static bool AreAllHandlesValid(HANDLE *handleArray, DWORD arrayLength) +{ + for (DWORD i = 0; i < arrayLength; i++) + { + HANDLE h = handleArray[i]; + if (h == INVALID_HANDLE_VALUE) + { + return false; + } + } + return true; +} + +static HRESULT InternalEnumerateCLRs(int pid, HANDLE **ppHandleArray, LPWSTR **ppStringArray, DWORD *pdwArrayLength) +{ + int numTries = 0; + HRESULT hr; + + while (numTries < 25) + { + hr = EnumerateCLRs(pid, ppHandleArray, ppStringArray, pdwArrayLength); + + // EnumerateCLRs uses the OS API CreateToolhelp32Snapshot which can return ERROR_BAD_LENGTH or + // ERROR_PARTIAL_COPY. If we get either of those, we try wait 1/10th of a second try again (that + // is the recommendation of the OS API owners). + if ((hr != HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY)) && (hr != HRESULT_FROM_WIN32(ERROR_BAD_LENGTH))) + { + // Just return any other error or if no handles were found (which means the coreclr module wasn't found yet). + if (FAILED(hr) || *ppHandleArray == NULL || *pdwArrayLength <= 0) + { + return hr; + } + // If EnumerateCLRs succeeded but any of the handles are INVALID_HANDLE_VALUE, then sleep and retry + // also. This fixes a race condition where dbgshim catches the coreclr module just being loaded but + // before g_hContinueStartupEvent has been initialized. + if (AreAllHandlesValid(*ppHandleArray, *pdwArrayLength)) + { + return hr; + } + // Clean up memory allocated in EnumerateCLRs since this path it succeeded + CloseCLREnumeration(*ppHandleArray, *ppStringArray, *pdwArrayLength); + + *ppHandleArray = NULL; + *ppStringArray = NULL; + *pdwArrayLength = 0; + } + + // Sleep and retry enumerating the runtimes + Sleep(100); + numTries++; + + // if (m_canceled) + // { + // break; + // } + } + + // Indicate a timeout + hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); + + return hr; +} + +static std::string GetCLRPath(int pid) +{ + HANDLE* pHandleArray; + LPWSTR* pStringArray; + DWORD dwArrayLength; + if (FAILED(InternalEnumerateCLRs(pid, &pHandleArray, &pStringArray, &dwArrayLength)) || dwArrayLength == 0) + return std::string(); + + std::string result = to_utf8(pStringArray[0]); + + CloseCLREnumeration(pHandleArray, pStringArray, dwArrayLength); + + return result; +} + +HRESULT ManagedDebugger::Startup(IUnknown *punk, int pid) +{ + HRESULT Status; + + Cleanup(); + + ToRelease pCorDebug; + IfFailRet(punk->QueryInterface(IID_ICorDebug, (void **)&pCorDebug)); + + IfFailRet(pCorDebug->Initialize()); + + Status = pCorDebug->SetManagedHandler(m_managedCallback); + if (FAILED(Status)) + { + pCorDebug->Terminate(); + return Status; + } + + if (m_clrPath.empty()) + m_clrPath = GetCLRPath(pid); + + SymbolReader::SetCoreCLRPath(m_clrPath); + + ToRelease pProcess; + Status = pCorDebug->DebugActiveProcess(pid, FALSE, &pProcess); + if (FAILED(Status)) + { + pCorDebug->Terminate(); + return Status; + } + + m_pProcess = pProcess.Detach(); + m_pDebug = pCorDebug.Detach(); + + m_processId = pid; + + return S_OK; +} + +HRESULT ManagedDebugger::RunProcess(std::string fileExec, std::vector execArgs) +{ + static const auto startupCallbackWaitTimeout = std::chrono::milliseconds(5000); + HRESULT Status; + + IfFailRet(CheckNoProcess()); + + std::stringstream ss; + ss << "\"" << fileExec << "\""; + for (std::string &arg : execArgs) + { + ss << " \"" << MIProtocol::EscapeMIValue(arg) << "\""; + } + std::string cmdString = ss.str(); + std::unique_ptr cmd(new WCHAR[cmdString.size() + 1]); + + MultiByteToWideChar(CP_UTF8, 0, cmdString.c_str(), cmdString.size() + 1, &cmd[0], cmdString.size() + 1); + + m_startupReady = false; + m_clrPath.clear(); + + BOOL bSuspendProcess = TRUE; + LPVOID lpEnvironment = nullptr; // as current + LPCWSTR lpCurrentDirectory = nullptr; // as current + HANDLE resumeHandle; + IfFailRet(CreateProcessForLaunch(&cmd[0], bSuspendProcess, lpEnvironment, lpCurrentDirectory, &m_processId, &resumeHandle)); + + IfFailRet(RegisterForRuntimeStartup(m_processId, ManagedDebugger::StartupCallback, this, &m_unregisterToken)); + + // Resume the process so that StartupCallback can run + IfFailRet(ResumeProcess(resumeHandle)); + CloseResumeHandle(resumeHandle); + + // Wait for ManagedDebugger::StartupCallback to complete + + // FIXME: if the process exits too soon the ManagedDebugger::StartupCallback() + // is never called (bug in dbgshim?). + // The workaround is to wait with timeout. + auto now = std::chrono::system_clock::now(); + + std::unique_lock lock(m_startupMutex); + if (!m_startupCV.wait_until(lock, now + startupCallbackWaitTimeout, [this](){return m_startupReady;})) + { + // Timed out + UnregisterForRuntimeStartup(m_unregisterToken); + m_unregisterToken = nullptr; + return E_FAIL; + } + + return m_startupResult; +} + +HRESULT ManagedDebugger::CheckNoProcess() +{ + if (m_pProcess || m_pDebug) + { + std::lock_guard lock(m_processAttachedMutex); + if (m_processAttachedState == ProcessAttached) + return E_FAIL; // Already attached + m_processAttachedMutex.unlock(); + + TerminateProcess(); + } + return S_OK; +} + +HRESULT ManagedDebugger::DetachFromProcess() +{ + if (!m_pProcess || !m_pDebug) + return E_FAIL; + + if (SUCCEEDED(m_pProcess->Stop(0))) + { + m_breakpoints.DeleteAllBreakpoints(); + DisableAllBreakpointsAndSteppers(m_pProcess); + m_pProcess->Detach(); + } + + Cleanup(); + + m_pProcess->Release(); + m_pProcess = nullptr; + + m_pDebug->Terminate(); + m_pDebug = nullptr; + + return S_OK; +} + +HRESULT ManagedDebugger::TerminateProcess() +{ + if (!m_pProcess || !m_pDebug) + return E_FAIL; + + if (SUCCEEDED(m_pProcess->Stop(0))) + { + DisableAllBreakpointsAndSteppers(m_pProcess); + //pProcess->Detach(); + } + + Cleanup(); + + m_pProcess->Terminate(0); + WaitProcessExited(); + + m_pProcess->Release(); + m_pProcess = nullptr; + + m_pDebug->Terminate(); + m_pDebug = nullptr; + + return S_OK; +} + +void ManagedDebugger::Cleanup() +{ + m_modules.CleanupAllModules(); + m_evaluator.Cleanup(); + m_protocol->Cleanup(); + // TODO: Cleanup libcoreclr.so instance +} + +HRESULT ManagedDebugger::AttachToProcess(int pid) +{ + HRESULT Status; + + IfFailRet(CheckNoProcess()); + + std::string m_clrPath = GetCLRPath(pid); + if (m_clrPath.empty()) + return E_INVALIDARG; // Unable to find libcoreclr.so + + WCHAR szModuleName[MAX_LONGPATH]; + MultiByteToWideChar(CP_UTF8, 0, m_clrPath.c_str(), m_clrPath.size() + 1, szModuleName, MAX_LONGPATH); + + WCHAR pBuffer[100]; + DWORD dwLength; + IfFailRet(CreateVersionStringFromModule( + pid, + szModuleName, + pBuffer, + _countof(pBuffer), + &dwLength)); + + ToRelease pCordb; + + IfFailRet(CreateDebuggingInterfaceFromVersionEx(4, pBuffer, &pCordb)); + + m_unregisterToken = nullptr; + return Startup(pCordb, pid); +} diff --git a/src/debug/netcoredbg/modules.h b/src/debug/netcoredbg/modules.h index 954c18f..2a37d7c 100644 --- a/src/debug/netcoredbg/modules.h +++ b/src/debug/netcoredbg/modules.h @@ -4,7 +4,7 @@ #include #include - +#include class SymbolReader; diff --git a/src/debug/netcoredbg/protocol.h b/src/debug/netcoredbg/protocol.h index 50c358e..0f11a41 100644 --- a/src/debug/netcoredbg/protocol.h +++ b/src/debug/netcoredbg/protocol.h @@ -4,9 +4,9 @@ #pragma once -#include "platform.h" #include #include +#include "platform.h" // From https://github.com/Microsoft/vscode-debugadapter-node/blob/master/protocol/src/debugProtocol.ts