From 5f32d29911226eeeb0c2a7623a6d4fe28edeaee9 Mon Sep 17 00:00:00 2001 From: Igor Kulaychuk Date: Wed, 31 Jan 2018 00:38:26 +0300 Subject: [PATCH] Add support for stopAtEntry option --- src/debug/netcoredbg/breakpoints.cpp | 168 ++++++++++++++++++++++++++++++- src/debug/netcoredbg/manageddebugger.cpp | 11 +- src/debug/netcoredbg/manageddebugger.h | 19 +++- src/debug/netcoredbg/modules.h | 3 +- src/debug/netcoredbg/vscodeprotocol.cpp | 2 +- 5 files changed, 194 insertions(+), 9 deletions(-) diff --git a/src/debug/netcoredbg/breakpoints.cpp b/src/debug/netcoredbg/breakpoints.cpp index 84d5bbc..321b926 100644 --- a/src/debug/netcoredbg/breakpoints.cpp +++ b/src/debug/netcoredbg/breakpoints.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "manageddebugger.h" @@ -30,10 +31,20 @@ void Breakpoints::ManagedBreakpoint::ToBreakpoint(Breakpoint &breakpoint) breakpoint.hitCount = this->times; } -HRESULT Breakpoints::HitBreakpoint(ICorDebugThread *pThread, Breakpoint &breakpoint) +HRESULT Breakpoints::HitBreakpoint( + ICorDebugThread *pThread, + ICorDebugBreakpoint *pBreakpoint, + Breakpoint &breakpoint, + bool &atEntry) { + std::lock_guard lock(m_breakpointsMutex); + HRESULT Status; + atEntry = HitEntry(pThread, pBreakpoint); + if (atEntry) + return S_OK; + ULONG32 ilOffset; Modules::SequencePoint sp; mdMethodDef methodToken; @@ -46,8 +57,6 @@ HRESULT Breakpoints::HitBreakpoint(ICorDebugThread *pThread, Breakpoint &breakpo IfFailRet(m_modules.GetFrameILAndSequencePoint(pFrame, ilOffset, sp)); - std::lock_guard lock(m_breakpointsMutex); - auto breakpoints = m_breakpoints.find(sp.document); if (breakpoints == m_breakpoints.end()) return E_FAIL; @@ -71,6 +80,64 @@ HRESULT Breakpoints::HitBreakpoint(ICorDebugThread *pThread, Breakpoint &breakpo return E_FAIL; } +static HRESULT IsSameFunctionBreakpoint( + ICorDebugFunctionBreakpoint *pBreakpoint1, + ICorDebugFunctionBreakpoint *pBreakpoint2) +{ + HRESULT Status; + + if (!pBreakpoint1 || !pBreakpoint2) + return E_FAIL; + + ULONG32 nOffset1; + ULONG32 nOffset2; + IfFailRet(pBreakpoint1->GetOffset(&nOffset1)); + IfFailRet(pBreakpoint2->GetOffset(&nOffset2)); + + if (nOffset1 != nOffset2) + return E_FAIL; + + ToRelease pFunction1; + ToRelease pFunction2; + IfFailRet(pBreakpoint1->GetFunction(&pFunction1)); + IfFailRet(pBreakpoint2->GetFunction(&pFunction2)); + + mdMethodDef methodDef1; + mdMethodDef methodDef2; + IfFailRet(pFunction1->GetToken(&methodDef1)); + IfFailRet(pFunction2->GetToken(&methodDef2)); + + if (methodDef1 != methodDef2) + return E_FAIL; + + ToRelease pModule1; + ToRelease pModule2; + IfFailRet(pFunction1->GetModule(&pModule1)); + IfFailRet(pFunction2->GetModule(&pModule2)); + + if (Modules::GetModuleFileName(pModule1) != Modules::GetModuleFileName(pModule2)) + return E_FAIL; + + return S_OK; +} + +bool Breakpoints::HitEntry(ICorDebugThread *pThread, ICorDebugBreakpoint *pBreakpoint) +{ + if (!m_stopAtEntry) + return false; + + ToRelease pFunctionBreakpoint; + if (FAILED(pBreakpoint->QueryInterface(IID_ICorDebugFunctionBreakpoint, (LPVOID*) &pFunctionBreakpoint))) + return false; + + if (FAILED(IsSameFunctionBreakpoint(pFunctionBreakpoint, m_entryBreakpoint))) + return false; + + m_entryBreakpoint->Activate(0); + m_entryBreakpoint.Release(); + return true; +} + void ManagedDebugger::InsertExceptionBreakpoint(const std::string &name, Breakpoint &breakpoint) { m_breakpoints.InsertExceptionBreakpoint(name, breakpoint); @@ -87,6 +154,10 @@ void Breakpoints::DeleteAllBreakpoints() std::lock_guard lock(m_breakpointsMutex); m_breakpoints.clear(); + + if (m_entryBreakpoint) + m_entryBreakpoint.Release(); + m_entryPoint = mdMethodDefNil; } HRESULT Breakpoints::ResolveBreakpointInModule(ICorDebugModule *pModule, ManagedBreakpoint &bp) @@ -126,6 +197,95 @@ HRESULT Breakpoints::ResolveBreakpointInModule(ICorDebugModule *pModule, Managed return S_OK; } +void Breakpoints::SetStopAtEntry(bool stopAtEntry) +{ + std::lock_guard lock(m_breakpointsMutex); + m_stopAtEntry = stopAtEntry; +} + +static mdMethodDef GetEntryPointTokenFromFile(const std::string &path) +{ + std::ifstream f(path, std::ifstream::binary); + + if (!f) + return mdMethodDefNil; + + IMAGE_DOS_HEADER dosHeader; + IMAGE_NT_HEADERS32 ntHeaders; + + if (!f.read((char*)&dosHeader, sizeof(dosHeader))) return mdMethodDefNil; + if (!f.seekg(VAL32(dosHeader.e_lfanew), f.beg)) return mdMethodDefNil; + if (!f.read((char*)&ntHeaders, sizeof(ntHeaders))) return mdMethodDefNil; + + ULONG corRVA = 0; + if (ntHeaders.OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR32_MAGIC)) + { + corRVA = VAL32(ntHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COMHEADER].VirtualAddress); + } + else + { + IMAGE_NT_HEADERS64 ntHeaders64; + if (!f.seekg(VAL32(dosHeader.e_lfanew), f.beg)) return mdMethodDefNil; + if (!f.read((char*)&ntHeaders64, sizeof(ntHeaders64))) return mdMethodDefNil; + corRVA = VAL32(ntHeaders64.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COMHEADER].VirtualAddress); + } + + ULONG pos = + VAL32(dosHeader.e_lfanew) + + sizeof(ntHeaders.Signature) + + sizeof(ntHeaders.FileHeader) + + VAL16(ntHeaders.FileHeader.SizeOfOptionalHeader); + + if (!f.seekg(pos, f.beg)) return mdMethodDefNil; + + for (int i = 0; i < VAL16(ntHeaders.FileHeader.NumberOfSections); i++) + { + IMAGE_SECTION_HEADER sectionHeader; + + if (!f.read((char*)§ionHeader, sizeof(sectionHeader))) return mdMethodDefNil; + + if (corRVA >= VAL32(sectionHeader.VirtualAddress) && + corRVA < VAL32(sectionHeader.VirtualAddress) + VAL32(sectionHeader.SizeOfRawData)) + { + ULONG offset = (corRVA - VAL32(sectionHeader.VirtualAddress)) + VAL32(sectionHeader.PointerToRawData); + + IMAGE_COR20_HEADER corHeader; + if (!f.seekg(offset, f.beg)) return mdMethodDefNil; + if (!f.read((char*)&corHeader, sizeof(corHeader))) return mdMethodDefNil; + + if (VAL32(corHeader.Flags) & COMIMAGE_FLAGS_NATIVE_ENTRYPOINT) + return mdMethodDefNil; + + return VAL32(corHeader.EntryPointToken); + } + } + + return mdMethodDefNil; +} + +HRESULT Breakpoints::TrySetupEntryBreakpoint(ICorDebugModule *pModule) +{ + if (!m_stopAtEntry || m_entryPoint != mdMethodDefNil) + return S_FALSE; + + HRESULT Status; + + mdMethodDef entryPointToken = GetEntryPointTokenFromFile(Modules::GetModuleFileName(pModule)); + if (entryPointToken == mdMethodDefNil) + return S_FALSE; + + ToRelease pFunction; + IfFailRet(pModule->GetFunctionFromToken(entryPointToken, &pFunction)); + + ToRelease entryBreakpoint; + IfFailRet(pFunction->CreateBreakpoint(&entryBreakpoint)); + + m_entryPoint = entryPointToken; + m_entryBreakpoint = entryBreakpoint.Detach(); + + return S_OK; +} + void Breakpoints::TryResolveBreakpointsForModule(ICorDebugModule *pModule, std::vector &events) { std::lock_guard lock(m_breakpointsMutex); @@ -147,6 +307,8 @@ void Breakpoints::TryResolveBreakpointsForModule(ICorDebugModule *pModule, std:: } } } + + TrySetupEntryBreakpoint(pModule); } HRESULT Breakpoints::ResolveBreakpoint(ManagedBreakpoint &bp) diff --git a/src/debug/netcoredbg/manageddebugger.cpp b/src/debug/netcoredbg/manageddebugger.cpp index a1aba3c..06b6a98 100644 --- a/src/debug/netcoredbg/manageddebugger.cpp +++ b/src/debug/netcoredbg/manageddebugger.cpp @@ -285,8 +285,16 @@ public: DWORD threadId = 0; pThread->GetID(&threadId); + bool atEntry = false; StoppedEvent event(StopBreakpoint, threadId); - m_debugger.m_breakpoints.HitBreakpoint(pThread, event.breakpoint); + if (FAILED(m_debugger.m_breakpoints.HitBreakpoint(pThread, pBreakpoint, event.breakpoint, atEntry))) + { + pAppDomain->Continue(0); + return S_OK; + } + + if (atEntry) + event.reason = StopEntry; ToRelease pFrame; if (SUCCEEDED(pThread->GetActiveFrame(&pFrame)) && pFrame != nullptr) @@ -658,6 +666,7 @@ HRESULT ManagedDebugger::Launch(std::string fileExec, std::vector e m_execPath = fileExec; m_execArgs = execArgs; m_stopAtEntry = stopAtEntry; + m_breakpoints.SetStopAtEntry(m_stopAtEntry); return S_OK; } diff --git a/src/debug/netcoredbg/manageddebugger.h b/src/debug/netcoredbg/manageddebugger.h index 58f55e0..c3a2e88 100644 --- a/src/debug/netcoredbg/manageddebugger.h +++ b/src/debug/netcoredbg/manageddebugger.h @@ -187,10 +187,23 @@ class Breakpoints HRESULT ResolveBreakpointInModule(ICorDebugModule *pModule, ManagedBreakpoint &bp); HRESULT ResolveBreakpoint(ManagedBreakpoint &bp); + bool m_stopAtEntry; + mdMethodDef m_entryPoint; + ToRelease m_entryBreakpoint; + + HRESULT TrySetupEntryBreakpoint(ICorDebugModule *pModule); + bool HitEntry(ICorDebugThread *pThread, ICorDebugBreakpoint *pBreakpoint); + public: - Breakpoints(Modules &modules) : m_modules(modules), m_nextBreakpointId(1) {} + Breakpoints(Modules &modules) : + m_modules(modules), m_nextBreakpointId(1), m_stopAtEntry(false), m_entryPoint(mdMethodDefNil) {} + + HRESULT HitBreakpoint( + ICorDebugThread *pThread, + ICorDebugBreakpoint *pBreakpoint, + Breakpoint &breakpoint, + bool &atEntry); - HRESULT HitBreakpoint(ICorDebugThread *pThread, Breakpoint &breakpoint); void DeleteAllBreakpoints(); void TryResolveBreakpointsForModule(ICorDebugModule *pModule, std::vector &events); @@ -202,6 +215,8 @@ public: std::string filename, const std::vector &lines, std::vector &breakpoints); + + void SetStopAtEntry(bool stopAtEntry); }; class Variables diff --git a/src/debug/netcoredbg/modules.h b/src/debug/netcoredbg/modules.h index b8e1188..812db30 100644 --- a/src/debug/netcoredbg/modules.h +++ b/src/debug/netcoredbg/modules.h @@ -25,8 +25,6 @@ class Modules std::mutex m_modulesInfoMutex; std::unordered_map m_modulesInfo; - std::string GetModuleFileName(ICorDebugModule *pModule); - static bool ShouldLoadSymbolsForModule(const std::string &moduleName); static HRESULT SetJMCFromAttributes(ICorDebugModule *pModule, SymbolReader *symbolReader); @@ -42,6 +40,7 @@ public: }; static HRESULT GetModuleId(ICorDebugModule *pModule, std::string &id); + static std::string GetModuleFileName(ICorDebugModule *pModule); HRESULT GetModuleWithName(const std::string &name, ICorDebugModule **ppModule); diff --git a/src/debug/netcoredbg/vscodeprotocol.cpp b/src/debug/netcoredbg/vscodeprotocol.cpp index a7ade52..db28196 100644 --- a/src/debug/netcoredbg/vscodeprotocol.cpp +++ b/src/debug/netcoredbg/vscodeprotocol.cpp @@ -288,7 +288,7 @@ HRESULT VSCodeProtocol::HandleCommand(const std::string &command, const json &ar if (dirSepIndex != std::string::npos) exeDir = exe.substr(0, dirSepIndex + 1); - return m_debugger->Launch(exeDir + "corerun", args); + return m_debugger->Launch(exeDir + "corerun", args, arguments.value("stopAtEntry", false)); } }, { "threads", [this](const json &arguments, json &body){ HRESULT Status; -- 2.7.4