summaryrefslogtreecommitdiff
path: root/src/ToolBox/SOS/lldbplugin
diff options
context:
space:
mode:
Diffstat (limited to 'src/ToolBox/SOS/lldbplugin')
-rw-r--r--src/ToolBox/SOS/lldbplugin/.gitmirrorall1
-rw-r--r--src/ToolBox/SOS/lldbplugin/CMakeLists.txt115
-rw-r--r--src/ToolBox/SOS/lldbplugin/coreruncommand.cpp47
-rw-r--r--src/ToolBox/SOS/lldbplugin/inc/lldbservices.h565
-rw-r--r--src/ToolBox/SOS/lldbplugin/mstypes.h106
-rw-r--r--src/ToolBox/SOS/lldbplugin/services.cpp1692
-rw-r--r--src/ToolBox/SOS/lldbplugin/services.h274
-rw-r--r--src/ToolBox/SOS/lldbplugin/setclrpathcommand.cpp53
-rw-r--r--src/ToolBox/SOS/lldbplugin/setsostidcommand.cpp64
-rw-r--r--src/ToolBox/SOS/lldbplugin/soscommand.cpp152
-rw-r--r--src/ToolBox/SOS/lldbplugin/sosplugin.cpp21
-rw-r--r--src/ToolBox/SOS/lldbplugin/sosplugin.h28
12 files changed, 3118 insertions, 0 deletions
diff --git a/src/ToolBox/SOS/lldbplugin/.gitmirrorall b/src/ToolBox/SOS/lldbplugin/.gitmirrorall
new file mode 100644
index 0000000000..9ee5c57b99
--- /dev/null
+++ b/src/ToolBox/SOS/lldbplugin/.gitmirrorall
@@ -0,0 +1 @@
+This folder will be mirrored by the Git-TFS Mirror recursively. \ No newline at end of file
diff --git a/src/ToolBox/SOS/lldbplugin/CMakeLists.txt b/src/ToolBox/SOS/lldbplugin/CMakeLists.txt
new file mode 100644
index 0000000000..9f90a54056
--- /dev/null
+++ b/src/ToolBox/SOS/lldbplugin/CMakeLists.txt
@@ -0,0 +1,115 @@
+project(sosplugin)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+# Set the RPATH of sos so that it can find dependencies without needing to set LD_LIBRARY_PATH
+# For more information: http://www.cmake.org/Wiki/CMake_RPATH_handling.
+if (CORECLR_SET_RPATH)
+ set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
+ if(CLR_CMAKE_PLATFORM_DARWIN)
+ set(CMAKE_INSTALL_RPATH "@loader_path")
+ else()
+ set(CMAKE_INSTALL_RPATH "\$ORIGIN")
+ endif(CLR_CMAKE_PLATFORM_DARWIN)
+endif (CORECLR_SET_RPATH)
+
+add_definitions(-DPAL_STDCPP_COMPAT)
+
+if(CLR_CMAKE_PLATFORM_ARCH_AMD64)
+ add_definitions(-D_TARGET_AMD64_=1)
+ add_definitions(-DDBG_TARGET_64BIT=1)
+ add_definitions(-DDBG_TARGET_AMD64=1)
+ add_definitions(-DDBG_TARGET_WIN64=1)
+ add_definitions(-DBIT64)
+elseif(CLR_CMAKE_PLATFORM_ARCH_ARM)
+ add_definitions(-D_TARGET_ARM_=1)
+ add_definitions(-DDBG_TARGET_32BIT=1)
+ add_definitions(-DDBG_TARGET_ARM=1)
+elseif(CLR_CMAKE_PLATFORM_ARCH_ARM64)
+ message(WARNING "lldb is not supported on linux/aarch64 yet")
+ return()
+endif()
+
+set(ENABLE_LLDBPLUGIN ${CLR_CMAKE_PLATFORM_UNIX} CACHE BOOL "Enable building the SOS plugin for LLDB.")
+set(REQUIRE_LLDBPLUGIN ${CLR_CMAKE_PLATFORM_LINUX} CACHE BOOL "Require building the SOS plugin for LLDB.")
+
+set(LLVM_HOST_DIR "$ENV{LLVM_HOME}")
+set(WITH_LLDB_LIBS "${LLVM_HOST_DIR}/lib" CACHE PATH "Path to LLDB libraries")
+set(WITH_LLDB_INCLUDES "${LLVM_HOST_DIR}/include" CACHE PATH "Path to LLDB headers")
+
+if(NOT ENABLE_LLDBPLUGIN)
+ return()
+endif()
+
+# Check for LLDB library
+find_library(LLDB NAMES LLDB lldb lldb-3.8 lldb-3.6 lldb-3.5 PATHS "${WITH_LLDB_LIBS}" PATH_SUFFIXES llvm NO_DEFAULT_PATH)
+find_library(LLDB NAMES LLDB lldb lldb-3.8 lldb-3.6 lldb-3.5 PATH_SUFFIXES llvm)
+if(LLDB STREQUAL LLDB-NOTFOUND)
+ if(REQUIRE_LLDBPLUGIN)
+ message(FATAL_ERROR "Cannot find lldb-3.5, lldb-3.6 or lldb-3.8. Try installing lldb-3.6-dev (or the appropriate package for your platform)")
+ else()
+ message(WARNING "Cannot find lldb-3.5,lldb-3.6 or lldb-3.8. Try installing lldb-3.6-dev (or the appropriate package for your platform)")
+ endif()
+ return()
+endif()
+
+message(STATUS "LLDB: ${LLDB}")
+
+# Check for LLDB headers
+
+find_path(LLDB_H "lldb/API/LLDB.h" PATHS "${WITH_LLDB_INCLUDES}" NO_DEFAULT_PATH)
+find_path(LLDB_H "lldb/API/LLDB.h")
+if(LLDB_H STREQUAL LLDB_H-NOTFOUND)
+ find_path(LLDB_H "lldb/API/LLDB.h" PATHS "/usr/lib/llvm-3.8/include")
+ if(LLDB_H STREQUAL LLDB_H-NOTFOUND)
+ find_path(LLDB_H "lldb/API/LLDB.h" PATHS "/usr/lib/llvm-3.6/include")
+ if(LLDB_H STREQUAL LLDB_H-NOTFOUND)
+ find_path(LLDB_H "lldb/API/LLDB.h" PATHS "/usr/lib/llvm-3.5/include")
+ if(LLDB_H STREQUAL LLDB_H-NOTFOUND)
+ if(REQUIRE_LLDBPLUGIN)
+ message(FATAL_ERROR "Cannot find LLDB.h. Try installing lldb-3.6-dev (or the appropriate package for your platform)")
+ else()
+ message(WARNING "Cannot find LLDB.h Try installing lldb-3.6-dev (or the appropriate package for your platform)")
+ endif()
+ return()
+ endif()
+ endif()
+ endif()
+endif()
+
+message(STATUS "LLDB_H: ${LLDB_H}")
+
+add_compile_options(-Wno-delete-non-virtual-dtor)
+
+include_directories(inc)
+include_directories("${LLDB_H}")
+include_directories(${CLR_DIR}/src/debug/inc)
+include_directories(${CLR_DIR}/src/inc)
+include_directories(${CLR_DIR}/src/coreclr/hosts/inc)
+include_directories(${CLR_DIR}/src/coreclr/hosts/unixcoreruncommon)
+
+set(SOURCES
+ sosplugin.cpp
+ soscommand.cpp
+ setclrpathcommand.cpp
+ setsostidcommand.cpp
+ services.cpp
+)
+
+if(UPPERCASE_CMAKE_BUILD_TYPE STREQUAL DEBUG OR UPPERCASE_CMAKE_BUILD_TYPE STREQUAL CHECKED)
+ list(APPEND SOURCES
+ coreruncommand.cpp
+ ${CLR_DIR}/src/coreclr/hosts/unixcorerun/corerun.cpp
+ ${CLR_DIR}/src/coreclr/hosts/unixcoreruncommon/coreruncommon.cpp
+ )
+endif(UPPERCASE_CMAKE_BUILD_TYPE STREQUAL DEBUG OR UPPERCASE_CMAKE_BUILD_TYPE STREQUAL CHECKED)
+
+_add_library(sosplugin SHARED ${SOURCES})
+add_dependencies(sosplugin sos)
+
+if (CLR_CMAKE_PLATFORM_UNIX)
+ target_link_libraries(sosplugin ${LLDB})
+endif()
+
+# add the install targets
+install_clr(sosplugin)
diff --git a/src/ToolBox/SOS/lldbplugin/coreruncommand.cpp b/src/ToolBox/SOS/lldbplugin/coreruncommand.cpp
new file mode 100644
index 0000000000..9187906acd
--- /dev/null
+++ b/src/ToolBox/SOS/lldbplugin/coreruncommand.cpp
@@ -0,0 +1,47 @@
+// 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 "sosplugin.h"
+#include <dlfcn.h>
+#include <string>
+
+extern int corerun(const int argc, const char* argv[]);
+
+class corerunCommand : public lldb::SBCommandPluginInterface
+{
+public:
+ corerunCommand()
+ {
+ }
+
+ virtual bool
+ DoExecute (lldb::SBDebugger debugger,
+ char** arguments,
+ lldb::SBCommandReturnObject &result)
+ {
+ if (arguments)
+ {
+ int argc = 0;
+ char **argv = arguments;
+ for (const char* arg = *arguments; arg; arg = *(++arguments))
+ {
+ ++argc;
+ }
+ int exitcode = corerun((const int)argc, (const char**)argv);
+ if (exitcode != 0)
+ {
+ result.SetError("corerun failed");
+ }
+ }
+ return result.Succeeded();
+ }
+};
+
+bool
+corerunCommandInitialize(lldb::SBDebugger debugger)
+{
+ lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter();
+ lldb::SBCommand command = interpreter.AddCommand("corerun", new corerunCommand(), "Run a managed app inside the debugger. corerun <exe-path> <managed-program-path> <command-line-args>");
+ return true;
+}
diff --git a/src/ToolBox/SOS/lldbplugin/inc/lldbservices.h b/src/ToolBox/SOS/lldbplugin/inc/lldbservices.h
new file mode 100644
index 0000000000..16702b116d
--- /dev/null
+++ b/src/ToolBox/SOS/lldbplugin/inc/lldbservices.h
@@ -0,0 +1,565 @@
+// 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.
+
+//----------------------------------------------------------------------------
+//
+// LLDB debugger services for sos
+//
+//----------------------------------------------------------------------------
+
+#ifndef __LLDBSERVICES_H__
+#define __LLDBSERVICES_H__
+
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Output mask bits.
+// Normal output.
+#define DEBUG_OUTPUT_NORMAL 0x00000001
+// Error output.
+#define DEBUG_OUTPUT_ERROR 0x00000002
+// Warnings.
+#define DEBUG_OUTPUT_WARNING 0x00000004
+// Additional output.
+#define DEBUG_OUTPUT_VERBOSE 0x00000008
+// Prompt output.
+#define DEBUG_OUTPUT_PROMPT 0x00000010
+// Register dump before prompt.
+#define DEBUG_OUTPUT_PROMPT_REGISTERS 0x00000020
+// Warnings specific to extension operation.
+#define DEBUG_OUTPUT_EXTENSION_WARNING 0x00000040
+// Debuggee debug output, such as from OutputDebugString.
+#define DEBUG_OUTPUT_DEBUGGEE 0x00000080
+// Debuggee-generated prompt, such as from DbgPrompt.
+#define DEBUG_OUTPUT_DEBUGGEE_PROMPT 0x00000100
+// Symbol messages, such as for !sym noisy.
+#define DEBUG_OUTPUT_SYMBOLS 0x00000200
+
+// Execute and ExecuteCommandFile flags.
+// These flags only apply to the command
+// text itself; output from the executed
+// command is controlled by the output
+// control parameter.
+// Default execution. Command is logged
+// but not output.
+#define DEBUG_EXECUTE_DEFAULT 0x00000000
+// Echo commands during execution. In
+// ExecuteCommandFile also echoes the prompt
+// for each line of the file.
+#define DEBUG_EXECUTE_ECHO 0x00000001
+// Do not log or output commands during execution.
+// Overridden by DEBUG_EXECUTE_ECHO.
+#define DEBUG_EXECUTE_NOT_LOGGED 0x00000002
+// If this flag is not set an empty string
+// to Execute will repeat the last Execute
+// string.
+#define DEBUG_EXECUTE_NO_REPEAT 0x00000004
+
+// Classes of debuggee. Each class
+// has different qualifiers for specific
+// kinds of debuggees.
+#define DEBUG_CLASS_UNINITIALIZED 0
+#define DEBUG_CLASS_KERNEL 1
+#define DEBUG_CLASS_USER_WINDOWS 2
+#define DEBUG_CLASS_IMAGE_FILE 3
+
+// Generic dump types. These can be used
+// with either user or kernel sessions.
+// Session-type-specific aliases are also
+// provided.
+#define DEBUG_DUMP_SMALL 1024
+#define DEBUG_DUMP_DEFAULT 1025
+#define DEBUG_DUMP_FULL 1026
+#define DEBUG_DUMP_IMAGE_FILE 1027
+#define DEBUG_DUMP_TRACE_LOG 1028
+#define DEBUG_DUMP_WINDOWS_CE 1029
+
+#define IMAGE_FILE_MACHINE_I386 0x014c // Intel 386.
+#define IMAGE_FILE_MACHINE_ARMNT 0x01c4 // ARM Thumb-2 Little-Endian
+#define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8)
+
+// Execution status codes used for waiting,
+// for returning current status and for
+// event method return values.
+#define DEBUG_STATUS_NO_CHANGE 0
+#define DEBUG_STATUS_GO 1
+#define DEBUG_STATUS_GO_HANDLED 2
+#define DEBUG_STATUS_GO_NOT_HANDLED 3
+#define DEBUG_STATUS_STEP_OVER 4
+#define DEBUG_STATUS_STEP_INTO 5
+#define DEBUG_STATUS_BREAK 6
+#define DEBUG_STATUS_NO_DEBUGGEE 7
+#define DEBUG_STATUS_STEP_BRANCH 8
+#define DEBUG_STATUS_IGNORE_EVENT 9
+#define DEBUG_STATUS_RESTART_REQUESTED 10
+#define DEBUG_STATUS_REVERSE_GO 11
+#define DEBUG_STATUS_REVERSE_STEP_BRANCH 12
+#define DEBUG_STATUS_REVERSE_STEP_OVER 13
+#define DEBUG_STATUS_REVERSE_STEP_INTO 14
+#define DEBUG_STATUS_OUT_OF_SYNC 15
+#define DEBUG_STATUS_WAIT_INPUT 16
+#define DEBUG_STATUS_TIMEOUT 17
+
+#define DEBUG_STATUS_MASK 0x1f
+
+#define DEBUG_EVENT_EXCEPTION 0x00000002
+
+#ifdef DEFINE_EXCEPTION_RECORD
+
+#define EXCEPTION_MAXIMUM_PARAMETERS 15
+
+// This copy of the "64" bit record has been modified
+// by removing the alignment field to make it the same
+// as the _EXCEPTION_RECORD used in the pal defined in
+// pal.h.
+typedef struct _EXCEPTION_RECORD64 {
+ DWORD ExceptionCode;
+ DWORD ExceptionFlags;
+ DWORD64 ExceptionRecord;
+ DWORD64 ExceptionAddress;
+ DWORD NumberParameters;
+ DWORD64 ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
+} EXCEPTION_RECORD64, *PEXCEPTION_RECORD64;
+
+#endif // DEFINE_EXCEPTION_RECORD
+
+typedef struct _DEBUG_LAST_EVENT_INFO_EXCEPTION
+{
+ EXCEPTION_RECORD64 ExceptionRecord;
+ ULONG FirstChance;
+} DEBUG_LAST_EVENT_INFO_EXCEPTION, *PDEBUG_LAST_EVENT_INFO_EXCEPTION;
+
+//
+// Information about a module.
+//
+
+// Flags.
+#define DEBUG_MODULE_LOADED 0x00000000
+#define DEBUG_MODULE_UNLOADED 0x00000001
+#define DEBUG_MODULE_USER_MODE 0x00000002
+#define DEBUG_MODULE_EXPLICIT 0x00000008
+#define DEBUG_MODULE_SECONDARY 0x00000010
+#define DEBUG_MODULE_SYNTHETIC 0x00000020
+#define DEBUG_MODULE_SYM_BAD_CHECKSUM 0x00010000
+
+// Symbol types.
+#define DEBUG_SYMTYPE_NONE 0
+#define DEBUG_SYMTYPE_COFF 1
+#define DEBUG_SYMTYPE_CODEVIEW 2
+#define DEBUG_SYMTYPE_PDB 3
+#define DEBUG_SYMTYPE_EXPORT 4
+#define DEBUG_SYMTYPE_DEFERRED 5
+#define DEBUG_SYMTYPE_SYM 6
+#define DEBUG_SYMTYPE_DIA 7
+
+typedef struct _DEBUG_MODULE_PARAMETERS
+{
+ ULONG64 Base;
+ ULONG Size;
+ ULONG TimeDateStamp;
+ ULONG Checksum;
+ ULONG Flags;
+ ULONG SymbolType;
+ ULONG ImageNameSize;
+ ULONG ModuleNameSize;
+ ULONG LoadedImageNameSize;
+ ULONG SymbolFileNameSize;
+ ULONG MappedImageNameSize;
+ ULONG64 Reserved[2];
+} DEBUG_MODULE_PARAMETERS, *PDEBUG_MODULE_PARAMETERS;
+
+// FindSourceFile flags.
+#define DEBUG_FIND_SOURCE_DEFAULT 0x00000000
+// Returns fully-qualified paths only. If this
+// is not set the path returned may be relative.
+#define DEBUG_FIND_SOURCE_FULL_PATH 0x00000001
+// Scans all the path elements for a match and
+// returns the one that has the most similarity
+// between the given file and the matching element.
+#define DEBUG_FIND_SOURCE_BEST_MATCH 0x00000002
+// Do not search source server paths.
+#define DEBUG_FIND_SOURCE_NO_SRCSRV 0x00000004
+// Restrict FindSourceFileAndToken to token lookup only.
+#define DEBUG_FIND_SOURCE_TOKEN_LOOKUP 0x00000008
+
+// A special value marking an offset that should not
+// be treated as a valid offset. This is only used
+// in special situations where it is unlikely that
+// this value would be a valid offset.
+#define DEBUG_INVALID_OFFSET ((ULONG64)-1)
+
+// General unspecified ID constant.
+#define DEBUG_ANY_ID 0xffffffff
+
+typedef struct _DEBUG_STACK_FRAME
+{
+ ULONG64 InstructionOffset;
+ ULONG64 ReturnOffset;
+ ULONG64 FrameOffset;
+ ULONG64 StackOffset;
+ ULONG64 FuncTableEntry;
+ ULONG64 Params[4];
+ ULONG64 Reserved[6];
+ BOOL Virtual;
+ ULONG FrameNumber;
+} DEBUG_STACK_FRAME, *PDEBUG_STACK_FRAME;
+
+#define DBG_FRAME_DEFAULT 0 // the same as INLINE_FRAME_CONTEXT_INIT in dbghelp.h
+#define DBG_FRAME_IGNORE_INLINE 0xFFFFFFFF // the same as INLINE_FRAME_CONTEXT_IGNORE in dbghelp.h
+
+typedef struct _DEBUG_STACK_FRAME_EX
+{
+ // First DEBUG_STACK_FRAME structure
+ ULONG64 InstructionOffset;
+ ULONG64 ReturnOffset;
+ ULONG64 FrameOffset;
+ ULONG64 StackOffset;
+ ULONG64 FuncTableEntry;
+ ULONG64 Params[4];
+ ULONG64 Reserved[6];
+ BOOL Virtual;
+ ULONG FrameNumber;
+
+ // Extended DEBUG_STACK_FRAME fields.
+ ULONG InlineFrameContext;
+ ULONG Reserved1; // For alignment purpose.
+} DEBUG_STACK_FRAME_EX, *PDEBUG_STACK_FRAME_EX;
+
+// The types of inline frame context.
+#define STACK_FRAME_TYPE_INIT 0x00
+#define STACK_FRAME_TYPE_STACK 0x01
+#define STACK_FRAME_TYPE_INLINE 0x02
+#define STACK_FRAME_TYPE_RA 0x80 // Whether the instruction pointer is the current IP or a RA from callee frame.
+#define STACK_FRAME_TYPE_IGNORE 0xFF
+
+//
+// options that are set/returned by SymSetOptions() & SymGetOptions()
+// these are used as a mask
+//
+#define SYMOPT_LOAD_LINES 0x00000010
+
+interface ILLDBServices;
+typedef HRESULT (*PFN_EXCEPTION_CALLBACK)(ILLDBServices *services);
+
+//----------------------------------------------------------------------------
+// ILLDBServices
+//----------------------------------------------------------------------------
+
+MIDL_INTERFACE("2E6C569A-9E14-4DA4-9DFC-CDB73A532566")
+ILLDBServices : public IUnknown
+{
+public:
+ //----------------------------------------------------------------------------
+ // ILLDBServices
+ //----------------------------------------------------------------------------
+
+ // Returns the coreclr module directory found by lldb plugin
+ // in the target process.
+ virtual PCSTR GetCoreClrDirectory() = 0;
+
+ // Evaluates a lldb expression into a value.
+ virtual DWORD_PTR GetExpression(
+ /* [in] */ PCSTR exp) = 0;
+
+ // Unwind one native stack frame given a thread and register context
+ virtual HRESULT VirtualUnwind(
+ /* [in] */ DWORD threadID,
+ /* [in] */ ULONG32 contextSize,
+ /* [in, out, size_is(contextSize)] */ PBYTE context) = 0;
+
+ // Set an exception throw callback
+ virtual HRESULT SetExceptionCallback(
+ /* [in] */ PFN_EXCEPTION_CALLBACK callback) = 0;
+
+ // Clear the exception throw callback
+ virtual HRESULT ClearExceptionCallback() = 0;
+
+ //------------------------------------------------
+ // IDebugControl2
+ //------------------------------------------------
+
+ // Checks for a user interrupt, such a Ctrl-C
+ // or stop button.
+ // This method is reentrant.
+ virtual HRESULT GetInterrupt(
+ void) = 0;
+
+ virtual HRESULT OutputVaList(
+ ULONG mask,
+ PCSTR format,
+ va_list args) = 0;
+
+ // Returns information about the debuggee such
+ // as user vs. kernel, dump vs. live, etc.
+ virtual HRESULT GetDebuggeeType(
+ PULONG debugClass,
+ PULONG qualifier) = 0;
+
+ // Returns the page size for the currently executing
+ // processor context. The page size may vary between
+ // processor types.
+ virtual HRESULT GetPageSize(
+ PULONG size) = 0;
+
+ // Returns the type of processor used in the
+ // current processor context.
+ virtual HRESULT GetExecutingProcessorType(
+ PULONG type) = 0;
+
+ // Executes the given command string.
+ // If the string has multiple commands
+ // Execute will not return until all
+ // of them have been executed. If this
+ // requires waiting for the debuggee to
+ // execute an internal wait will be done
+ // so Execute can take an arbitrary amount
+ // of time.
+ virtual HRESULT Execute(
+ ULONG outputControl,
+ PCSTR command,
+ ULONG flags) = 0;
+
+ // Retrieves information about the last event that occurred.
+ // EventType is one of the event callback mask bits.
+ // ExtraInformation contains additional event-specific
+ // information. Not all events have additional information.
+ virtual HRESULT GetLastEventInformation(
+ PULONG type,
+ PULONG processId,
+ PULONG threadId,
+ PVOID extraInformation,
+ ULONG extraInformationSize,
+ PULONG extraInformationUsed,
+ PSTR description,
+ ULONG descriptionSize,
+ PULONG descriptionUsed) = 0;
+
+ virtual HRESULT Disassemble(
+ ULONG64 offset,
+ ULONG flags,
+ PSTR buffer,
+ ULONG bufferSize,
+ PULONG disassemblySize,
+ PULONG64 endOffset) = 0;
+
+ //----------------------------------------------------------------------------
+ // IDebugControl4
+ //----------------------------------------------------------------------------
+
+ // Stack tracing with a full initial context
+ // and full context return for each frame.
+ // The FrameContextsSize parameter is the total
+ // byte size of FrameContexts. FrameContextsEntrySize
+ // gives the byte size of each entry in
+ // FrameContexts.
+ virtual HRESULT GetContextStackTrace(
+ PVOID startContext,
+ ULONG startContextSize,
+ PDEBUG_STACK_FRAME frames,
+ ULONG framesSize,
+ PVOID frameContexts,
+ ULONG frameContextsSize,
+ ULONG frameContextsEntrySize,
+ PULONG framesFilled) = 0;
+
+ //------------------------------------------------
+ // IDebugDataSpaces
+ //------------------------------------------------
+
+ virtual HRESULT ReadVirtual(
+ ULONG64 offset,
+ PVOID buffer,
+ ULONG bufferSize,
+ PULONG bytesRead) = 0;
+
+ virtual HRESULT WriteVirtual(
+ ULONG64 offset,
+ PVOID buffer,
+ ULONG bufferSize,
+ PULONG bytesWritten) = 0;
+
+ //------------------------------------------------
+ // IDebugSymbols
+ //------------------------------------------------
+
+ // Controls the symbol options used during
+ // symbol operations.
+ // Uses the same flags as dbghelps SymSetOptions.
+ virtual HRESULT GetSymbolOptions(
+ PULONG options) = 0;
+
+ virtual HRESULT GetNameByOffset(
+ ULONG64 offset,
+ PSTR nameBuffer,
+ ULONG nameBufferSize,
+ PULONG nameSize,
+ PULONG64 displacement) = 0;
+
+ // Enumerates the engines list of modules
+ // loaded for the current process. This may
+ // or may not match the system module list
+ // for the process. Reload can be used to
+ // synchronize the engines list with the system
+ // if necessary.
+ // Some sessions also track recently unloaded
+ // code modules for help in analyzing failures
+ // where an attempt is made to call unloaded code.
+ // These modules are indexed after the loaded
+ // modules.
+ virtual HRESULT GetNumberModules(
+ PULONG loaded,
+ PULONG unloaded) = 0;
+
+ virtual HRESULT GetModuleByIndex(
+ ULONG index,
+ PULONG64 base) = 0;
+
+ // The module name may not be unique.
+ // This method returns the first match.
+ virtual HRESULT GetModuleByModuleName(
+ PCSTR name,
+ ULONG startIndex,
+ PULONG index,
+ PULONG64 base) = 0;
+
+ // Offset can be any offset within
+ // the module extent. Extents may
+ // not be unique when including unloaded
+ // drivers. This method returns the
+ // first match.
+ virtual HRESULT GetModuleByOffset(
+ ULONG64 offset,
+ ULONG startIndex,
+ PULONG index,
+ PULONG64 base) = 0;
+
+ // If Index is DEBUG_ANY_ID the base address
+ // is used to look up the module instead.
+ virtual HRESULT GetModuleNames(
+ ULONG index,
+ ULONG64 base,
+ PSTR imageNameBuffer,
+ ULONG imageNameBufferSize,
+ PULONG imageNameSize,
+ PSTR moduleNameBuffer,
+ ULONG moduleNameBufferSize,
+ PULONG moduleNameSize,
+ PSTR loadedImageNameBuffer,
+ ULONG loadedImageNameBufferSize,
+ PULONG loadedImageNameSize) = 0;
+
+ HRESULT virtual GetLineByOffset(
+ ULONG64 offset,
+ PULONG line,
+ PSTR fileBuffer,
+ ULONG fileBufferSize,
+ PULONG fileSize,
+ PULONG64 displacement) = 0;
+
+ HRESULT virtual GetSourceFileLineOffsets(
+ PCSTR file,
+ PULONG64 buffer,
+ ULONG bufferLines,
+ PULONG fileLines) = 0;
+
+ // Uses the given file path and the source path
+ // information to try and locate an existing file.
+ // The given file path is merged with elements
+ // of the source path and checked for existence.
+ // If a match is found the element used is returned.
+ // A starting element can be specified to restrict
+ // the search to a subset of the path elements;
+ // this can be useful when checking for multiple
+ // matches along the source path.
+ // The returned element can be 1, indicating
+ // the file was found directly and not on the path.
+ HRESULT virtual FindSourceFile(
+ ULONG startElement,
+ PCSTR file,
+ ULONG flags,
+ PULONG foundElement,
+ PSTR buffer,
+ ULONG bufferSize,
+ PULONG foundSize) = 0;
+
+ //------------------------------------------------
+ // IDebugSystemObjects
+ //------------------------------------------------
+
+ virtual HRESULT GetCurrentProcessId(
+ PULONG id) = 0;
+
+ // Controls implicit thread used by the
+ // debug engine. The debuggers current
+ // thread is just a piece of data held
+ // by the debugger for calls which use
+ // thread-specific information. In those
+ // calls the debuggers current thread is used.
+ // The debuggers current thread is not related
+ // to any system thread attribute.
+ // IDs for threads are small integer IDs
+ // maintained by the engine. They are not
+ // related to system thread IDs.
+ virtual HRESULT GetCurrentThreadId(
+ PULONG id) = 0;
+
+ virtual HRESULT SetCurrentThreadId(
+ ULONG id) = 0;
+
+ // Returns the system unique ID for the current thread.
+ // Not currently supported when kernel debugging.
+ virtual HRESULT GetCurrentThreadSystemId(
+ PULONG sysId) = 0;
+
+ // Looks up a debugger thread ID for the given
+ // system thread ID.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ virtual HRESULT GetThreadIdBySystemId(
+ ULONG sysId,
+ PULONG id) = 0;
+
+ // This is a special sos/lldb function used to implement the ICLRDataTarget interface and
+ // not actually part of dbgeng's IDebugSystemObjects interface.
+ virtual HRESULT GetThreadContextById(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 contextFlags,
+ /* [in] */ ULONG32 contextSize,
+ /* [out, size_is(contextSize)] */ PBYTE context) = 0;
+
+ //------------------------------------------------
+ // IDebugRegister
+ //------------------------------------------------
+
+ // This is the combination of dbgeng's GetIndexByName and GetValue and not
+ // actually part of the dbgeng's IDebugRegister interface.
+ virtual HRESULT GetValueByName(
+ PCSTR name,
+ PDWORD_PTR value) = 0;
+
+ // Abstracted pieces of processor information.
+ // The mapping of these values to architectural
+ // registers is architecture-specific and their
+ // interpretation and existence may vary. They
+ // are intended to be directly compatible with
+ // calls which take this information, such as
+ // stack walking.
+ virtual HRESULT GetInstructionOffset(
+ PULONG64 offset) = 0;
+
+ virtual HRESULT GetStackOffset(
+ PULONG64 offset) = 0;
+
+ virtual HRESULT GetFrameOffset(
+ PULONG64 offset) = 0;
+};
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // #ifndef __LLDBSERVICES_H__
diff --git a/src/ToolBox/SOS/lldbplugin/mstypes.h b/src/ToolBox/SOS/lldbplugin/mstypes.h
new file mode 100644
index 0000000000..b5eee921a3
--- /dev/null
+++ b/src/ToolBox/SOS/lldbplugin/mstypes.h
@@ -0,0 +1,106 @@
+// 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.
+
+// Contains some definitions duplicated from pal.h, palrt.h, rpc.h,
+// etc. because they have various conflicits with the linux standard
+// runtime h files like wchar_t, memcpy, etc.
+
+#include <../../../pal/inc/pal_mstypes.h>
+
+#define S_OK (HRESULT)0x00000000
+#define S_FALSE (HRESULT)0x00000001
+#define E_NOTIMPL (HRESULT)0x80004001
+#define E_FAIL (HRESULT)0x80004005
+#define E_INVALIDARG (HRESULT)0x80070057
+#define E_NOINTERFACE (HRESULT)0x80004002
+
+#define MAX_PATH 260
+
+// Platform-specific library naming
+//
+#ifdef __APPLE__
+#define MAKEDLLNAME_W(name) u"lib" name u".dylib"
+#define MAKEDLLNAME_A(name) "lib" name ".dylib"
+#elif defined(_AIX)
+#define MAKEDLLNAME_W(name) L"lib" name L".a"
+#define MAKEDLLNAME_A(name) "lib" name ".a"
+#elif defined(__hppa__) || defined(_IA64_)
+#define MAKEDLLNAME_W(name) L"lib" name L".sl"
+#define MAKEDLLNAME_A(name) "lib" name ".sl"
+#else
+#define MAKEDLLNAME_W(name) u"lib" name u".so"
+#define MAKEDLLNAME_A(name) "lib" name ".so"
+#endif
+
+#if defined(_MSC_VER) || defined(__llvm__)
+#define DECLSPEC_ALIGN(x) __declspec(align(x))
+#else
+#define DECLSPEC_ALIGN(x)
+#endif
+
+#define interface struct
+#define DECLSPEC_UUID(x) __declspec(uuid(x))
+#define DECLSPEC_NOVTABLE
+#define MIDL_INTERFACE(x) struct DECLSPEC_UUID(x) DECLSPEC_NOVTABLE
+
+#ifdef __cplusplus
+#define REFGUID const GUID &
+#else
+#define REFGUID const GUID *
+#endif
+
+#ifdef __cplusplus
+extern "C++" {
+#include "string.h"
+#if !defined _SYS_GUID_OPERATOR_EQ_ && !defined _NO_SYS_GUID_OPERATOR_EQ_
+#define _SYS_GUID_OPERATOR_EQ_
+inline int IsEqualGUID(REFGUID rguid1, REFGUID rguid2)
+ { return !memcmp(&rguid1, &rguid2, sizeof(GUID)); }
+inline int operator==(REFGUID guidOne, REFGUID guidOther)
+ { return IsEqualGUID(guidOne,guidOther); }
+inline int operator!=(REFGUID guidOne, REFGUID guidOther)
+ { return !IsEqualGUID(guidOne,guidOther); }
+#endif
+};
+#endif // __cplusplus
+
+typedef GUID IID;
+#ifdef __cplusplus
+#define REFIID const IID &
+#else
+#define REFIID const IID *
+#endif
+#define IID_NULL GUID_NULL
+#define IsEqualIID(riid1, riid2) IsEqualGUID(riid1, riid2)
+
+MIDL_INTERFACE("00000000-0000-0000-C000-000000000046")
+IUnknown
+{
+public:
+ virtual HRESULT QueryInterface(
+ REFIID riid,
+ void **ppvObject) = 0;
+
+ virtual ULONG AddRef( void) = 0;
+
+ virtual ULONG Release( void) = 0;
+};
+
+EXTERN_C
+inline
+LONG
+InterlockedIncrement(
+ LONG volatile *lpAddend)
+{
+ return __sync_add_and_fetch(lpAddend, (LONG)1);
+}
+
+EXTERN_C
+inline
+LONG
+InterlockedDecrement(
+ LONG volatile *lpAddend)
+{
+ return __sync_sub_and_fetch(lpAddend, (LONG)1);
+} \ No newline at end of file
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;
+}
diff --git a/src/ToolBox/SOS/lldbplugin/services.h b/src/ToolBox/SOS/lldbplugin/services.h
new file mode 100644
index 0000000000..3c384c9076
--- /dev/null
+++ b/src/ToolBox/SOS/lldbplugin/services.h
@@ -0,0 +1,274 @@
+// 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>
+
+class LLDBServices : public ILLDBServices
+{
+private:
+ LONG m_ref;
+ lldb::SBDebugger &m_debugger;
+ lldb::SBCommandReturnObject &m_returnObject;
+
+ lldb::SBProcess *m_currentProcess;
+ lldb::SBThread *m_currentThread;
+
+ void OutputString(ULONG mask, PCSTR str);
+ ULONG64 GetModuleBase(lldb::SBTarget& target, lldb::SBModule& module);
+ DWORD_PTR GetExpression(lldb::SBFrame& frame, lldb::SBError& error, PCSTR exp);
+ void GetContextFromFrame(lldb::SBFrame& frame, DT_CONTEXT *dtcontext);
+ DWORD_PTR GetRegister(lldb::SBFrame& frame, const char *name);
+
+ lldb::SBProcess GetCurrentProcess();
+ lldb::SBThread GetCurrentThread();
+ lldb::SBFrame GetCurrentFrame();
+
+public:
+ LLDBServices(lldb::SBDebugger &debugger, lldb::SBCommandReturnObject &returnObject, lldb::SBProcess *process = nullptr, lldb::SBThread *thread = nullptr);
+ ~LLDBServices();
+
+ //----------------------------------------------------------------------------
+ // IUnknown
+ //----------------------------------------------------------------------------
+
+ HRESULT QueryInterface(
+ REFIID InterfaceId,
+ PVOID* Interface);
+
+ ULONG AddRef();
+
+ ULONG Release();
+
+ //----------------------------------------------------------------------------
+ // ILLDBServices
+ //----------------------------------------------------------------------------
+
+ PCSTR GetCoreClrDirectory();
+
+ DWORD_PTR GetExpression(
+ PCSTR exp);
+
+ HRESULT VirtualUnwind(
+ DWORD threadID,
+ ULONG32 contextSize,
+ PBYTE context);
+
+ HRESULT SetExceptionCallback(
+ PFN_EXCEPTION_CALLBACK callback);
+
+ HRESULT ClearExceptionCallback();
+
+ //----------------------------------------------------------------------------
+ // IDebugControl2
+ //----------------------------------------------------------------------------
+
+ HRESULT GetInterrupt();
+
+ HRESULT Output(
+ ULONG mask,
+ PCSTR format,
+ ...);
+
+ HRESULT OutputVaList(
+ ULONG mask,
+ PCSTR format,
+ va_list args);
+
+ HRESULT ControlledOutput(
+ ULONG outputControl,
+ ULONG mask,
+ PCSTR format,
+ ...);
+
+ HRESULT ControlledOutputVaList(
+ ULONG outputControl,
+ ULONG mask,
+ PCSTR format,
+ va_list args);
+
+ HRESULT GetDebuggeeType(
+ PULONG debugClass,
+ PULONG qualifier);
+
+ HRESULT GetPageSize(
+ PULONG size);
+
+ HRESULT GetExecutingProcessorType(
+ PULONG type);
+
+ HRESULT Execute(
+ ULONG outputControl,
+ PCSTR command,
+ ULONG flags);
+
+ HRESULT GetLastEventInformation(
+ PULONG type,
+ PULONG processId,
+ PULONG threadId,
+ PVOID extraInformation,
+ ULONG extraInformationSize,
+ PULONG extraInformationUsed,
+ PSTR description,
+ ULONG descriptionSize,
+ PULONG descriptionUsed);
+
+ HRESULT Disassemble(
+ ULONG64 offset,
+ ULONG flags,
+ PSTR buffer,
+ ULONG bufferSize,
+ PULONG disassemblySize,
+ PULONG64 endOffset);
+
+ //----------------------------------------------------------------------------
+ // IDebugControl4
+ //----------------------------------------------------------------------------
+
+ HRESULT
+ GetContextStackTrace(
+ PVOID startContext,
+ ULONG startContextSize,
+ PDEBUG_STACK_FRAME frames,
+ ULONG framesSize,
+ PVOID frameContexts,
+ ULONG frameContextsSize,
+ ULONG frameContextsEntrySize,
+ PULONG framesFilled);
+
+ //----------------------------------------------------------------------------
+ // IDebugDataSpaces
+ //----------------------------------------------------------------------------
+
+ HRESULT ReadVirtual(
+ ULONG64 offset,
+ PVOID buffer,
+ ULONG bufferSize,
+ PULONG bytesRead);
+
+ HRESULT WriteVirtual(
+ ULONG64 offset,
+ PVOID buffer,
+ ULONG bufferSize,
+ PULONG bytesWritten);
+
+ //----------------------------------------------------------------------------
+ // IDebugSymbols
+ //----------------------------------------------------------------------------
+
+ HRESULT GetSymbolOptions(
+ PULONG options);
+
+ HRESULT GetNameByOffset(
+ ULONG64 offset,
+ PSTR nameBuffer,
+ ULONG nameBufferSize,
+ PULONG nameSize,
+ PULONG64 displacement);
+
+ HRESULT GetNumberModules(
+ PULONG loaded,
+ PULONG unloaded);
+
+ HRESULT GetModuleByIndex(
+ ULONG index,
+ PULONG64 base);
+
+ HRESULT GetModuleByModuleName(
+ PCSTR name,
+ ULONG startIndex,
+ PULONG index,
+ PULONG64 base);
+
+ HRESULT GetModuleByOffset(
+ ULONG64 offset,
+ ULONG startIndex,
+ PULONG index,
+ PULONG64 base);
+
+ HRESULT GetModuleNames(
+ ULONG index,
+ ULONG64 base,
+ PSTR imageNameBuffer,
+ ULONG imageNameBufferSize,
+ PULONG imageNameSize,
+ PSTR moduleNameBuffer,
+ ULONG moduleNameBufferSize,
+ PULONG moduleNameSize,
+ PSTR loadedImageNameBuffer,
+ ULONG loadedImageNameBufferSize,
+ PULONG loadedImageNameSize);
+
+ HRESULT GetLineByOffset(
+ ULONG64 offset,
+ PULONG line,
+ PSTR fileBuffer,
+ ULONG fileBufferSize,
+ PULONG fileSize,
+ PULONG64 displacement);
+
+ HRESULT GetSourceFileLineOffsets(
+ PCSTR file,
+ PULONG64 buffer,
+ ULONG bufferLines,
+ PULONG fileLines);
+
+ HRESULT FindSourceFile(
+ ULONG startElement,
+ PCSTR file,
+ ULONG flags,
+ PULONG foundElement,
+ PSTR buffer,
+ ULONG bufferSize,
+ PULONG foundSize);
+
+ //----------------------------------------------------------------------------
+ // IDebugSystemObjects
+ //----------------------------------------------------------------------------
+
+ HRESULT GetCurrentProcessId(
+ PULONG id);
+
+ HRESULT GetCurrentThreadId(
+ PULONG id);
+
+ HRESULT SetCurrentThreadId(
+ ULONG id);
+
+ HRESULT GetCurrentThreadSystemId(
+ PULONG sysId);
+
+ HRESULT GetThreadIdBySystemId(
+ ULONG sysId,
+ PULONG threadId);
+
+ HRESULT GetThreadContextById(
+ ULONG32 threadID,
+ ULONG32 contextFlags,
+ ULONG32 contextSize,
+ PBYTE context);
+
+ //----------------------------------------------------------------------------
+ // IDebugRegisters
+ //----------------------------------------------------------------------------
+
+ HRESULT GetValueByName(
+ PCSTR name,
+ PDWORD_PTR debugValue);
+
+ HRESULT GetInstructionOffset(
+ PULONG64 offset);
+
+ HRESULT GetStackOffset(
+ PULONG64 offset);
+
+ HRESULT GetFrameOffset(
+ PULONG64 offset);
+
+ //----------------------------------------------------------------------------
+ // LLDBServices (internal)
+ //----------------------------------------------------------------------------
+
+ PCSTR GetModuleDirectory(
+ PCSTR name);
+};
diff --git a/src/ToolBox/SOS/lldbplugin/setclrpathcommand.cpp b/src/ToolBox/SOS/lldbplugin/setclrpathcommand.cpp
new file mode 100644
index 0000000000..2208306671
--- /dev/null
+++ b/src/ToolBox/SOS/lldbplugin/setclrpathcommand.cpp
@@ -0,0 +1,53 @@
+// 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 "sosplugin.h"
+#include <dlfcn.h>
+#include <string.h>
+#include <string>
+#include <stdlib.h>
+#include <limits.h>
+
+class setclrpathCommand : public lldb::SBCommandPluginInterface
+{
+public:
+ setclrpathCommand()
+ {
+ }
+
+ virtual bool
+ DoExecute (lldb::SBDebugger debugger,
+ char** arguments,
+ lldb::SBCommandReturnObject &result)
+ {
+ if (arguments[0] == NULL)
+ {
+ result.Printf("Load path for sos/dac/dbi: '%s'\n", g_coreclrDirectory == NULL ? "<none>" : g_coreclrDirectory);
+ }
+ else {
+ if (g_coreclrDirectory != NULL)
+ {
+ free(g_coreclrDirectory);
+ }
+
+ std::string path(arguments[0]);
+ if (path[path.length() - 1] != '/')
+ {
+ path.append("/");
+ }
+
+ g_coreclrDirectory = strdup(path.c_str());
+ result.Printf("Set load path for sos/dac/dbi to '%s'\n", g_coreclrDirectory);
+ }
+ return result.Succeeded();
+ }
+};
+
+bool
+setclrpathCommandInitialize(lldb::SBDebugger debugger)
+{
+ lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter();
+ lldb::SBCommand command = interpreter.AddCommand("setclrpath", new setclrpathCommand(), "Set the path to load coreclr sos/dac/dbi files. setclrpath <path>");
+ return true;
+}
diff --git a/src/ToolBox/SOS/lldbplugin/setsostidcommand.cpp b/src/ToolBox/SOS/lldbplugin/setsostidcommand.cpp
new file mode 100644
index 0000000000..c7d6a1ba25
--- /dev/null
+++ b/src/ToolBox/SOS/lldbplugin/setsostidcommand.cpp
@@ -0,0 +1,64 @@
+// 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 "sosplugin.h"
+#include <dlfcn.h>
+#include <string.h>
+#include <string>
+#include <stdlib.h>
+#include <limits.h>
+
+class setsostidCommand : public lldb::SBCommandPluginInterface
+{
+public:
+ setsostidCommand()
+ {
+ }
+
+ virtual bool
+ DoExecute(lldb::SBDebugger debugger,
+ char** arguments,
+ lldb::SBCommandReturnObject &result)
+ {
+ if (arguments[0] == NULL)
+ {
+ if (g_currentThreadSystemId == -1 || g_currentThreadIndex == -1)
+ {
+ result.Printf("sos OS tid not mapped\n");
+ }
+ else {
+ result.Printf("sos OS tid 0x%x mapped to lldb thread index %d\n",
+ g_currentThreadSystemId, g_currentThreadIndex);
+ }
+ }
+ else if (strcmp(arguments[0], "-clear") == 0) {
+ g_currentThreadIndex = -1;
+ g_currentThreadSystemId = -1;
+ result.Printf("Cleared sos OS tid/index\n");
+ }
+ else if (arguments[1] == NULL)
+ {
+ result.Printf("Need thread index parameter that maps to the OS tid\n");
+ }
+ else
+ {
+ ULONG tid = strtoul(arguments[0], NULL, 16);
+ g_currentThreadSystemId = tid;
+
+ ULONG index = strtoul(arguments[1], NULL, 16);
+ g_currentThreadIndex = index;
+
+ result.Printf("Mapped sos OS tid 0x%x to lldb thread index %d\n", tid, index);
+ }
+ return result.Succeeded();
+ }
+};
+
+bool
+setsostidCommandInitialize(lldb::SBDebugger debugger)
+{
+ lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter();
+ lldb::SBCommand command = interpreter.AddCommand("setsostid", new setsostidCommand(), "Set the current os tid/thread index instead of using the one lldb provides. setsostid <tid> <index>");
+ return true;
+}
diff --git a/src/ToolBox/SOS/lldbplugin/soscommand.cpp b/src/ToolBox/SOS/lldbplugin/soscommand.cpp
new file mode 100644
index 0000000000..4ca7ce37f3
--- /dev/null
+++ b/src/ToolBox/SOS/lldbplugin/soscommand.cpp
@@ -0,0 +1,152 @@
+// 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 "sosplugin.h"
+#include <dlfcn.h>
+#include <string.h>
+#include <string>
+
+class sosCommand : public lldb::SBCommandPluginInterface
+{
+ const char *m_command;
+ void *m_sosHandle;
+
+public:
+ sosCommand(const char *command)
+ {
+ m_command = command;
+ m_sosHandle = NULL;
+ }
+
+ virtual bool
+ DoExecute (lldb::SBDebugger debugger,
+ char** arguments,
+ lldb::SBCommandReturnObject &result)
+ {
+ LLDBServices* services = new LLDBServices(debugger, result);
+ LoadSos(services);
+
+ if (m_sosHandle)
+ {
+ const char* sosCommand = m_command;
+ if (sosCommand == NULL)
+ {
+ if (arguments == NULL || *arguments == NULL) {
+ sosCommand = "Help";
+ }
+ else
+ {
+ sosCommand = *arguments++;
+ }
+ }
+ CommandFunc commandFunc = (CommandFunc)dlsym(m_sosHandle, sosCommand);
+ if (commandFunc)
+ {
+ std::string str;
+ if (arguments != NULL)
+ {
+ for (const char* arg = *arguments; arg; arg = *(++arguments))
+ {
+ str.append(arg);
+ str.append(" ");
+ }
+ }
+ const char* sosArgs = str.c_str();
+ HRESULT hr = commandFunc(services, sosArgs);
+ if (hr != S_OK)
+ {
+ services->Output(DEBUG_OUTPUT_ERROR, "%s %s failed\n", sosCommand, sosArgs);
+ }
+ }
+ else
+ {
+ services->Output(DEBUG_OUTPUT_ERROR, "SOS command '%s' not found %s\n", sosCommand, dlerror());
+ }
+ }
+
+ services->Release();
+ return result.Succeeded();
+ }
+
+ void
+ LoadSos(LLDBServices *services)
+ {
+ if (m_sosHandle == NULL)
+ {
+ if (g_coreclrDirectory == NULL)
+ {
+ const char *coreclrModule = MAKEDLLNAME_A("coreclr");
+ const char *directory = services->GetModuleDirectory(coreclrModule);
+ if (directory != NULL)
+ {
+ std::string path(directory);
+ path.append("/");
+ g_coreclrDirectory = strdup(path.c_str());
+ }
+ else
+ {
+ services->Output(DEBUG_OUTPUT_WARNING, "The %s module is not loaded yet in the target process\n", coreclrModule);
+ }
+ }
+
+ if (g_coreclrDirectory != NULL)
+ {
+ // Load the DAC module first explicitly because SOS and DBI
+ // have implicit references to the DAC's PAL.
+ LoadModule(services, MAKEDLLNAME_A("mscordaccore"));
+
+ m_sosHandle = LoadModule(services, MAKEDLLNAME_A("sos"));
+ }
+ }
+ }
+
+ void *
+ LoadModule(LLDBServices *services, const char *moduleName)
+ {
+ std::string modulePath(g_coreclrDirectory);
+ modulePath.append(moduleName);
+
+ void *moduleHandle = dlopen(modulePath.c_str(), RTLD_NOW);
+ if (moduleHandle == NULL)
+ {
+ services->Output(DEBUG_OUTPUT_ERROR, "dlopen(%s) failed %s\n", modulePath.c_str(), dlerror());
+ }
+
+ return moduleHandle;
+ }
+};
+
+bool
+sosCommandInitialize(lldb::SBDebugger debugger)
+{
+ lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter();
+ interpreter.AddCommand("sos", new sosCommand(NULL), "Various coreclr debugging commands. See 'soshelp' for more details. sos <command-name> <args>");
+ interpreter.AddCommand("bpmd", new sosCommand("bpmd"), "Creates a breakpoint at the specified managed method in the specified module.");
+ interpreter.AddCommand("clrstack", new sosCommand("ClrStack"), "Provides a stack trace of managed code only.");
+ interpreter.AddCommand("clrthreads", new sosCommand("Threads"), "List the managed threads running.");
+ interpreter.AddCommand("clru", new sosCommand("u"), "Displays an annotated disassembly of a managed method.");
+ interpreter.AddCommand("dumpclass", new sosCommand("DumpClass"), "Displays information about a EE class structure at the specified address.");
+ interpreter.AddCommand("dumpheap", new sosCommand("DumpHeap"), "Displays info about the garbage-collected heap and collection statistics about objects.");
+ interpreter.AddCommand("dumpil", new sosCommand("DumpIL"), "Displays the Microsoft intermediate language (MSIL) that is associated with a managed method.");
+ interpreter.AddCommand("dumplog", new sosCommand("DumpLog"), "Writes the contents of an in-memory stress log to the specified file.");
+ interpreter.AddCommand("dumpmd", new sosCommand("DumpMD"), "Displays information about a MethodDesc structure at the specified address.");
+ interpreter.AddCommand("dumpmodule", new sosCommand("DumpModule"), "Displays information about a EE module structure at the specified address.");
+ interpreter.AddCommand("dumpmt", new sosCommand("DumpMT"), "Displays information about a method table at the specified address.");
+ interpreter.AddCommand("dumpobj", new sosCommand("DumpObj"), "Displays info about an object at the specified address.");
+ interpreter.AddCommand("dumpstack", new sosCommand("DumpStack"), "Displays a native and managed stack trace.");
+ interpreter.AddCommand("dso", new sosCommand("DumpStackObjects"), "Displays all managed objects found within the bounds of the current stack.");
+ interpreter.AddCommand("eeheap", new sosCommand("EEHeap"), "Displays info about process memory consumed by internal runtime data structures.");
+ interpreter.AddCommand("eestack", new sosCommand("EEStack"), "Runs dumpstack on all threads in the process.");
+ interpreter.AddCommand("gcroot", new sosCommand("GCRoot"), "Displays info about references (or roots) to an object at the specified address.");
+ interpreter.AddCommand("ip2md", new sosCommand("IP2MD"), "Displays the MethodDesc structure at the specified address in code that has been JIT-compiled.");
+ interpreter.AddCommand("name2ee", new sosCommand("Name2EE"), "Displays the MethodTable structure and EEClass structure for the specified type or method in the specified module.");
+ interpreter.AddCommand("pe", new sosCommand("PrintException"), "Displays and formats fields of any object derived from the Exception class at the specified address.");
+ interpreter.AddCommand("histclear", new sosCommand("HistClear"), "Releases any resources used by the family of Hist commands.");
+ interpreter.AddCommand("histinit", new sosCommand("HistInit"), "Initializes the SOS structures from the stress log saved in the debuggee.");
+ interpreter.AddCommand("histobj", new sosCommand("HistObj"), "Examines all stress log relocation records and displays the chain of garbage collection relocations that may have led to the address passed in as an argument.");
+ interpreter.AddCommand("histobjfind", new sosCommand("HistObjFind"), "Displays all the log entries that reference an object at the specified address.");
+ interpreter.AddCommand("histroot", new sosCommand("HistRoot"), "Displays information related to both promotions and relocations of the specified root.");
+ interpreter.AddCommand("soshelp", new sosCommand("Help"), "Displays all available commands when no parameter is specified, or displays detailed help information about the specified command. soshelp <command>");
+ return true;
+}
diff --git a/src/ToolBox/SOS/lldbplugin/sosplugin.cpp b/src/ToolBox/SOS/lldbplugin/sosplugin.cpp
new file mode 100644
index 0000000000..6d43d90823
--- /dev/null
+++ b/src/ToolBox/SOS/lldbplugin/sosplugin.cpp
@@ -0,0 +1,21 @@
+// 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 "sosplugin.h"
+
+namespace lldb {
+ bool PluginInitialize (lldb::SBDebugger debugger);
+}
+
+bool
+lldb::PluginInitialize (lldb::SBDebugger debugger)
+{
+#ifdef _DEBUG
+ corerunCommandInitialize(debugger);
+#endif
+ sosCommandInitialize(debugger);
+ setclrpathCommandInitialize(debugger);
+ setsostidCommandInitialize(debugger);
+ return true;
+}
diff --git a/src/ToolBox/SOS/lldbplugin/sosplugin.h b/src/ToolBox/SOS/lldbplugin/sosplugin.h
new file mode 100644
index 0000000000..4d38589e3f
--- /dev/null
+++ b/src/ToolBox/SOS/lldbplugin/sosplugin.h
@@ -0,0 +1,28 @@
+// 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 <lldb/API/LLDB.h>
+#include "mstypes.h"
+#define DEFINE_EXCEPTION_RECORD
+#include <lldbservices.h>
+#include <dbgtargetcontext.h>
+#include "services.h"
+
+typedef HRESULT (*CommandFunc)(ILLDBServices* services, const char *args);
+
+extern char *g_coreclrDirectory;
+extern ULONG g_currentThreadIndex;
+extern ULONG g_currentThreadSystemId;
+
+bool
+sosCommandInitialize(lldb::SBDebugger debugger);
+
+bool
+setsostidCommandInitialize(lldb::SBDebugger debugger);
+
+bool
+setclrpathCommandInitialize(lldb::SBDebugger debugger);
+
+bool
+corerunCommandInitialize(lldb::SBDebugger debugger);