From 2138b1e1eaa3bb0543713cab8d0f9b7f94a508d7 Mon Sep 17 00:00:00 2001 From: Igor Kulaychuk Date: Thu, 6 Jul 2017 21:10:55 +0300 Subject: [PATCH] Add breakpoints management --- src/debug/debugger/CMakeLists.txt | 5 +- src/debug/debugger/breakpoints.cpp | 261 +++++++++++++++++ src/debug/debugger/main.cpp | 546 ++++++++++++++---------------------- src/debug/debugger/modules.cpp | 205 ++++++++++++++ src/debug/debugger/symbolreader.cpp | 1 + src/debug/debugger/symbolreader.h | 39 --- src/debug/debugger/torelease.h | 39 +++ 7 files changed, 712 insertions(+), 384 deletions(-) create mode 100644 src/debug/debugger/breakpoints.cpp create mode 100644 src/debug/debugger/modules.cpp diff --git a/src/debug/debugger/CMakeLists.txt b/src/debug/debugger/CMakeLists.txt index 4e3ac0e..fefe705 100644 --- a/src/debug/debugger/CMakeLists.txt +++ b/src/debug/debugger/CMakeLists.txt @@ -1,6 +1,6 @@ project(coreclrdbg) -set(coreclrdbg_SRC main.cpp symbolreader.cpp tpa.cpp) +set(coreclrdbg_SRC main.cpp symbolreader.cpp tpa.cpp breakpoints.cpp modules.cpp) if(CLR_CMAKE_PLATFORM_ARCH_AMD64) add_definitions(-D_TARGET_AMD64_=1) @@ -37,12 +37,13 @@ include_directories(${CLR_DIR}/src/debug/inc) include_directories(${CLR_DIR}/src/debug/shim) include_directories(${CLR_DIR}/src/coreclr/hosts/inc) +include_directories(${CLR_DIR}/src/ToolBox/SOS/Strike) + target_link_libraries(coreclrdbg corguids dbgshim mscordaccore palrt - edit ) install_clr(coreclrdbg) diff --git a/src/debug/debugger/breakpoints.cpp b/src/debug/debugger/breakpoints.cpp new file mode 100644 index 0000000..2e7fe75 --- /dev/null +++ b/src/debug/debugger/breakpoints.cpp @@ -0,0 +1,261 @@ +#include + +#include "corhdr.h" +#include "cor.h" +#include "cordebug.h" +#include "debugshim.h" + +#include +#include +#include +#include +#include + +#include "torelease.h" + +// Modules +HRESULT GetFrameLocation(ICorDebugFrame *pFrame, + ULONG32 &ilOffset, + mdMethodDef &methodToken, + std::string &fullname, + ULONG &linenum); +std::string GetModuleName(ICorDebugModule *pModule); + +HRESULT GetLocationInModule(ICorDebugModule *pModule, + std::string filename, + ULONG linenum, + ULONG32 &ilOffset, + mdMethodDef &methodToken, + std::string &fullname); + + +std::mutex g_breakMutex; +ULONG32 g_breakIndex = 1; + +struct Breakpoint { + ULONG32 id; + CORDB_ADDRESS modAddress; + mdMethodDef methodToken; + ULONG32 ilOffset; + std::string fullname; + int linenum; + ICorDebugBreakpoint *breakpoint; + + bool IsResolved() const + { + return modAddress != 0; + } + + Breakpoint() : id(0), modAddress(0), methodToken(0), ilOffset(0), linenum(0), breakpoint(nullptr) {} +}; + +std::vector g_breaks; + +HRESULT PrintBreakpoint(ULONG32 id, std::string &output) +{ + std::lock_guard lock(g_breakMutex); + + for (Breakpoint &b : g_breaks) + { + if (b.id != id) + continue; + + std::stringstream ss; + + HRESULT Status; + if (b.IsResolved()) + { + ss << "bkpt={number=\"" << id << "\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\"," + "func=\"\",fullname=\"" << b.fullname << "\",line=\"" << b.linenum << "\"}"; + Status = S_OK; + } + else + { + ss << "bkpt={number=\"" << id << "\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\"," + "warning=\"No executable code of the debugger’s target code type is associated with this line.\"}"; + Status = S_FALSE; + } + output = ss.str(); + return Status; + } + + return E_FAIL; +} + +HRESULT FindCurrentBreakpointId(ICorDebugThread *pThread, ULONG32 &id) +{ + HRESULT Status; + ULONG32 ilOffset; + mdMethodDef methodToken; + std::string fullname; + ULONG linenum; + + ToRelease pFrame; + IfFailRet(pThread->GetActiveFrame(&pFrame)); + IfFailRet(GetFrameLocation(pFrame, ilOffset, methodToken, fullname, linenum)); + + std::lock_guard lock(g_breakMutex); + + for (Breakpoint &b : g_breaks) + { + if (b.fullname == fullname && + b.ilOffset == ilOffset && + b.methodToken == methodToken && + b.linenum == linenum) + { + id = b.id; + return S_OK; + } + } + + return E_FAIL; +} + +HRESULT DeleteBreakpoint(ULONG32 id) +{ + std::lock_guard lock(g_breakMutex); + + auto bpit = g_breaks.begin(); + + while(bpit != g_breaks.end()) + { + if (bpit->id == id) + { + if (bpit->breakpoint) + { + bpit->breakpoint->Activate(0); + bpit->breakpoint->Release(); + } + bpit = g_breaks.erase(bpit); + return S_OK; + } + else + { + ++bpit; + } + } + + return E_FAIL; +} + +void DeleteAllBreakpoints() +{ + std::lock_guard lock(g_breakMutex); + + for (Breakpoint &b : g_breaks) + { + if (b.breakpoint) + { + b.breakpoint->Activate(0); + b.breakpoint->Release(); + } + } + + g_breaks.clear(); +} + +HRESULT ResolveBreakpoint(ICorDebugModule *pModule, std::string filename, int linenum, Breakpoint &bp) +{ + HRESULT Status; + + mdMethodDef methodToken; + ULONG32 ilOffset; + std::string fullname; + + IfFailRet(GetLocationInModule(pModule, filename, + linenum, + ilOffset, + methodToken, + fullname)); + + ToRelease pFunc; + ToRelease pCode; + IfFailRet(pModule->GetFunctionFromToken(methodToken, &pFunc)); + IfFailRet(pFunc->GetILCode(&pCode)); + + ToRelease pBreakpoint; + IfFailRet(pCode->CreateBreakpoint(ilOffset, &pBreakpoint)); + IfFailRet(pBreakpoint->Activate(TRUE)); + + CORDB_ADDRESS modAddress; + IfFailRet(pModule->GetBaseAddress(&modAddress)); + + bp.modAddress = modAddress; + bp.methodToken = methodToken; + bp.ilOffset = ilOffset; + bp.fullname = fullname; + bp.linenum = linenum; + bp.breakpoint = pBreakpoint.Detach(); + + return S_OK; +} + +HRESULT TryResolveBreakpointsForModule(ICorDebugModule *pModule) +{ + std::lock_guard lock(g_breakMutex); + + for (Breakpoint &b : g_breaks) + { + if (b.IsResolved()) + continue; + + if (SUCCEEDED(ResolveBreakpoint(pModule, b.fullname, b.linenum, b))) + { + return S_OK; + } + } + return E_FAIL; +} + +HRESULT CreateBreakpointInProcess(ICorDebugProcess *pProcess, std::string filename, int linenum, ULONG32 &id) +{ + HRESULT Status; + + ToRelease domains; + IfFailRet(pProcess->EnumerateAppDomains(&domains)); + + Breakpoint bp; + + ICorDebugAppDomain *curDomain; + ULONG domainsFetched; + while (SUCCEEDED(domains->Next(1, &curDomain, &domainsFetched)) && domainsFetched == 1) + { + ToRelease pDomain = curDomain; + + ToRelease assemblies; + IfFailRet(pDomain->EnumerateAssemblies(&assemblies)); + + ICorDebugAssembly *curAssembly; + ULONG assembliesFetched; + while (SUCCEEDED(assemblies->Next(1, &curAssembly, &assembliesFetched)) && assembliesFetched == 1) + { + ToRelease pAssembly = curAssembly; + + ToRelease modules; + IfFailRet(pAssembly->EnumerateModules(&modules)); + + ICorDebugModule *curModule; + ULONG modulesFetched; + while (SUCCEEDED(modules->Next(1, &curModule, &modulesFetched)) && modulesFetched == 1) + { + ToRelease pModule = curModule; + if (SUCCEEDED(ResolveBreakpoint(pModule, filename, linenum, bp))) + { + std::lock_guard lock(g_breakMutex); + id = g_breakIndex++; + bp.id = id; + g_breaks.push_back(bp); + return S_OK; + } + } + } + } + + // Add pending breakpoint + std::lock_guard lock(g_breakMutex); + id = g_breakIndex++; + bp.id = id; + g_breaks.push_back(bp); + + return S_FALSE; +} \ No newline at end of file diff --git a/src/debug/debugger/main.cpp b/src/debug/debugger/main.cpp index 06e26c8..6c643aa 100644 --- a/src/debug/debugger/main.cpp +++ b/src/debug/debugger/main.cpp @@ -1,10 +1,4 @@ -// #include -// #include - #include -// #include -// #include -// #include #include "corhdr.h" #include "cor.h" @@ -13,17 +7,20 @@ #include "clrinternal.h" #include -#include - -#include #include #include #include #include +#include #include -#include "symbolreader.h" +typedef char * LPCUTF8; +typedef uintptr_t TADDR; +#include "sos_md.h" + +#include +#include "torelease.h" EXTERN_C HRESULT CreateDebuggingInterfaceFromVersionEx( int iDebuggerVersion, @@ -38,9 +35,7 @@ CreateVersionStringFromModule( DWORD cchBuffer, DWORD* pdwLength); -#include -#include "torelease.h" - +std::mutex g_processMutex; ICorDebugProcess *g_process = NULL; ULONG OSPageSize () @@ -69,6 +64,11 @@ size_t NextOSPageAddress (size_t addr) BOOL SafeReadMemory (TADDR offset, PVOID lpBuffer, ULONG cb, PULONG lpcbBytesRead) { + std::lock_guard lock(g_processMutex); + + if (!g_process) + return FALSE; + BOOL bRet = FALSE; SIZE_T bytesRead = 0; @@ -87,36 +87,47 @@ BOOL SafeReadMemory (TADDR offset, PVOID lpBuffer, ULONG cb, return bRet; } -/* This holds all the state for our line editor */ -EditLine *el; - -const char * prompt(EditLine *e) { - return "(gdb) "; -} +bool g_processExited = false; -// Printing the following string causes libedit to back up to the beginning of the line & blank it out. -const char undo_prompt_string[4] = { (char) 13, (char) 27, (char) 91, (char) 75}; +std::mutex g_outMutex; -void _el_printf(EditLine *el, const char *fmt, ...) - __attribute__((format (printf, 2, 3))); +void _out_printf(const char *fmt, ...) + __attribute__((format (printf, 1, 2))); -#define el_printf(el, fmt, ...) _el_printf(el, fmt, ##__VA_ARGS__) +#define out_printf(fmt, ...) _out_printf(fmt, ##__VA_ARGS__) -void _el_printf(EditLine *el, const char *fmt, ...) +void _out_printf(const char *fmt, ...) { - fwrite(undo_prompt_string , sizeof(char), sizeof(undo_prompt_string), stdout); + std::lock_guard lock(g_outMutex); va_list arg; - /* Write the error message */ va_start(arg, fmt); vfprintf(stdout, fmt, arg); va_end(arg); fflush(stdout); - - el_set(el, EL_REFRESH); } +// Breakpoints +void DeleteAllBreakpoints(); +HRESULT FindCurrentBreakpointId(ICorDebugThread *pThread, ULONG32 &id); +HRESULT DeleteBreakpoint(ULONG32 id); +HRESULT CreateBreakpointInProcess(ICorDebugProcess *pProcess, std::string filename, int linenum, ULONG32 &id); +HRESULT TryResolveBreakpointsForModule(ICorDebugModule *pModule); +HRESULT PrintBreakpoint(ULONG32 id, std::string &output); + +// Modules +void SetCoreCLRPath(const std::string &coreclrPath); +std::string GetModuleName(ICorDebugModule *pModule); +HRESULT GetStepRangeFromCurrentIP(ICorDebugThread *pThread, COR_DEBUG_STEP_RANGE *range); +HRESULT TryLoadModuleSymbols(ICorDebugModule *pModule); +HRESULT GetFrameLocation(ICorDebugFrame *pFrame, + ULONG32 &ilOffset, + mdMethodDef &methodToken, + std::string &fullname, + ULONG &linenum); + + HRESULT PrintThread(ICorDebugThread *pThread, std::string &output) { HRESULT Status = S_OK; @@ -171,7 +182,7 @@ HRESULT PrintThreadsState(ICorDebugController *controller, std::string &output) std::stringstream ss; - ss << "^done,threads=["; + ss << "threads=["; ICorDebugThread *handle; ULONG fetched; @@ -195,206 +206,40 @@ HRESULT PrintThreadsState(ICorDebugController *controller, std::string &output) HRESULT PrintFrameLocation(ICorDebugFrame *pFrame, std::string &output) { HRESULT Status; + ULONG32 ilOffset; mdMethodDef methodToken; + std::string fullname; + ULONG linenum; - IfFailRet(pFrame->GetFunctionToken(&methodToken)); - - ToRelease pFunc; - IfFailRet(pFrame->GetFunction(&pFunc)); - - ToRelease pModule; - IfFailRet(pFunc->GetModule(&pModule)); - - ToRelease pMDUnknown; - ToRelease pMDImport; - IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown)); - - ToRelease pILFrame; - IfFailRet(pFrame->QueryInterface(IID_ICorDebugILFrame, (LPVOID*) &pILFrame)); - - ULONG32 nOffset; - CorDebugMappingResult mappingResult; - IfFailRet(pILFrame->GetIP(&nOffset, &mappingResult)); - - SymbolReader symbolReader; - if (SUCCEEDED(symbolReader.LoadSymbols(pMDImport, pModule))) - { - WCHAR name[mdNameLen]; - ULONG linenum; - IfFailRet(symbolReader.GetLineByILOffset(methodToken, nOffset, &linenum, name, mdNameLen)); + IfFailRet(GetFrameLocation(pFrame, ilOffset, methodToken, fullname, linenum)); - char cname[mdNameLen]; - - WideCharToMultiByte(CP_UTF8, 0, name, (int)(PAL_wcslen(name) + 1), cname, mdNameLen, NULL, NULL); - - std::stringstream ss; - ss << "line=\"" << linenum << "\",fullname=\"" << cname << "\""; - output = ss.str(); - } + std::stringstream ss; + ss << "line=\"" << linenum << "\",fullname=\"" << fullname << "\""; + output = ss.str(); return S_OK; } -HRESULT PrintLocation(ICorDebugThread *pThread, std::string &output) -{ - HRESULT Status; - ToRelease pFrame; - IfFailRet(pThread->GetActiveFrame(&pFrame)); - return PrintFrameLocation(pFrame, output); -} - -HRESULT GetStepRangeFromCurrentIP(ICorDebugThread *pThread, COR_DEBUG_STEP_RANGE *range) +HRESULT PrintLocation(ICorDebugThread *pThread, std::string &output) { HRESULT Status; ToRelease pFrame; IfFailRet(pThread->GetActiveFrame(&pFrame)); + ULONG32 ilOffset; mdMethodDef methodToken; - IfFailRet(pFrame->GetFunctionToken(&methodToken)); - - ToRelease pFunc; - IfFailRet(pFrame->GetFunction(&pFunc)); - - ToRelease pModule; - IfFailRet(pFunc->GetModule(&pModule)); - - ToRelease pMDUnknown; - ToRelease pMDImport; - IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown)); - - ToRelease pILFrame; - IfFailRet(pFrame->QueryInterface(IID_ICorDebugILFrame, (LPVOID*) &pILFrame)); - - ULONG32 nOffset; - CorDebugMappingResult mappingResult; - IfFailRet(pILFrame->GetIP(&nOffset, &mappingResult)); - - SymbolReader symbolReader; - IfFailRet(symbolReader.LoadSymbols(pMDImport, pModule)); - - ULONG32 ilStartOffset; - ULONG32 ilEndOffset; - - IfFailRet(symbolReader.GetStepRangesFromIP(nOffset, methodToken, &ilStartOffset, &ilEndOffset)); + std::string fullname; + ULONG linenum; - if (ilStartOffset == ilEndOffset) - { - ToRelease pCode; - IfFailRet(pFunc->GetILCode(&pCode)); - IfFailRet(pCode->GetSize(&ilEndOffset)); - } + IfFailRet(GetFrameLocation(pFrame, ilOffset, methodToken, fullname, linenum)); - range->startOffset = ilStartOffset; - range->endOffset = ilEndOffset; + std::stringstream ss; + ss << "line=\"" << linenum << "\",fullname=\"" << fullname << "\""; + output = ss.str(); return S_OK; } -struct ModuleInfo -{ - CORDB_ADDRESS address; - std::shared_ptr symbols; - //ICorDebugModule *module; -}; - -std::mutex g_modulesInfoMutex; -std::unordered_map g_modulesInfo; - -std::string GetModuleName(ICorDebugModule *pModule) -{ - char cname[mdNameLen]; - WCHAR name[mdNameLen]; - ULONG32 name_len = 0; - if (SUCCEEDED(pModule->GetName(mdNameLen, &name_len, name))) - { - WideCharToMultiByte(CP_UTF8, 0, name, (int)(PAL_wcslen(name) + 1), cname, mdNameLen, NULL, NULL); - return cname; - } - return std::string(); -} - -HRESULT CreateBreakpoint(ICorDebugModule *pModule, std::string filename, int linenum) -{ - HRESULT Status; - - WCHAR nameBuffer[MAX_LONGPATH]; - - Status = MultiByteToWideChar(CP_UTF8, 0, filename.c_str(), filename.size() + 1, nameBuffer, MAX_LONGPATH); - - std::string modName = GetModuleName(pModule); - - { - std::lock_guard lock(g_modulesInfoMutex); - auto info_pair = g_modulesInfo.find(modName); - if (info_pair == g_modulesInfo.end()) - { - return E_FAIL; - } - - CORDB_ADDRESS modAddress; - IfFailRet(pModule->GetBaseAddress(&modAddress)); - - mdMethodDef methodToken; - ULONG32 ilOffset; - - IfFailRet(info_pair->second.symbols->ResolveSequencePoint(nameBuffer, linenum, modAddress, &methodToken, &ilOffset)); - - //el_printf(el, " methodToken=0x%x ilOffset=%i\n", methodToken, ilOffset); - - ToRelease pFunc; - ToRelease pCode; - IfFailRet(pModule->GetFunctionFromToken(methodToken, &pFunc)); - IfFailRet(pFunc->GetILCode(&pCode)); - - ToRelease pBreakpoint; - IfFailRet(pCode->CreateBreakpoint(ilOffset, &pBreakpoint)); - IfFailRet(pBreakpoint->Activate(TRUE)); - - return S_OK; - } - - return E_FAIL; -} - -HRESULT CreateBreakpointInProcess(ICorDebugProcess *pProcess, std::string filename, int linenum) -{ - HRESULT Status; - - ToRelease domains; - IfFailRet(pProcess->EnumerateAppDomains(&domains)); - - ICorDebugAppDomain *curDomain; - ULONG domainsFetched; - while (SUCCEEDED(domains->Next(1, &curDomain, &domainsFetched)) && domainsFetched == 1) - { - ToRelease pDomain = curDomain; - - ToRelease assemblies; - IfFailRet(pDomain->EnumerateAssemblies(&assemblies)); - - ICorDebugAssembly *curAssembly; - ULONG assembliesFetched; - while (SUCCEEDED(assemblies->Next(1, &curAssembly, &assembliesFetched)) && assembliesFetched == 1) - { - ToRelease pAssembly = curAssembly; - - ToRelease modules; - IfFailRet(pAssembly->EnumerateModules(&modules)); - - ICorDebugModule *curModule; - ULONG modulesFetched; - while (SUCCEEDED(modules->Next(1, &curModule, &modulesFetched)) && modulesFetched == 1) - { - ToRelease pModule = curModule; - - if (SUCCEEDED(CreateBreakpoint(pModule, filename, linenum))) - return S_OK; - } - } - } - return S_FALSE; -} - HRESULT DisableAllBreakpointsAndSteppersInAppDomain(ICorDebugAppDomain *pAppDomain) { HRESULT Status; @@ -411,6 +256,8 @@ HRESULT DisableAllBreakpointsAndSteppersInAppDomain(ICorDebugAppDomain *pAppDoma } } + DeleteAllBreakpoints(); + ToRelease steppers; if (SUCCEEDED(pAppDomain->EnumerateSteppers(&steppers))) { @@ -443,33 +290,6 @@ HRESULT DisableAllBreakpointsAndSteppers(ICorDebugProcess *pProcess) return S_OK; } -HRESULT TryLoadModuleSymbols(ICorDebugModule *pModule) -{ - HRESULT Status; - std::string name = GetModuleName(pModule); - - if (name.empty()) - return E_FAIL; - - ToRelease pMDUnknown; - ToRelease pMDImport; - IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown)); - - IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMDImport)); - - auto symbolReader = std::make_shared(); - IfFailRet(symbolReader->LoadSymbols(pMDImport, pModule)); - - CORDB_ADDRESS modAddress; - pModule->GetBaseAddress(&modAddress); - - { - std::lock_guard lock(g_modulesInfoMutex); - g_modulesInfo.insert({name, {modAddress, symbolReader}}); - } - return S_OK; -} - enum StepType { STEP_IN = 0, STEP_OVER, @@ -661,6 +481,19 @@ HRESULT PrintFrames(ICorDebugThread *pThread, std::string &output) } wcscat_s (m_szName, _countof(m_szName), szFunctionName); + ToRelease pIMDI; + IfFailRet(GetMDInternalFromImport(pMD, &pIMDI)); + + LPCSTR szName = NULL; + LPCSTR szNameSpace = NULL; + pIMDI->GetNameOfTypeDef(memTypeDef, &szName, &szNameSpace); + + // CORDB_ADDRESS modAddress; + // IfFailRet(pModule->GetBaseAddress(&modAddress)); + // WCHAR cBuffer[2048]; + // PrettyPrintClassFromToken(modAddress, typeDef, cBuffer, 2048); + // IMDInternalImport *pIMDI; + // TODO: // LONG lSigBlobRemaining; // hr = GetFullNameForMD(pbSigBlob, ulSigBlob, &lSigBlobRemaining); @@ -668,7 +501,7 @@ HRESULT PrintFrames(ICorDebugThread *pThread, std::string &output) char funcName[2048] = {0}; WideCharToMultiByte(CP_UTF8, 0, m_szName, (int)(_wcslen(m_szName) + 1), funcName, _countof(funcName), NULL, NULL); - ss << "func=\"" << funcName << "\"}"; + ss << "func=\"" << szName << "." << szName << "\"}"; } ss << "]"; @@ -688,8 +521,7 @@ public: void HandleEvent(ICorDebugController *controller, const char *eventName) { - el_printf(el, "test"); - el_printf(el, "event received on tid %li: %s\n", syscall(SYS_gettid), eventName); + out_printf("=message,text=\"event received %s\"\n", eventName); controller->Continue(0); } @@ -746,14 +578,17 @@ public: /* [in] */ ICorDebugThread *pThread, /* [in] */ ICorDebugBreakpoint *pBreakpoint) { + ULONG32 id = 0; + FindCurrentBreakpointId(pThread, id); + std::string output; PrintLocation(pThread, output); DWORD threadId = 0; pThread->GetID(&threadId); - el_printf(el, "*stopped,reason=\"breakpoint-hit\",thread-id=\"%i\",stopped-threads=\"all\",bkptno=\"1\",%s\n", - (int)threadId, output.c_str()); + out_printf("*stopped,reason=\"breakpoint-hit\",thread-id=\"%i\",stopped-threads=\"all\",bkptno=\"%u\",%s\n", + (int)threadId, (unsigned int)id, output.c_str()); { std::lock_guard lock(g_currentThreadMutex); if (g_currentThread) @@ -776,7 +611,7 @@ public: DWORD threadId = 0; pThread->GetID(&threadId); - el_printf(el, "*stopped,reason=\"end-stepping-range\",thread-id=\"%i\",stopped-threads=\"all\",%s\n", + out_printf("*stopped,reason=\"end-stepping-range\",thread-id=\"%i\",stopped-threads=\"all\",%s\n", (int)threadId, output.c_str()); { @@ -797,7 +632,38 @@ public: virtual HRESULT STDMETHODCALLTYPE Exception( /* [in] */ ICorDebugAppDomain *pAppDomain, /* [in] */ ICorDebugThread *pThread, - /* [in] */ BOOL unhandled) { return S_OK; } + /* [in] */ BOOL unhandled) + { + std::string output; + PrintLocation(pThread, output); + + DWORD threadId = 0; + pThread->GetID(&threadId); + + if (unhandled) + { + ToRelease pFrame; + std::string output; + + if (SUCCEEDED(pThread->GetActiveFrame(&pFrame))) + PrintFrameLocation(pFrame, output); + + out_printf("*stopped,reason=\"exception-received\",exception-stage=\"%s\",thread-id=\"%i\",stopped-threads=\"all\",%s\n", + unhandled ? "unhandled" : "handled", (int)threadId, output.c_str()); + + std::lock_guard lock(g_currentThreadMutex); + if (g_currentThread) + g_currentThread->Release(); + pThread->AddRef(); + g_currentThread = pThread; + } else { + out_printf("=message,text=\"Exception thrown: '%s' in %s\\n\",send-to=\"output-window\",source=\"target-exception\"\n", + "", ""); + pAppDomain->Continue(0); + } + + return S_OK; + } virtual HRESULT STDMETHODCALLTYPE EvalComplete( /* [in] */ ICorDebugAppDomain *pAppDomain, @@ -820,7 +686,8 @@ public: virtual HRESULT STDMETHODCALLTYPE ExitProcess( /* [in] */ ICorDebugProcess *pProcess) { - el_printf(el, "*stopped,reason=\"exited\",exit-code=\"%i\"\n", 0); + out_printf("*stopped,reason=\"exited\",exit-code=\"%i\"\n", 0); + g_processExited = true; return S_OK; } @@ -830,7 +697,7 @@ public: { DWORD threadId = 0; thread->GetID(&threadId); - el_printf(el, "=thread-created,id=\"%i\"\n", (int)threadId); + out_printf("=thread-created,id=\"%i\"\n", (int)threadId); pAppDomain->Continue(0); return S_OK; } @@ -850,9 +717,10 @@ public: std::string name = GetModuleName(pModule); if (!name.empty()) { - el_printf(el, "=library-loaded,target-name=\"%s\"\n", name.c_str()); + out_printf("=library-loaded,target-name=\"%s\"\n", name.c_str()); } TryLoadModuleSymbols(pModule); + TryResolveBreakpointsForModule(pModule); pAppDomain->Continue(0); return S_OK; } @@ -968,7 +836,22 @@ public: /* [in] */ ICorDebugFrame *pFrame, /* [in] */ ULONG32 nOffset, /* [in] */ CorDebugExceptionCallbackType dwEventType, - /* [in] */ DWORD dwFlags) {return S_OK; } + /* [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; + // } + // out_printf("*stopped,reason=\"exception-received2\",exception-stage=\"%s\"\n", + // cbTypeName); + pAppDomain->Continue(0); + return S_OK; + } virtual HRESULT STDMETHODCALLTYPE ExceptionUnwind( /* [in] */ ICorDebugAppDomain *pAppDomain, @@ -1078,7 +961,7 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - SymbolReader::SetCoreCLRPath(coreclrPath); + SetCoreCLRPath(coreclrPath); WCHAR szModuleName[MAX_LONGPATH]; MultiByteToWideChar(CP_UTF8, 0, coreclrPath.c_str(), coreclrPath.size() + 1, szModuleName, MAX_LONGPATH); @@ -1089,7 +972,7 @@ int main(int argc, char *argv[]) pidDebuggee, szModuleName, pBuffer, - 100, + _countof(pBuffer), &dwLength); if (FAILED(hr)) @@ -1145,103 +1028,77 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - g_process = pProcess; - - //printf("libedit thread %li\n", syscall(SYS_gettid)); - - - /* This holds the info for our history */ - History *myhistory; - - /* Temp variables */ - int keepreading = 1; - HistEvent ev; - - /* Initialize the EditLine state to use our prompt function and - emacs style editing. */ - - el = el_init(argv[0], stdin, stdout, stderr); - el_set(el, EL_PROMPT, &prompt); - el_set(el, EL_EDITOR, "emacs"); - el_set(el, EL_SIGNAL, 1); - - /* Initialize the history */ - myhistory = history_init(); - if (myhistory == 0) { - fprintf(stderr, "history could not be initialized\n"); - return 1; + { + std::lock_guard lock(g_processMutex); + g_process = pProcess; } - /* Set the size of the history */ - history(myhistory, &ev, H_SETSIZE, 800); - - /* This sets up the call back functions for history functionality */ - el_set(el, EL_HIST, history, myhistory); + static char inputBuffer[1024]; + const char *token = ""; - std::string prev_command; + for (;;) { + token = ""; - while (keepreading) { - /* count is the number of characters read. - line is a const char* of our command line with the tailing \n */ - int count; + out_printf("(gdb)\n"); + if (!fgets(inputBuffer, _countof(inputBuffer), stdin)) + break; - const char *raw_line = el_gets(el, &count); - // Sleep(3000); - // const char *raw_line = "-gdb-exit\n"; - // count = strlen(raw_line); + size_t count = strlen(inputBuffer); - if (count <= 0) - { - keepreading = 0; + if (count < 1 || inputBuffer[count - 1] != '\n') break; - } - /* In order to use our history we have to explicitly add commands - to the history */ + if (count >= 2 && inputBuffer[count - 2] == '\r') + --count; - std::string line(raw_line, count - 1); - - if (!line.empty()) - { - history(myhistory, &ev, H_ENTER, raw_line); - prev_command = line; - } + int miCmdStart = 0; + char *tokenEnd = strchr(inputBuffer, '-'); + if (tokenEnd) + miCmdStart = tokenEnd - inputBuffer + 1; else - { - line = prev_command; - } + tokenEnd = inputBuffer; + std::string line(inputBuffer + miCmdStart, count - miCmdStart - 1); + *tokenEnd = '\0'; + token = inputBuffer; - if (line == "-thread-info") + if (line == "thread-info") { std::string output; HRESULT hr = PrintThreadsState(pProcess, output); - printf("%x:%s\n", hr, output.c_str()); + if (SUCCEEDED(hr)) + { + out_printf("%s^done,%s\n", token, output.c_str()); + } + else + { + out_printf("%s^error,msg=\"HRESULT=%x\"\n", token, hr); + } } - else if (line == "-exec-continue") + else if (line == "exec-continue") { HRESULT hr = pProcess->Continue(0); if (SUCCEEDED(hr)) { - printf("^done\n"); + out_printf("%s^done\n", token); } else { - printf("^error,msg=\"HRESULT=%x\"\n", hr); + out_printf("%s^error,msg=\"HRESULT=%x\"\n", token, hr); } } - else if (line == "-exec-interrupt") + else if (line == "exec-interrupt") { HRESULT hr = pProcess->Stop(0); if (SUCCEEDED(hr)) { - printf("^done\n"); + out_printf("%s^done\n", token); } else { - printf("^error,msg=\"HRESULT=%x\"\n", hr); + out_printf("%s^error,msg=\"HRESULT=%x\"\n", token, hr); } } - else if (line.find("-break-insert ") == 0) + else if (line.find("break-insert ") == 0) { // TODO: imlement proper argument parsing std::size_t i1 = line.find(' '); @@ -1253,25 +1110,34 @@ int main(int argc, char *argv[]) std::string slinenum = line.substr(i2 + 1); int linenum = std::stoi(slinenum); - if (CreateBreakpointInProcess(pProcess, filename, linenum) == S_OK) + ULONG32 id; + if (SUCCEEDED(CreateBreakpointInProcess(pProcess, filename, linenum, id))) { - int bkpt_num = 1; - printf("^done,bkpt={number=\"%i\",fullname=\"%s\",line=\"%i\"}\n", bkpt_num, filename.c_str(), linenum); + std::string output; + PrintBreakpoint(id, output); + out_printf("%s^done,%s\n", token, output.c_str()); } } else { - printf("^error,msg=\"Unknown breakpoint location format\"\n"); + out_printf("%s^error,msg=\"Unknown breakpoint location format\"\n", token); } } - else if (line == "-exec-next" || line == "-exec-step" || line == "-exec-finish") + else if (line.find("break-delete ") == 0) + { + std::size_t i = line.find(' '); + ULONG32 id = std::stoul(line.substr(i)); + DeleteBreakpoint(id); + out_printf("%s^done\n", token); + } + else if (line == "exec-next" || line == "exec-step" || line == "exec-finish") { StepType stepType; - if (line == "-exec-next") + if (line == "exec-next") stepType = STEP_OVER; - else if (line == "-exec-step") + else if (line == "exec-step") stepType = STEP_IN; - else if (line == "-exec-finish") + else if (line == "exec-finish") stepType = STEP_OUT; HRESULT hr; @@ -1282,23 +1148,24 @@ int main(int argc, char *argv[]) if (FAILED(hr)) { - printf("^error,msg=\"Cannot create stepper: %x\"\n", hr); + out_printf("%s^error,msg=\"Cannot create stepper: %x\"\n", token, hr); } else { hr = pProcess->Continue(0); if (SUCCEEDED(hr)) { - printf("^done\n"); + out_printf("%s^done\n", token); } else { - printf("^error,msg=\"HRESULT=%x\"\n", hr); + out_printf("%s^error,msg=\"HRESULT=%x\"\n", token, hr); } } } - else if (line == "-stack-list-frames") + else if (line == "stack-list-frames") { + // TODO: Add parsing frame indeces std::string output; HRESULT hr; { @@ -1307,14 +1174,14 @@ int main(int argc, char *argv[]) } if (SUCCEEDED(hr)) { - printf("^done,%s\n", output.c_str()); + out_printf("%s^done,%s\n", inputBuffer, output.c_str()); } else { - printf("^error,msg=\"HRESULT=%x\"\n", hr); + out_printf("%s^error,msg=\"HRESULT=%x\"\n", inputBuffer, hr); } } - else if (line == "-gdb-exit") + else if (line == "gdb-exit") { hr = pProcess->Stop(0); if (SUCCEEDED(hr)) @@ -1323,36 +1190,29 @@ int main(int argc, char *argv[]) hr = pProcess->Terminate(0); - // TODO: wait for process exit event - Sleep(2000); + while (!g_processExited) + Sleep(100); pProcess.Release(); } - keepreading = 0; + break; } else { - printf("^error,msg=\"Unknown command: %s\"\n", line.c_str()); + out_printf("%s^error,msg=\"Unknown command: %s\"\n", token, line.c_str()); } } - /* Clean up our memory */ - history_end(myhistory); - el_end(el); - if (pProcess) { - hr = pProcess->Stop(0); - //fprintf(stderr, "Stop : hr=%x\n", hr); - if (SUCCEEDED(hr)) + if (SUCCEEDED(pProcess->Stop(0))) { DisableAllBreakpointsAndSteppers(pProcess); hr = pProcess->Detach(); } } - //fprintf(stderr, "Detach : hr=%x\n", hr); pCorDebug->Terminate(); - printf("^exit\n"); + out_printf("%s^exit\n", token); // TODO: Cleanup libcoreclr.so instance diff --git a/src/debug/debugger/modules.cpp b/src/debug/debugger/modules.cpp new file mode 100644 index 0000000..77d8d1e --- /dev/null +++ b/src/debug/debugger/modules.cpp @@ -0,0 +1,205 @@ +#include + +#include "corhdr.h" +#include "cor.h" +#include "cordebug.h" +#include "debugshim.h" + +#include +#include +#include +#include +#include + +#include "torelease.h" +#include "symbolreader.h" + +struct ModuleInfo +{ + CORDB_ADDRESS address; + std::shared_ptr symbols; + //ICorDebugModule *module; +}; + +std::mutex g_modulesInfoMutex; +std::unordered_map g_modulesInfo; + +void SetCoreCLRPath(const std::string &coreclrPath) +{ + SymbolReader::SetCoreCLRPath(coreclrPath); +} + +std::string GetModuleName(ICorDebugModule *pModule) +{ + char cname[mdNameLen]; + WCHAR name[mdNameLen]; + ULONG32 name_len = 0; + if (SUCCEEDED(pModule->GetName(mdNameLen, &name_len, name))) + { + WideCharToMultiByte(CP_UTF8, 0, name, (int)(PAL_wcslen(name) + 1), cname, mdNameLen, NULL, NULL); + return cname; + } + return std::string(); +} + +HRESULT GetLocationInModule(ICorDebugModule *pModule, + std::string filename, + ULONG linenum, + ULONG32 &ilOffset, + mdMethodDef &methodToken, + std::string &fullname) +{ + HRESULT Status; + + WCHAR nameBuffer[MAX_LONGPATH]; + + Status = MultiByteToWideChar(CP_UTF8, 0, filename.c_str(), filename.size() + 1, nameBuffer, MAX_LONGPATH); + + std::string modName = GetModuleName(pModule); + + std::lock_guard lock(g_modulesInfoMutex); + auto info_pair = g_modulesInfo.find(modName); + if (info_pair == g_modulesInfo.end()) + { + return E_FAIL; + } + + CORDB_ADDRESS modAddress; + IfFailRet(pModule->GetBaseAddress(&modAddress)); + IfFailRet(info_pair->second.symbols->ResolveSequencePoint(nameBuffer, linenum, modAddress, &methodToken, &ilOffset)); + + WCHAR wFilename[MAX_LONGPATH]; + ULONG resolvedLinenum; + IfFailRet(info_pair->second.symbols->GetLineByILOffset(methodToken, ilOffset, &resolvedLinenum, wFilename, _countof(wFilename))); + + char cFilename[MAX_LONGPATH]; + WideCharToMultiByte(CP_UTF8, 0, wFilename, (int)(PAL_wcslen(wFilename) + 1), cFilename, _countof(cFilename), NULL, NULL); + fullname = cFilename; + + return S_OK; +} + +HRESULT GetFrameLocation(ICorDebugFrame *pFrame, + ULONG32 &ilOffset, + mdMethodDef &methodToken, + std::string &fullname, + ULONG &linenum) +{ + HRESULT Status; + + IfFailRet(pFrame->GetFunctionToken(&methodToken)); + + ToRelease pFunc; + IfFailRet(pFrame->GetFunction(&pFunc)); + + ToRelease pModule; + IfFailRet(pFunc->GetModule(&pModule)); + + ToRelease pMDUnknown; + ToRelease pMDImport; + IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown)); + + ToRelease pILFrame; + IfFailRet(pFrame->QueryInterface(IID_ICorDebugILFrame, (LPVOID*) &pILFrame)); + + CorDebugMappingResult mappingResult; + IfFailRet(pILFrame->GetIP(&ilOffset, &mappingResult)); + + std::string modName = GetModuleName(pModule); + WCHAR name[MAX_LONGPATH]; + + { + std::lock_guard lock(g_modulesInfoMutex); + auto info_pair = g_modulesInfo.find(modName); + if (info_pair == g_modulesInfo.end()) + { + return E_FAIL; + } + + IfFailRet(info_pair->second.symbols->GetLineByILOffset(methodToken, ilOffset, &linenum, name, _countof(name))); + } + + char cname[MAX_LONGPATH]; + + WideCharToMultiByte(CP_UTF8, 0, name, (int)(PAL_wcslen(name) + 1), cname, _countof(cname), NULL, NULL); + + fullname = cname; + + return S_OK; +} + +HRESULT GetStepRangeFromCurrentIP(ICorDebugThread *pThread, COR_DEBUG_STEP_RANGE *range) +{ + HRESULT Status; + ToRelease pFrame; + IfFailRet(pThread->GetActiveFrame(&pFrame)); + + mdMethodDef methodToken; + IfFailRet(pFrame->GetFunctionToken(&methodToken)); + + ToRelease pFunc; + IfFailRet(pFrame->GetFunction(&pFunc)); + + ToRelease pModule; + IfFailRet(pFunc->GetModule(&pModule)); + + ToRelease pILFrame; + IfFailRet(pFrame->QueryInterface(IID_ICorDebugILFrame, (LPVOID*) &pILFrame)); + + ULONG32 nOffset; + CorDebugMappingResult mappingResult; + IfFailRet(pILFrame->GetIP(&nOffset, &mappingResult)); + + ULONG32 ilStartOffset; + ULONG32 ilEndOffset; + + { + std::lock_guard lock(g_modulesInfoMutex); + auto info_pair = g_modulesInfo.find(GetModuleName(pModule)); + if (info_pair == g_modulesInfo.end()) + { + return E_FAIL; + } + + IfFailRet(info_pair->second.symbols->GetStepRangesFromIP(nOffset, methodToken, &ilStartOffset, &ilEndOffset)); + } + + if (ilStartOffset == ilEndOffset) + { + ToRelease pCode; + IfFailRet(pFunc->GetILCode(&pCode)); + IfFailRet(pCode->GetSize(&ilEndOffset)); + } + + range->startOffset = ilStartOffset; + range->endOffset = ilEndOffset; + + return S_OK; +} + +HRESULT TryLoadModuleSymbols(ICorDebugModule *pModule) +{ + HRESULT Status; + std::string name = GetModuleName(pModule); + + if (name.empty()) + return E_FAIL; + + ToRelease pMDUnknown; + ToRelease pMDImport; + IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown)); + + IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMDImport)); + + auto symbolReader = std::make_shared(); + IfFailRet(symbolReader->LoadSymbols(pMDImport, pModule)); + + CORDB_ADDRESS modAddress; + pModule->GetBaseAddress(&modAddress); + + { + std::lock_guard lock(g_modulesInfoMutex); + g_modulesInfo.insert({name, {modAddress, symbolReader}}); + } + return S_OK; +} diff --git a/src/debug/debugger/symbolreader.cpp b/src/debug/debugger/symbolreader.cpp index 4c55a8a..06fd879 100644 --- a/src/debug/debugger/symbolreader.cpp +++ b/src/debug/debugger/symbolreader.cpp @@ -17,6 +17,7 @@ #include +#include "torelease.h" #include "symbolreader.h" std::string SymbolReader::coreClrPath; diff --git a/src/debug/debugger/symbolreader.h b/src/debug/debugger/symbolreader.h index 7c4cb84..8c73424 100644 --- a/src/debug/debugger/symbolreader.h +++ b/src/debug/debugger/symbolreader.h @@ -1,42 +1,3 @@ -#ifndef IfFailRet -#define IfFailRet(EXPR) do { Status = (EXPR); if(FAILED(Status)) { return (Status); } } while (0) -#endif - -#ifndef _countof -#define _countof(x) (sizeof(x)/sizeof(x[0])) -#endif - -#ifdef PAL_STDCPP_COMPAT -#define _iswprint PAL_iswprint -#define _wcslen PAL_wcslen -#define _wcsncmp PAL_wcsncmp -#define _wcsrchr PAL_wcsrchr -#define _wcscmp PAL_wcscmp -#define _wcschr PAL_wcschr -#define _wcscspn PAL_wcscspn -#define _wcscat PAL_wcscat -#define _wcsstr PAL_wcsstr -#else // PAL_STDCPP_COMPAT -#define _iswprint iswprint -#define _wcslen wcslen -#define _wcsncmp wcsncmp -#define _wcsrchr wcsrchr -#define _wcscmp wcscmp -#define _wcschr wcschr -#define _wcscspn wcscspn -#define _wcscat wcscat -#define _wcsstr wcsstr -#endif // !PAL_STDCPP_COMPAT - -typedef uintptr_t TADDR; -typedef ULONG64 CLRDATA_ADDRESS; - -// Convert between CLRDATA_ADDRESS and TADDR. -#define TO_TADDR(cdaddr) ((TADDR)(cdaddr)) -#define TO_CDADDR(taddr) ((CLRDATA_ADDRESS)(LONG_PTR)(taddr)) - -const int mdNameLen = 2048; - static const char *SymbolReaderDllName = "SOS.NETCore"; static const char *SymbolReaderClassName = "SOS.SymbolReader"; diff --git a/src/debug/debugger/torelease.h b/src/debug/debugger/torelease.h index 81612be..e48b86a 100644 --- a/src/debug/debugger/torelease.h +++ b/src/debug/debugger/torelease.h @@ -70,3 +70,42 @@ public: private: T* m_ptr; }; + +#ifndef IfFailRet +#define IfFailRet(EXPR) do { Status = (EXPR); if(FAILED(Status)) { return (Status); } } while (0) +#endif + +#ifndef _countof +#define _countof(x) (sizeof(x)/sizeof(x[0])) +#endif + +#ifdef PAL_STDCPP_COMPAT +#define _iswprint PAL_iswprint +#define _wcslen PAL_wcslen +#define _wcsncmp PAL_wcsncmp +#define _wcsrchr PAL_wcsrchr +#define _wcscmp PAL_wcscmp +#define _wcschr PAL_wcschr +#define _wcscspn PAL_wcscspn +#define _wcscat PAL_wcscat +#define _wcsstr PAL_wcsstr +#else // PAL_STDCPP_COMPAT +#define _iswprint iswprint +#define _wcslen wcslen +#define _wcsncmp wcsncmp +#define _wcsrchr wcsrchr +#define _wcscmp wcscmp +#define _wcschr wcschr +#define _wcscspn wcscspn +#define _wcscat wcscat +#define _wcsstr wcsstr +#endif // !PAL_STDCPP_COMPAT + +typedef uintptr_t TADDR; +typedef ULONG64 CLRDATA_ADDRESS; + +// Convert between CLRDATA_ADDRESS and TADDR. +#define TO_TADDR(cdaddr) ((TADDR)(cdaddr)) +#define TO_CDADDR(taddr) ((CLRDATA_ADDRESS)(LONG_PTR)(taddr)) + +const int mdNameLen = 2048; -- 2.7.4