diff options
Diffstat (limited to 'src/ToolBox/SOS/lldbplugin/services.cpp')
-rw-r--r-- | src/ToolBox/SOS/lldbplugin/services.cpp | 1692 |
1 files changed, 1692 insertions, 0 deletions
diff --git a/src/ToolBox/SOS/lldbplugin/services.cpp b/src/ToolBox/SOS/lldbplugin/services.cpp new file mode 100644 index 0000000000..f6b42139c4 --- /dev/null +++ b/src/ToolBox/SOS/lldbplugin/services.cpp @@ -0,0 +1,1692 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include <cstdarg> +#include <cstdlib> +#include "sosplugin.h" +#include <string.h> +#include <string> + +ULONG g_currentThreadIndex = -1; +ULONG g_currentThreadSystemId = -1; +char *g_coreclrDirectory; + +LLDBServices::LLDBServices(lldb::SBDebugger &debugger, lldb::SBCommandReturnObject &returnObject, lldb::SBProcess *process, lldb::SBThread *thread) : + m_ref(1), + m_debugger(debugger), + m_returnObject(returnObject), + m_currentProcess(process), + m_currentThread(thread) +{ + returnObject.SetStatus(lldb::eReturnStatusSuccessFinishResult); +} + +LLDBServices::~LLDBServices() +{ +} + +//---------------------------------------------------------------------------- +// IUnknown +//---------------------------------------------------------------------------- + +HRESULT +LLDBServices::QueryInterface( + REFIID InterfaceId, + PVOID* Interface + ) +{ + if (InterfaceId == __uuidof(IUnknown) || + InterfaceId == __uuidof(ILLDBServices)) + { + *Interface = (ILLDBServices*)this; + AddRef(); + return S_OK; + } + else + { + *Interface = NULL; + return E_NOINTERFACE; + } +} + +ULONG +LLDBServices::AddRef() +{ + LONG ref = InterlockedIncrement(&m_ref); + return ref; +} + +ULONG +LLDBServices::Release() +{ + LONG ref = InterlockedDecrement(&m_ref); + if (ref == 0) + { + delete this; + } + return ref; +} + +//---------------------------------------------------------------------------- +// ILLDBServices +//---------------------------------------------------------------------------- + +PCSTR +LLDBServices::GetCoreClrDirectory() +{ + return g_coreclrDirectory; +} + +DWORD_PTR +LLDBServices::GetExpression( + PCSTR exp) +{ + if (exp == nullptr) + { + return 0; + } + + lldb::SBFrame frame = GetCurrentFrame(); + if (!frame.IsValid()) + { + return 0; + } + + DWORD_PTR result = 0; + lldb::SBError error; + std::string str; + + // To be compatible with windbg/dbgeng, we need to emulate the default + // hex radix (because sos prints addresses and other hex values without + // the 0x) by first prepending 0x and if that fails use the actual + // undecorated expression. + str.append("0x"); + str.append(exp); + + result = GetExpression(frame, error, str.c_str()); + if (error.Fail()) + { + result = GetExpression(frame, error, exp); + } + + return result; +} + +// Internal function +DWORD_PTR +LLDBServices::GetExpression( + /* const */ lldb::SBFrame& frame, + lldb::SBError& error, + PCSTR exp) +{ + DWORD_PTR result = 0; + + lldb::SBValue value = frame.EvaluateExpression(exp, lldb::eNoDynamicValues); + if (value.IsValid()) + { + result = value.GetValueAsUnsigned(error); + } + + return result; +} + +// +// lldb doesn't have a way or API to unwind an arbitary context (IP, SP) +// and return the next frame so we have to stick with the native frames +// lldb has found and find the closest frame to the incoming context SP. +// +HRESULT +LLDBServices::VirtualUnwind( + DWORD threadID, + ULONG32 contextSize, + PBYTE context) +{ + lldb::SBProcess process; + lldb::SBThread thread; + + if (context == NULL || contextSize < sizeof(DT_CONTEXT)) + { + return E_INVALIDARG; + } + + process = GetCurrentProcess(); + if (!process.IsValid()) + { + return E_FAIL; + } + + thread = process.GetThreadByID(threadID); + if (!thread.IsValid()) + { + return E_FAIL; + } + + DT_CONTEXT *dtcontext = (DT_CONTEXT*)context; + lldb::SBFrame frameFound; + +#ifdef DBG_TARGET_AMD64 + DWORD64 spToFind = dtcontext->Rsp; +#elif DBG_TARGET_ARM + DWORD spToFind = dtcontext->Sp; +#endif + + int numFrames = thread.GetNumFrames(); + for (int i = 0; i < numFrames; i++) + { + lldb::SBFrame frame = thread.GetFrameAtIndex(i); + if (!frame.IsValid()) + { + break; + } + lldb::addr_t sp = frame.GetSP(); + + if ((i + 1) < numFrames) + { + lldb::SBFrame frameNext = thread.GetFrameAtIndex(i + 1); + if (frameNext.IsValid()) + { + lldb::addr_t spNext = frameNext.GetSP(); + + // An exact match of the current frame's SP would be nice + // but sometimes the incoming context is between lldb frames + if (spToFind >= sp && spToFind < spNext) + { + frameFound = frameNext; + break; + } + } + } + } + + if (!frameFound.IsValid()) + { + return E_FAIL; + } + + GetContextFromFrame(frameFound, dtcontext); + + return S_OK; +} + +bool +ExceptionBreakpointCallback( + void *baton, + lldb::SBProcess &process, + lldb::SBThread &thread, + lldb::SBBreakpointLocation &location) +{ + lldb::SBDebugger debugger = process.GetTarget().GetDebugger(); + + // Send the normal and error output to stdout/stderr since we + // don't have a return object from the command interpreter. + lldb::SBCommandReturnObject result; + result.SetImmediateOutputFile(stdout); + result.SetImmediateErrorFile(stderr); + + // Save the process and thread to be used by the current process/thread + // helper functions. + LLDBServices* client = new LLDBServices(debugger, result, &process, &thread); + return ((PFN_EXCEPTION_CALLBACK)baton)(client) == S_OK; +} + +lldb::SBBreakpoint g_exceptionbp; + +HRESULT +LLDBServices::SetExceptionCallback( + PFN_EXCEPTION_CALLBACK callback) +{ + if (!g_exceptionbp.IsValid()) + { + lldb::SBTarget target = m_debugger.GetSelectedTarget(); + if (!target.IsValid()) + { + return E_FAIL; + } + lldb::SBBreakpoint exceptionbp = target.BreakpointCreateForException(lldb::LanguageType::eLanguageTypeC_plus_plus, false, true); + if (!exceptionbp.IsValid()) + { + return E_FAIL; + } +#ifdef FLAGS_ANONYMOUS_ENUM + exceptionbp.AddName("DoNotDeleteOrDisable"); +#endif + exceptionbp.SetCallback(ExceptionBreakpointCallback, (void *)callback); + g_exceptionbp = exceptionbp; + } + return S_OK; +} + +HRESULT +LLDBServices::ClearExceptionCallback() +{ + if (g_exceptionbp.IsValid()) + { + lldb::SBTarget target = m_debugger.GetSelectedTarget(); + if (!target.IsValid()) + { + return E_FAIL; + } + target.BreakpointDelete(g_exceptionbp.GetID()); + g_exceptionbp = lldb::SBBreakpoint(); + } + return S_OK; +} + +//---------------------------------------------------------------------------- +// IDebugControl2 +//---------------------------------------------------------------------------- + +// Checks for a user interrupt, such a Ctrl-C +// or stop button. +// This method is reentrant. +HRESULT +LLDBServices::GetInterrupt() +{ + return E_FAIL; +} + +// Sends output through clients +// output callbacks if the mask is allowed +// by the current output control mask and +// according to the output distribution +// settings. +HRESULT +LLDBServices::Output( + ULONG mask, + PCSTR format, + ...) +{ + va_list args; + va_start (args, format); + HRESULT result = OutputVaList(mask, format, args); + va_end (args); + return result; +} + +HRESULT +LLDBServices::OutputVaList( + ULONG mask, + PCSTR format, + va_list args) +{ + HRESULT result = S_OK; + char str[1024]; + + va_list args_copy; + va_copy (args_copy, args); + + // Try and format our string into a fixed buffer first and see if it fits + size_t length = ::vsnprintf(str, sizeof(str), format, args); + if (length < sizeof(str)) + { + OutputString(mask, str); + } + else + { + // Our stack buffer wasn't big enough to contain the entire formatted + // string, so lets let vasprintf create the string for us! + char *str_ptr = nullptr; + length = ::vasprintf(&str_ptr, format, args_copy); + if (str_ptr) + { + OutputString(mask, str_ptr); + ::free (str_ptr); + } + else + { + result = E_FAIL; + } + } + + va_end (args_copy); + + return result; +} + +// The following methods allow direct control +// over the distribution of the given output +// for situations where something other than +// the default is desired. These methods require +// extra work in the engine so they should +// only be used when necessary. +HRESULT +LLDBServices::ControlledOutput( + ULONG outputControl, + ULONG mask, + PCSTR format, + ...) +{ + va_list args; + va_start (args, format); + HRESULT result = ControlledOutputVaList(outputControl, mask, format, args); + va_end (args); + return result; +} + +HRESULT +LLDBServices::ControlledOutputVaList( + ULONG outputControl, + ULONG mask, + PCSTR format, + va_list args) +{ + return OutputVaList(mask, format, args); +} + +// Returns information about the debuggee such +// as user vs. kernel, dump vs. live, etc. +HRESULT +LLDBServices::GetDebuggeeType( + PULONG debugClass, + PULONG qualifier) +{ + *debugClass = DEBUG_CLASS_USER_WINDOWS; + *qualifier = 0; + return S_OK; +} + +// Returns the page size for the currently executing +// processor context. The page size may vary between +// processor types. +HRESULT +LLDBServices::GetPageSize( + PULONG size) +{ + *size = 4096; + return S_OK; +} + +HRESULT +LLDBServices::GetExecutingProcessorType( + PULONG type) +{ +#ifdef DBG_TARGET_AMD64 + *type = IMAGE_FILE_MACHINE_AMD64; +#elif DBG_TARGET_ARM + *type = IMAGE_FILE_MACHINE_ARMNT; +#endif + return S_OK; +} + +HRESULT +LLDBServices::Execute( + ULONG outputControl, + PCSTR command, + ULONG flags) +{ + lldb::SBCommandInterpreter interpreter = m_debugger.GetCommandInterpreter(); + + lldb::SBCommandReturnObject result; + lldb::ReturnStatus status = interpreter.HandleCommand(command, result); + + return status <= lldb::eReturnStatusSuccessContinuingResult ? S_OK : E_FAIL; +} + +// PAL raise exception function and exception record pointer variable name +// See coreclr\src\pal\src\exception\seh-unwind.cpp for the details. This +// function depends on RtlpRaisException not being inlined or optimized. +#define FUNCTION_NAME "RtlpRaiseException" +#define VARIABLE_NAME "ExceptionRecord" + +HRESULT +LLDBServices::GetLastEventInformation( + PULONG type, + PULONG processId, + PULONG threadId, + PVOID extraInformation, + ULONG extraInformationSize, + PULONG extraInformationUsed, + PSTR description, + ULONG descriptionSize, + PULONG descriptionUsed) +{ + if (extraInformationSize < sizeof(DEBUG_LAST_EVENT_INFO_EXCEPTION) || + type == NULL || processId == NULL || threadId == NULL || extraInformationUsed == NULL) + { + return E_INVALIDARG; + } + + *type = DEBUG_EVENT_EXCEPTION; + *processId = 0; + *threadId = 0; + *extraInformationUsed = sizeof(DEBUG_LAST_EVENT_INFO_EXCEPTION); + + DEBUG_LAST_EVENT_INFO_EXCEPTION *pdle = (DEBUG_LAST_EVENT_INFO_EXCEPTION *)extraInformation; + pdle->FirstChance = 1; + + lldb::SBProcess process = GetCurrentProcess(); + if (!process.IsValid()) + { + return E_FAIL; + } + lldb::SBThread thread = GetCurrentThread(); + if (!thread.IsValid()) + { + return E_FAIL; + } + + *processId = process.GetProcessID(); + *threadId = thread.GetThreadID(); + + // Enumerate each stack frame at the special "throw" + // breakpoint and find the raise exception function + // with the exception record parameter. + int numFrames = thread.GetNumFrames(); + for (int i = 0; i < numFrames; i++) + { + lldb::SBFrame frame = thread.GetFrameAtIndex(i); + if (!frame.IsValid()) + { + break; + } + + const char *functionName = frame.GetFunctionName(); + if (functionName == NULL || strncmp(functionName, FUNCTION_NAME, sizeof(FUNCTION_NAME) - 1) != 0) + { + continue; + } + + lldb::SBValue exValue = frame.FindVariable(VARIABLE_NAME); + if (!exValue.IsValid()) + { + break; + } + + lldb::SBError error; + ULONG64 pExceptionRecord = exValue.GetValueAsUnsigned(error); + if (error.Fail()) + { + break; + } + + process.ReadMemory(pExceptionRecord, &pdle->ExceptionRecord, sizeof(pdle->ExceptionRecord), error); + if (error.Fail()) + { + break; + } + + return S_OK; + } + + return E_FAIL; +} + +HRESULT +LLDBServices::Disassemble( + ULONG64 offset, + ULONG flags, + PSTR buffer, + ULONG bufferSize, + PULONG disassemblySize, + PULONG64 endOffset) +{ + lldb::SBInstruction instruction; + lldb::SBInstructionList list; + lldb::SBTarget target; + lldb::SBAddress address; + lldb::SBError error; + lldb::SBData data; + std::string str; + HRESULT hr = S_OK; + ULONG size = 0; + uint8_t byte; + int cch; + + if (buffer == NULL) + { + hr = E_INVALIDARG; + goto exit; + } + *buffer = 0; + + target = m_debugger.GetSelectedTarget(); + if (!target.IsValid()) + { + hr = E_INVALIDARG; + goto exit; + } + address = target.ResolveLoadAddress(offset); + if (!address.IsValid()) + { + hr = E_INVALIDARG; + goto exit; + } + list = target.ReadInstructions(address, 1, "intel"); + if (!list.IsValid()) + { + hr = E_FAIL; + goto exit; + } + instruction = list.GetInstructionAtIndex(0); + if (!instruction.IsValid()) + { + hr = E_FAIL; + goto exit; + } + cch = snprintf(buffer, bufferSize, "%016llx ", (unsigned long long)offset); + buffer += cch; + bufferSize -= cch; + + size = instruction.GetByteSize(); + data = instruction.GetData(target); + for (int i = 0; i < size && bufferSize > 0; i++) + { + byte = data.GetUnsignedInt8(error, i); + if (error.Fail()) + { + hr = E_FAIL; + goto exit; + } + cch = snprintf(buffer, bufferSize, "%02x", byte); + buffer += cch; + bufferSize -= cch; + } + // Pad the data bytes to 16 chars + cch = size * 2; + while (bufferSize > 0) + { + *buffer++ = ' '; + bufferSize--; + if (++cch >= 21) + break; + } + + cch = snprintf(buffer, bufferSize, "%s", instruction.GetMnemonic(target)); + buffer += cch; + bufferSize -= cch; + + // Pad the mnemonic to 8 chars + while (bufferSize > 0) + { + *buffer++ = ' '; + bufferSize--; + if (++cch >= 8) + break; + } + snprintf(buffer, bufferSize, "%s\n", instruction.GetOperands(target)); + +exit: + if (disassemblySize != NULL) + { + *disassemblySize = size; + } + if (endOffset != NULL) + { + *endOffset = offset + size; + } + return hr; +} + +// Internal output string function +void +LLDBServices::OutputString( + ULONG mask, + PCSTR str) +{ + if (mask == DEBUG_OUTPUT_ERROR) + { + m_returnObject.SetStatus(lldb::eReturnStatusFailed); + } + // Can not use AppendMessage or AppendWarning because they add a newline. SetError + // can not be used for DEBUG_OUTPUT_ERROR mask because it caches the error strings + // seperately from the normal output so error/normal texts are not intermixed + // correctly. + m_returnObject.Printf("%s", str); +} + +//---------------------------------------------------------------------------- +// IDebugControl4 +//---------------------------------------------------------------------------- + +HRESULT +LLDBServices::GetContextStackTrace( + PVOID startContext, + ULONG startContextSize, + PDEBUG_STACK_FRAME frames, + ULONG framesSize, + PVOID frameContexts, + ULONG frameContextsSize, + ULONG frameContextsEntrySize, + PULONG framesFilled) +{ + DT_CONTEXT *currentContext = (DT_CONTEXT*)frameContexts; + PDEBUG_STACK_FRAME currentFrame = frames; + lldb::SBThread thread; + lldb::SBFrame frame; + ULONG cFrames = 0; + HRESULT hr = S_OK; + + // Doesn't support a starting context + if (startContext != NULL || frames == NULL || frameContexts == NULL || frameContextsEntrySize != sizeof(DT_CONTEXT)) + { + hr = E_INVALIDARG; + goto exit; + } + + thread = GetCurrentThread(); + if (!thread.IsValid()) + { + hr = E_FAIL; + goto exit; + } + + frame = thread.GetFrameAtIndex(0); + for (int i = 0; i < thread.GetNumFrames(); i++) + { + if (!frame.IsValid() || (cFrames > framesSize) || ((char *)currentContext > ((char *)frameContexts + frameContextsSize))) + { + break; + } + lldb::SBFrame framePrevious; + lldb::SBFrame frameNext; + + currentFrame->InstructionOffset = frame.GetPC(); + currentFrame->StackOffset = frame.GetSP(); + + currentFrame->FuncTableEntry = 0; + currentFrame->Params[0] = 0; + currentFrame->Params[1] = 0; + currentFrame->Params[2] = 0; + currentFrame->Params[3] = 0; + currentFrame->Virtual = i == 0 ? TRUE : FALSE; + currentFrame->FrameNumber = frame.GetFrameID(); + + frameNext = thread.GetFrameAtIndex(i + 1); + if (frameNext.IsValid()) + { + currentFrame->ReturnOffset = frameNext.GetPC(); + } + + if (framePrevious.IsValid()) + { + currentFrame->FrameOffset = framePrevious.GetSP(); + } + else + { + currentFrame->FrameOffset = frame.GetSP(); + } + + GetContextFromFrame(frame, currentContext); + + framePrevious = frame; + frame = frameNext; + currentContext++; + currentFrame++; + cFrames++; + } + +exit: + if (framesFilled != NULL) + { + *framesFilled = cFrames; + } + return hr; +} + +//---------------------------------------------------------------------------- +// IDebugDataSpaces +//---------------------------------------------------------------------------- + +HRESULT +LLDBServices::ReadVirtual( + ULONG64 offset, + PVOID buffer, + ULONG bufferSize, + PULONG bytesRead) +{ + lldb::SBError error; + size_t read = 0; + + lldb::SBProcess process = GetCurrentProcess(); + if (!process.IsValid()) + { + goto exit; + } + + read = process.ReadMemory(offset, buffer, bufferSize, error); + +exit: + if (bytesRead) + { + *bytesRead = read; + } + return error.Success() ? S_OK : E_FAIL; +} + +HRESULT +LLDBServices::WriteVirtual( + ULONG64 offset, + PVOID buffer, + ULONG bufferSize, + PULONG bytesWritten) +{ + lldb::SBError error; + size_t written = 0; + + lldb::SBProcess process = GetCurrentProcess(); + if (!process.IsValid()) + { + goto exit; + } + + written = process.WriteMemory(offset, buffer, bufferSize, error); + +exit: + if (bytesWritten) + { + *bytesWritten = written; + } + return error.Success() ? S_OK : E_FAIL; +} + +//---------------------------------------------------------------------------- +// IDebugSymbols +//---------------------------------------------------------------------------- + +HRESULT +LLDBServices::GetSymbolOptions( + PULONG options) +{ + *options = SYMOPT_LOAD_LINES; + return S_OK; +} + +HRESULT +LLDBServices::GetNameByOffset( + ULONG64 offset, + PSTR nameBuffer, + ULONG nameBufferSize, + PULONG nameSize, + PULONG64 displacement) +{ + ULONG64 disp = DEBUG_INVALID_OFFSET; + HRESULT hr = S_OK; + + lldb::SBTarget target; + lldb::SBAddress address; + lldb::SBModule module; + lldb::SBFileSpec file; + lldb::SBSymbol symbol; + std::string str; + + target = m_debugger.GetSelectedTarget(); + if (!target.IsValid()) + { + hr = E_FAIL; + goto exit; + } + + address = target.ResolveLoadAddress(offset); + if (!address.IsValid()) + { + hr = E_INVALIDARG; + goto exit; + } + + module = address.GetModule(); + if (!module.IsValid()) + { + hr = E_FAIL; + goto exit; + } + + file = module.GetFileSpec(); + if (file.IsValid()) + { + str.append(file.GetFilename()); + } + + symbol = address.GetSymbol(); + if (symbol.IsValid()) + { + lldb::SBAddress startAddress = symbol.GetStartAddress(); + disp = address.GetOffset() - startAddress.GetOffset(); + + const char *name = symbol.GetName(); + if (name) + { + if (file.IsValid()) + { + str.append("!"); + } + str.append(name); + } + } + + str.append(1, '\0'); + +exit: + if (nameSize) + { + *nameSize = str.length(); + } + if (nameBuffer) + { + str.copy(nameBuffer, nameBufferSize); + } + if (displacement) + { + *displacement = disp; + } + return hr; +} + +HRESULT +LLDBServices::GetNumberModules( + PULONG loaded, + PULONG unloaded) +{ + ULONG numModules = 0; + HRESULT hr = S_OK; + + lldb::SBTarget target = m_debugger.GetSelectedTarget(); + if (!target.IsValid()) + { + hr = E_FAIL; + goto exit; + } + + numModules = target.GetNumModules(); + +exit: + if (loaded) + { + *loaded = numModules; + } + if (unloaded) + { + *unloaded = 0; + } + return hr; +} + +HRESULT LLDBServices::GetModuleByIndex( + ULONG index, + PULONG64 base) +{ + ULONG64 moduleBase = UINT64_MAX; + + lldb::SBTarget target; + lldb::SBModule module; + + target = m_debugger.GetSelectedTarget(); + if (!target.IsValid()) + { + goto exit; + } + + module = target.GetModuleAtIndex(index); + if (!module.IsValid()) + { + goto exit; + } + + moduleBase = GetModuleBase(target, module); + +exit: + if (base) + { + *base = moduleBase; + } + return moduleBase == UINT64_MAX ? E_FAIL : S_OK; +} + +HRESULT +LLDBServices::GetModuleByModuleName( + PCSTR name, + ULONG startIndex, + PULONG index, + PULONG64 base) +{ + ULONG64 moduleBase = UINT64_MAX; + ULONG moduleIndex = UINT32_MAX; + + lldb::SBTarget target; + lldb::SBModule module; + lldb::SBFileSpec fileSpec; + fileSpec.SetFilename(name); + + target = m_debugger.GetSelectedTarget(); + if (!target.IsValid()) + { + goto exit; + } + + module = target.FindModule(fileSpec); + if (!module.IsValid()) + { + goto exit; + } + + moduleBase = GetModuleBase(target, module); + + if (index) + { + int numModules = target.GetNumModules(); + for (int mi = startIndex; mi < numModules; mi++) + { + lldb::SBModule mod = target.GetModuleAtIndex(mi); + if (module == mod) + { + moduleIndex = mi; + break; + } + } + } + +exit: + if (index) + { + *index = moduleIndex; + } + if (base) + { + *base = moduleBase; + } + return moduleBase == UINT64_MAX ? E_FAIL : S_OK; +} + +HRESULT +LLDBServices::GetModuleByOffset( + ULONG64 offset, + ULONG startIndex, + PULONG index, + PULONG64 base) +{ + ULONG64 moduleBase = UINT64_MAX; + ULONG moduleIndex = UINT32_MAX; + + lldb::SBTarget target; + int numModules; + + target = m_debugger.GetSelectedTarget(); + if (!target.IsValid()) + { + goto exit; + } + + numModules = target.GetNumModules(); + for (int mi = startIndex; mi < numModules; mi++) + { + lldb::SBModule module = target.GetModuleAtIndex(mi); + + int numSections = module.GetNumSections(); + for (int si = 0; si < numSections; si++) + { + lldb::SBSection section = module.GetSectionAtIndex(si); + if (section.IsValid()) + { + lldb::addr_t baseAddress = section.GetLoadAddress(target); + if (baseAddress != LLDB_INVALID_ADDRESS) + { + if (offset > baseAddress) + { + if ((offset - baseAddress) < section.GetByteSize()) + { + moduleIndex = mi; + moduleBase = baseAddress - section.GetFileOffset(); + goto exit; + } + } + } + } + } + } + +exit: + if (index) + { + *index = moduleIndex; + } + if (base) + { + *base = moduleBase; + } + return moduleBase == UINT64_MAX ? E_FAIL : S_OK; +} + +HRESULT +LLDBServices::GetModuleNames( + ULONG index, + ULONG64 base, + PSTR imageNameBuffer, + ULONG imageNameBufferSize, + PULONG imageNameSize, + PSTR moduleNameBuffer, + ULONG moduleNameBufferSize, + PULONG moduleNameSize, + PSTR loadedImageNameBuffer, + ULONG loadedImageNameBufferSize, + PULONG loadedImageNameSize) +{ + lldb::SBTarget target; + lldb::SBFileSpec fileSpec; + HRESULT hr = S_OK; + + target = m_debugger.GetSelectedTarget(); + if (!target.IsValid()) + { + hr = E_FAIL; + goto exit; + } + + if (index != DEBUG_ANY_ID) + { + lldb::SBModule module = target.GetModuleAtIndex(index); + if (module.IsValid()) + { + fileSpec = module.GetFileSpec(); + } + } + else + { + int numModules = target.GetNumModules(); + for (int mi = 0; mi < numModules; mi++) + { + lldb::SBModule module = target.GetModuleAtIndex(mi); + if (module.IsValid()) + { + ULONG64 moduleBase = GetModuleBase(target, module); + if (base == moduleBase) + { + fileSpec = module.GetFileSpec(); + break; + } + } + } + } + + if (!fileSpec.IsValid()) + { + hr = E_FAIL; + goto exit; + } + +exit: + if (imageNameBuffer) + { + int size = fileSpec.GetPath(imageNameBuffer, imageNameBufferSize); + if (imageNameSize) + { + *imageNameSize = size; + } + } + if (moduleNameBuffer) + { + const char *fileName = fileSpec.GetFilename(); + if (fileName == NULL) + { + fileName = ""; + } + stpncpy(moduleNameBuffer, fileName, moduleNameBufferSize); + if (moduleNameSize) + { + *moduleNameSize = strlen(fileName); + } + } + if (loadedImageNameBuffer) + { + int size = fileSpec.GetPath(loadedImageNameBuffer, loadedImageNameBufferSize); + if (loadedImageNameSize) + { + *loadedImageNameSize = size; + } + } + return hr; +} + +HRESULT +LLDBServices::GetLineByOffset( + ULONG64 offset, + PULONG fileLine, + PSTR fileBuffer, + ULONG fileBufferSize, + PULONG fileSize, + PULONG64 displacement) +{ + ULONG64 disp = DEBUG_INVALID_OFFSET; + HRESULT hr = S_OK; + ULONG line = 0; + + lldb::SBTarget target; + lldb::SBAddress address; + lldb::SBFileSpec file; + lldb::SBLineEntry lineEntry; + std::string str; + + target = m_debugger.GetSelectedTarget(); + if (!target.IsValid()) + { + hr = E_FAIL; + goto exit; + } + + address = target.ResolveLoadAddress(offset); + if (!address.IsValid()) + { + hr = E_INVALIDARG; + goto exit; + } + + if (displacement) + { + lldb::SBSymbol symbol = address.GetSymbol(); + if (symbol.IsValid()) + { + lldb::SBAddress startAddress = symbol.GetStartAddress(); + disp = address.GetOffset() - startAddress.GetOffset(); + } + } + + lineEntry = address.GetLineEntry(); + if (!lineEntry.IsValid()) + { + hr = E_FAIL; + goto exit; + } + + line = lineEntry.GetLine(); + file = lineEntry.GetFileSpec(); + if (file.IsValid()) + { + str.append(file.GetDirectory()); + str.append(1, '/'); + str.append(file.GetFilename()); + } + + str.append(1, '\0'); + +exit: + if (fileLine) + { + *fileLine = line; + } + if (fileSize) + { + *fileSize = str.length(); + } + if (fileBuffer) + { + str.copy(fileBuffer, fileBufferSize); + } + if (displacement) + { + *displacement = disp; + } + return hr; +} + +HRESULT +LLDBServices::GetSourceFileLineOffsets( + PCSTR file, + PULONG64 buffer, + ULONG bufferLines, + PULONG fileLines) +{ + if (fileLines != NULL) + { + *fileLines = (ULONG)-1; + } + return E_NOTIMPL; +} + +HRESULT +LLDBServices::FindSourceFile( + ULONG startElement, + PCSTR file, + ULONG flags, + PULONG foundElement, + PSTR buffer, + ULONG bufferSize, + PULONG foundSize) +{ + return E_NOTIMPL; +} + +// Internal functions +PCSTR +LLDBServices::GetModuleDirectory( + PCSTR name) +{ + lldb::SBTarget target = m_debugger.GetSelectedTarget(); + if (!target.IsValid()) + { + return NULL; + } + + lldb::SBFileSpec fileSpec; + fileSpec.SetFilename(name); + + lldb::SBModule module = target.FindModule(fileSpec); + if (!module.IsValid()) + { + return NULL; + } + + return module.GetFileSpec().GetDirectory(); +} + +ULONG64 +LLDBServices::GetModuleBase( + /* const */ lldb::SBTarget& target, + /* const */ lldb::SBModule& module) +{ + // Find the first section with an valid base address + int numSections = module.GetNumSections(); + for (int si = 0; si < numSections; si++) + { + lldb::SBSection section = module.GetSectionAtIndex(si); + if (section.IsValid()) + { + lldb::addr_t baseAddress = section.GetLoadAddress(target); + if (baseAddress != LLDB_INVALID_ADDRESS) + { + return baseAddress - section.GetFileOffset(); + } + } + } + + return UINT64_MAX; +} + +//---------------------------------------------------------------------------- +// IDebugSystemObjects +//---------------------------------------------------------------------------- + +HRESULT +LLDBServices::GetCurrentProcessId( + PULONG id) +{ + if (id == NULL) + { + return E_INVALIDARG; + } + + lldb::SBProcess process = GetCurrentProcess(); + if (!process.IsValid()) + { + *id = 0; + return E_FAIL; + } + + *id = process.GetProcessID(); + return S_OK; +} + +HRESULT +LLDBServices::GetCurrentThreadId( + PULONG id) +{ + if (id == NULL) + { + return E_INVALIDARG; + } + + lldb::SBThread thread = GetCurrentThread(); + if (!thread.IsValid()) + { + *id = 0; + return E_FAIL; + } + + // This is allow the a valid current TID to be returned to + // workaround a bug in lldb on core dumps. + if (g_currentThreadIndex != -1) + { + *id = g_currentThreadIndex; + return S_OK; + } + + *id = thread.GetIndexID(); + return S_OK; +} + +HRESULT +LLDBServices::SetCurrentThreadId( + ULONG id) +{ + lldb::SBProcess process = GetCurrentProcess(); + if (!process.IsValid()) + { + return E_FAIL; + } + + if (!process.SetSelectedThreadByIndexID(id)) + { + return E_FAIL; + } + + return S_OK; +} + +HRESULT +LLDBServices::GetCurrentThreadSystemId( + PULONG sysId) +{ + if (sysId == NULL) + { + return E_INVALIDARG; + } + + lldb::SBThread thread = GetCurrentThread(); + if (!thread.IsValid()) + { + *sysId = 0; + return E_FAIL; + } + + // This is allow the a valid current TID to be returned to + // workaround a bug in lldb on core dumps. + if (g_currentThreadSystemId != -1) + { + *sysId = g_currentThreadSystemId; + return S_OK; + } + + *sysId = thread.GetThreadID(); + return S_OK; +} + +HRESULT +LLDBServices::GetThreadIdBySystemId( + ULONG sysId, + PULONG threadId) +{ + HRESULT hr = E_FAIL; + ULONG id = 0; + + lldb::SBProcess process; + lldb::SBThread thread; + + if (threadId == NULL) + { + return E_INVALIDARG; + } + + process = GetCurrentProcess(); + if (!process.IsValid()) + { + goto exit; + } + + // If we have a "fake" thread OS (system) id and a fake thread index, + // we need to return fake thread index. + if (g_currentThreadSystemId == sysId && g_currentThreadIndex != -1) + { + id = g_currentThreadIndex; + } + else + { + thread = process.GetThreadByID(sysId); + if (!thread.IsValid()) + { + goto exit; + } + + id = thread.GetIndexID(); + } + hr = S_OK; + +exit: + *threadId = id; + return hr; +} + +HRESULT +LLDBServices::GetThreadContextById( + /* in */ ULONG32 threadID, + /* in */ ULONG32 contextFlags, + /* in */ ULONG32 contextSize, + /* out */ PBYTE context) +{ + lldb::SBProcess process; + lldb::SBThread thread; + lldb::SBFrame frame; + DT_CONTEXT *dtcontext; + HRESULT hr = E_FAIL; + + if (context == NULL || contextSize < sizeof(DT_CONTEXT)) + { + goto exit; + } + memset(context, 0, contextSize); + + process = GetCurrentProcess(); + if (!process.IsValid()) + { + goto exit; + } + + // If we have a "fake" thread OS (system) id and a fake thread index, + // use the fake thread index to get the context. + if (g_currentThreadSystemId == threadID && g_currentThreadIndex != -1) + { + thread = process.GetThreadByIndexID(g_currentThreadIndex); + } + else + { + thread = process.GetThreadByID(threadID); + } + + if (!thread.IsValid()) + { + goto exit; + } + + frame = thread.GetFrameAtIndex(0); + if (!frame.IsValid()) + { + goto exit; + } + + dtcontext = (DT_CONTEXT*)context; + dtcontext->ContextFlags = contextFlags; + + GetContextFromFrame(frame, dtcontext); + hr = S_OK; + +exit: + return hr; +} + +// Internal function +void +LLDBServices::GetContextFromFrame( + /* const */ lldb::SBFrame& frame, + DT_CONTEXT *dtcontext) +{ +#ifdef DBG_TARGET_AMD64 + dtcontext->Rip = frame.GetPC(); + dtcontext->Rsp = frame.GetSP(); + dtcontext->Rbp = frame.GetFP(); + dtcontext->EFlags = GetRegister(frame, "rflags"); + + dtcontext->Rax = GetRegister(frame, "rax"); + dtcontext->Rbx = GetRegister(frame, "rbx"); + dtcontext->Rcx = GetRegister(frame, "rcx"); + dtcontext->Rdx = GetRegister(frame, "rdx"); + dtcontext->Rsi = GetRegister(frame, "rsi"); + dtcontext->Rdi = GetRegister(frame, "rdi"); + dtcontext->R8 = GetRegister(frame, "r8"); + dtcontext->R9 = GetRegister(frame, "r9"); + dtcontext->R10 = GetRegister(frame, "r10"); + dtcontext->R11 = GetRegister(frame, "r11"); + dtcontext->R12 = GetRegister(frame, "r12"); + dtcontext->R13 = GetRegister(frame, "r13"); + dtcontext->R14 = GetRegister(frame, "r14"); + dtcontext->R15 = GetRegister(frame, "r15"); + + dtcontext->SegCs = GetRegister(frame, "cs"); + dtcontext->SegSs = GetRegister(frame, "ss"); + dtcontext->SegDs = GetRegister(frame, "ds"); + dtcontext->SegEs = GetRegister(frame, "es"); + dtcontext->SegFs = GetRegister(frame, "fs"); + dtcontext->SegGs = GetRegister(frame, "gs"); +#elif DBG_TARGET_ARM + dtcontext->Pc = frame.GetPC(); + dtcontext->Sp = frame.GetSP(); + dtcontext->Lr = GetRegister(frame, "lr"); + dtcontext->Cpsr = GetRegister(frame, "cpsr"); + + dtcontext->R0 = GetRegister(frame, "r0"); + dtcontext->R1 = GetRegister(frame, "r1"); + dtcontext->R2 = GetRegister(frame, "r2"); + dtcontext->R3 = GetRegister(frame, "r3"); + dtcontext->R4 = GetRegister(frame, "r4"); + dtcontext->R5 = GetRegister(frame, "r5"); + dtcontext->R6 = GetRegister(frame, "r6"); + dtcontext->R7 = GetRegister(frame, "r7"); + dtcontext->R8 = GetRegister(frame, "r8"); + dtcontext->R9 = GetRegister(frame, "r9"); + dtcontext->R10 = GetRegister(frame, "r10"); + dtcontext->R11 = GetRegister(frame, "r11"); + dtcontext->R12 = GetRegister(frame, "r12"); +#endif +} + +// Internal function +DWORD_PTR +LLDBServices::GetRegister( + /* const */ lldb::SBFrame& frame, + const char *name) +{ + lldb::SBValue regValue = frame.FindRegister(name); + + lldb::SBError error; + DWORD_PTR result = regValue.GetValueAsUnsigned(error); + + return result; +} + +//---------------------------------------------------------------------------- +// IDebugRegisters +//---------------------------------------------------------------------------- + +HRESULT +LLDBServices::GetValueByName( + PCSTR name, + PDWORD_PTR debugValue) +{ + lldb::SBFrame frame = GetCurrentFrame(); + if (!frame.IsValid()) + { + *debugValue = 0; + return E_FAIL; + } + + lldb::SBValue value = frame.FindRegister(name); + if (!value.IsValid()) + { + *debugValue = 0; + return E_FAIL; + } + + *debugValue = value.GetValueAsUnsigned(); + return S_OK; +} + +HRESULT +LLDBServices::GetInstructionOffset( + PULONG64 offset) +{ + lldb::SBFrame frame = GetCurrentFrame(); + if (!frame.IsValid()) + { + *offset = 0; + return E_FAIL; + } + + *offset = frame.GetPC(); + return S_OK; +} + +HRESULT +LLDBServices::GetStackOffset( + PULONG64 offset) +{ + lldb::SBFrame frame = GetCurrentFrame(); + if (!frame.IsValid()) + { + *offset = 0; + return E_FAIL; + } + + *offset = frame.GetSP(); + return S_OK; +} + +HRESULT +LLDBServices::GetFrameOffset( + PULONG64 offset) +{ + lldb::SBFrame frame = GetCurrentFrame(); + if (!frame.IsValid()) + { + *offset = 0; + return E_FAIL; + } + + *offset = frame.GetFP(); + return S_OK; +} + +//---------------------------------------------------------------------------- +// Helper functions +//---------------------------------------------------------------------------- + +lldb::SBProcess +LLDBServices::GetCurrentProcess() +{ + lldb::SBProcess process; + + if (m_currentProcess == nullptr) + { + lldb::SBTarget target = m_debugger.GetSelectedTarget(); + if (target.IsValid()) + { + process = target.GetProcess(); + } + } + else + { + process = *m_currentProcess; + } + + return process; +} + +lldb::SBThread +LLDBServices::GetCurrentThread() +{ + lldb::SBThread thread; + + if (m_currentThread == nullptr) + { + lldb::SBProcess process = GetCurrentProcess(); + if (process.IsValid()) + { + thread = process.GetSelectedThread(); + } + } + else + { + thread = *m_currentThread; + } + + return thread; +} + +lldb::SBFrame +LLDBServices::GetCurrentFrame() +{ + lldb::SBFrame frame; + + lldb::SBThread thread = GetCurrentThread(); + if (thread.IsValid()) + { + frame = thread.GetSelectedFrame(); + } + + return frame; +} |