Refactor ManagedDebugger implementation into separate file
authorIgor Kulaychuk <i.kulaychuk@samsung.com>
Thu, 18 Jan 2018 19:53:45 +0000 (22:53 +0300)
committerIgor Kulaychuk <i.kulaychuk@samsung.com>
Thu, 18 Jan 2018 19:54:07 +0000 (22:54 +0300)
src/debug/netcoredbg/CMakeLists.txt
src/debug/netcoredbg/main.cpp
src/debug/netcoredbg/manageddebugger.cpp [new file with mode: 0644]
src/debug/netcoredbg/modules.h
src/debug/netcoredbg/protocol.h

index fa3aa813721aacaccfc5dd70dbc4456eed0459b3..b6b3c1217e63cd94fa17880df925f9fa92b3b64d 100644 (file)
@@ -1,5 +1,6 @@
 set(netcoredbg_SRC
     main.cpp
+    manageddebugger.cpp
     symbolreader.cpp
     platform.cpp
     breakpoints.cpp
index 5cc345635d9d321f95d4d1e1f654c3ca3be533b4..73e16fe89f3f3cff6de1a42d33daf81e9f933389 100644 (file)
 // See the LICENSE file in the project root for more information.
 
 #include "common.h"
-
-#include <sstream>
-#include <mutex>
-#include <condition_variable>
-#include <memory>
-#include <unordered_map>
-#include <vector>
-#include <list>
-#include <chrono>
-
-#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<std::mutex> lock(m_processAttachedMutex);
-    m_processAttachedState = ProcessAttached;
-}
-
-void ManagedDebugger::NotifyProcessExited()
-{
-    std::lock_guard<std::mutex> lock(m_processAttachedMutex);
-    m_processAttachedState = ProcessUnattached;
-    m_processAttachedMutex.unlock();
-    m_processAttachedCV.notify_one();
-}
-
-void ManagedDebugger::WaitProcessExited()
-{
-    std::unique_lock<std::mutex> 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<std::mutex> 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<std::mutex> 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<ICorDebugStepperEnum> steppers;
-    IfFailRet(pAppDomain->EnumerateSteppers(&steppers));
-
-    ICorDebugStepper *curStepper;
-    ULONG steppersFetched;
-    while (SUCCEEDED(steppers->Next(1, &curStepper, &steppersFetched)) && steppersFetched == 1)
-    {
-        ToRelease<ICorDebugStepper> pStepper(curStepper);
-        pStepper->Deactivate();
-    }
-
-    return S_OK;
-}
-
-HRESULT ManagedDebugger::DisableAllSteppers(ICorDebugProcess *pProcess)
-{
-    HRESULT Status;
-
-    ToRelease<ICorDebugAppDomainEnum> domains;
-    IfFailRet(pProcess->EnumerateAppDomains(&domains));
-
-    ICorDebugAppDomain *curDomain;
-    ULONG domainsFetched;
-    while (SUCCEEDED(domains->Next(1, &curDomain, &domainsFetched)) && domainsFetched == 1)
-    {
-        ToRelease<ICorDebugAppDomain> pDomain(curDomain);
-        DisableAllSteppersInAppDomain(pDomain);
-    }
-    return S_OK;
-}
-
-static HRESULT DisableAllBreakpointsAndSteppersInAppDomain(ICorDebugAppDomain *pAppDomain)
-{
-    HRESULT Status;
-
-    ToRelease<ICorDebugBreakpointEnum> breakpoints;
-    if (SUCCEEDED(pAppDomain->EnumerateBreakpoints(&breakpoints)))
-    {
-        ICorDebugBreakpoint *curBreakpoint;
-        ULONG breakpointsFetched;
-        while (SUCCEEDED(breakpoints->Next(1, &curBreakpoint, &breakpointsFetched)) && breakpointsFetched == 1)
-        {
-            ToRelease<ICorDebugBreakpoint> pBreakpoint(curBreakpoint);
-            pBreakpoint->Activate(FALSE);
-        }
-    }
-
-    DisableAllSteppersInAppDomain(pAppDomain);
-
-    return S_OK;
-}
-
-HRESULT DisableAllBreakpointsAndSteppers(ICorDebugProcess *pProcess)
-{
-    HRESULT Status;
-
-    ToRelease<ICorDebugAppDomainEnum> domains;
-    IfFailRet(pProcess->EnumerateAppDomains(&domains));
-
-    ICorDebugAppDomain *curDomain;
-    ULONG domainsFetched;
-    while (SUCCEEDED(domains->Next(1, &curDomain, &domainsFetched)) && domainsFetched == 1)
-    {
-        ToRelease<ICorDebugAppDomain> pDomain(curDomain);
-        DisableAllBreakpointsAndSteppersInAppDomain(pDomain);
-    }
-    return S_OK;
-}
-
-void ManagedDebugger::SetLastStoppedThread(ICorDebugThread *pThread)
-{
-    DWORD threadId = 0;
-    pThread->GetID(&threadId);
-
-    std::lock_guard<std::mutex> lock(m_lastStoppedThreadIdMutex);
-    m_lastStoppedThreadId = threadId;
-}
-
-int ManagedDebugger::GetLastStoppedThreadId()
-{
-    std::lock_guard<std::mutex> lock(m_lastStoppedThreadIdMutex);
-    return m_lastStoppedThreadId;
-}
-
-static HRESULT GetExceptionInfo(ICorDebugThread *pThread,
-                                std::string &excType,
-                                std::string &excModule)
-{
-    HRESULT Status;
-
-    ToRelease<ICorDebugValue> pExceptionValue;
-    IfFailRet(pThread->GetCurrentException(&pExceptionValue));
-
-    TypePrinter::GetTypeOfValue(pExceptionValue, excType);
-
-    ToRelease<ICorDebugFrame> pFrame;
-    IfFailRet(pThread->GetActiveFrame(&pFrame));
-    if (pFrame == nullptr)
-        return E_FAIL;
-    ToRelease<ICorDebugFunction> pFunc;
-    IfFailRet(pFrame->GetFunction(&pFunc));
-
-    ToRelease<ICorDebugModule> pModule;
-    IfFailRet(pFunc->GetModule(&pModule));
-
-    ToRelease<IUnknown> pMDUnknown;
-    ToRelease<IMetaDataImport> 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<ICorDebugManagedCallback*>(this);
-                AddRef();
-                return S_OK;
-            }
-            else if(riid == __uuidof(ICorDebugManagedCallback2))
-            {
-                *ppInterface = static_cast<ICorDebugManagedCallback2*>(this);
-                AddRef();
-                return S_OK;
-            }
-            else if(riid == __uuidof(IUnknown))
-            {
-                *ppInterface = static_cast<IUnknown*>(static_cast<ICorDebugManagedCallback*>(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<ICorDebugFrame> 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<ICorDebugFrame> 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<ICorDebugFrame> 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<ICorDebugValue> 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<BreakpointEvent> 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<ICorDebugStepper> 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<ICorDebugStepper2> 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<ICorDebugThread> 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<Thread> &threads)
-{
-    if (!m_pProcess)
-        return E_FAIL;
-    return GetThreadsState(m_pProcess, threads);
-}
-
-HRESULT ManagedDebugger::GetStackTrace(int threadId, int lowFrame, int highFrame, std::vector<StackFrame> &stackFrames)
-{
-    HRESULT Status;
-    if (!m_pProcess)
-        return E_FAIL;
-    ToRelease<ICorDebugThread> 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<ManagedDebugger*>(parameter);
-
-    std::unique_lock<std::mutex> 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<ICorDebug> 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<ICorDebugProcess> 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<std::string> 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<WCHAR[]> 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<std::mutex> 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<std::mutex> 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<IUnknown> 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 (file)
index 0000000..b88d50b
--- /dev/null
@@ -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 <sstream>
+#include <mutex>
+#include <condition_variable>
+#include <memory>
+#include <unordered_map>
+#include <vector>
+#include <list>
+#include <chrono>
+
+#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<std::mutex> lock(m_processAttachedMutex);
+    m_processAttachedState = ProcessAttached;
+}
+
+void ManagedDebugger::NotifyProcessExited()
+{
+    std::lock_guard<std::mutex> lock(m_processAttachedMutex);
+    m_processAttachedState = ProcessUnattached;
+    m_processAttachedMutex.unlock();
+    m_processAttachedCV.notify_one();
+}
+
+void ManagedDebugger::WaitProcessExited()
+{
+    std::unique_lock<std::mutex> 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<std::mutex> 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<std::mutex> 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<ICorDebugStepperEnum> steppers;
+    IfFailRet(pAppDomain->EnumerateSteppers(&steppers));
+
+    ICorDebugStepper *curStepper;
+    ULONG steppersFetched;
+    while (SUCCEEDED(steppers->Next(1, &curStepper, &steppersFetched)) && steppersFetched == 1)
+    {
+        ToRelease<ICorDebugStepper> pStepper(curStepper);
+        pStepper->Deactivate();
+    }
+
+    return S_OK;
+}
+
+HRESULT ManagedDebugger::DisableAllSteppers(ICorDebugProcess *pProcess)
+{
+    HRESULT Status;
+
+    ToRelease<ICorDebugAppDomainEnum> domains;
+    IfFailRet(pProcess->EnumerateAppDomains(&domains));
+
+    ICorDebugAppDomain *curDomain;
+    ULONG domainsFetched;
+    while (SUCCEEDED(domains->Next(1, &curDomain, &domainsFetched)) && domainsFetched == 1)
+    {
+        ToRelease<ICorDebugAppDomain> pDomain(curDomain);
+        DisableAllSteppersInAppDomain(pDomain);
+    }
+    return S_OK;
+}
+
+static HRESULT DisableAllBreakpointsAndSteppersInAppDomain(ICorDebugAppDomain *pAppDomain)
+{
+    HRESULT Status;
+
+    ToRelease<ICorDebugBreakpointEnum> breakpoints;
+    if (SUCCEEDED(pAppDomain->EnumerateBreakpoints(&breakpoints)))
+    {
+        ICorDebugBreakpoint *curBreakpoint;
+        ULONG breakpointsFetched;
+        while (SUCCEEDED(breakpoints->Next(1, &curBreakpoint, &breakpointsFetched)) && breakpointsFetched == 1)
+        {
+            ToRelease<ICorDebugBreakpoint> pBreakpoint(curBreakpoint);
+            pBreakpoint->Activate(FALSE);
+        }
+    }
+
+    DisableAllSteppersInAppDomain(pAppDomain);
+
+    return S_OK;
+}
+
+HRESULT DisableAllBreakpointsAndSteppers(ICorDebugProcess *pProcess)
+{
+    HRESULT Status;
+
+    ToRelease<ICorDebugAppDomainEnum> domains;
+    IfFailRet(pProcess->EnumerateAppDomains(&domains));
+
+    ICorDebugAppDomain *curDomain;
+    ULONG domainsFetched;
+    while (SUCCEEDED(domains->Next(1, &curDomain, &domainsFetched)) && domainsFetched == 1)
+    {
+        ToRelease<ICorDebugAppDomain> pDomain(curDomain);
+        DisableAllBreakpointsAndSteppersInAppDomain(pDomain);
+    }
+    return S_OK;
+}
+
+void ManagedDebugger::SetLastStoppedThread(ICorDebugThread *pThread)
+{
+    DWORD threadId = 0;
+    pThread->GetID(&threadId);
+
+    std::lock_guard<std::mutex> lock(m_lastStoppedThreadIdMutex);
+    m_lastStoppedThreadId = threadId;
+}
+
+int ManagedDebugger::GetLastStoppedThreadId()
+{
+    std::lock_guard<std::mutex> lock(m_lastStoppedThreadIdMutex);
+    return m_lastStoppedThreadId;
+}
+
+static HRESULT GetExceptionInfo(ICorDebugThread *pThread,
+                                std::string &excType,
+                                std::string &excModule)
+{
+    HRESULT Status;
+
+    ToRelease<ICorDebugValue> pExceptionValue;
+    IfFailRet(pThread->GetCurrentException(&pExceptionValue));
+
+    TypePrinter::GetTypeOfValue(pExceptionValue, excType);
+
+    ToRelease<ICorDebugFrame> pFrame;
+    IfFailRet(pThread->GetActiveFrame(&pFrame));
+    if (pFrame == nullptr)
+        return E_FAIL;
+    ToRelease<ICorDebugFunction> pFunc;
+    IfFailRet(pFrame->GetFunction(&pFunc));
+
+    ToRelease<ICorDebugModule> pModule;
+    IfFailRet(pFunc->GetModule(&pModule));
+
+    ToRelease<IUnknown> pMDUnknown;
+    ToRelease<IMetaDataImport> 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<ICorDebugManagedCallback*>(this);
+                AddRef();
+                return S_OK;
+            }
+            else if(riid == __uuidof(ICorDebugManagedCallback2))
+            {
+                *ppInterface = static_cast<ICorDebugManagedCallback2*>(this);
+                AddRef();
+                return S_OK;
+            }
+            else if(riid == __uuidof(IUnknown))
+            {
+                *ppInterface = static_cast<IUnknown*>(static_cast<ICorDebugManagedCallback*>(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<ICorDebugFrame> 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<ICorDebugFrame> 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<ICorDebugFrame> 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<ICorDebugValue> 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<BreakpointEvent> 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<ICorDebugStepper> 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<ICorDebugStepper2> 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<ICorDebugThread> 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<Thread> &threads)
+{
+    if (!m_pProcess)
+        return E_FAIL;
+    return GetThreadsState(m_pProcess, threads);
+}
+
+HRESULT ManagedDebugger::GetStackTrace(int threadId, int lowFrame, int highFrame, std::vector<StackFrame> &stackFrames)
+{
+    HRESULT Status;
+    if (!m_pProcess)
+        return E_FAIL;
+    ToRelease<ICorDebugThread> 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<ManagedDebugger*>(parameter);
+
+    std::unique_lock<std::mutex> 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<ICorDebug> 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<ICorDebugProcess> 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<std::string> 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<WCHAR[]> 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<std::mutex> 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<std::mutex> 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<IUnknown> pCordb;
+
+    IfFailRet(CreateDebuggingInterfaceFromVersionEx(4, pBuffer, &pCordb));
+
+    m_unregisterToken = nullptr;
+    return Startup(pCordb, pid);
+}
index 954c18fdca6c517b7f852ef9ab6bf56d7e1148ed..2a37d7c2cbf4e806810e92bebf1f4d9a8822c318 100644 (file)
@@ -4,7 +4,7 @@
 
 #include <unordered_map>
 #include <mutex>
-
+#include <memory>
 
 class SymbolReader;
 
index 50c358e519a2a2d6bf66f2415fdfd6ab75411956..0f11a4170b25d171e1e4ce84729dff98f82fbed4 100644 (file)
@@ -4,9 +4,9 @@
 
 #pragma once
 
-#include "platform.h"
 #include <string>
 #include <vector>
+#include "platform.h"
 
 // From https://github.com/Microsoft/vscode-debugadapter-node/blob/master/protocol/src/debugProtocol.ts