summaryrefslogtreecommitdiff
path: root/src/ToolBox/SOS/Strike
diff options
context:
space:
mode:
Diffstat (limited to 'src/ToolBox/SOS/Strike')
-rw-r--r--src/ToolBox/SOS/Strike/.gitmirror1
-rw-r--r--src/ToolBox/SOS/Strike/ApolloNative.rc10
-rw-r--r--src/ToolBox/SOS/Strike/CMakeLists.txt201
-rw-r--r--src/ToolBox/SOS/Strike/EventCallbacks.cpp160
-rw-r--r--src/ToolBox/SOS/Strike/EventCallbacks.h68
-rw-r--r--src/ToolBox/SOS/Strike/ExpressionNode.cpp2178
-rw-r--r--src/ToolBox/SOS/Strike/ExpressionNode.h307
-rw-r--r--src/ToolBox/SOS/Strike/Native.rc10
-rw-r--r--src/ToolBox/SOS/Strike/SOS.nativeproj7
-rw-r--r--src/ToolBox/SOS/Strike/SOS.sln76
-rw-r--r--src/ToolBox/SOS/Strike/SOS.vcproj303
-rw-r--r--src/ToolBox/SOS/Strike/UtilCode.h11
-rw-r--r--src/ToolBox/SOS/Strike/WatchCmd.cpp331
-rw-r--r--src/ToolBox/SOS/Strike/WatchCmd.h110
-rw-r--r--src/ToolBox/SOS/Strike/apollososdocs.txt2727
-rw-r--r--src/ToolBox/SOS/Strike/data.h51
-rw-r--r--src/ToolBox/SOS/Strike/datatarget.cpp215
-rw-r--r--src/ToolBox/SOS/Strike/datatarget.h90
-rw-r--r--src/ToolBox/SOS/Strike/dirs.proj20
-rw-r--r--src/ToolBox/SOS/Strike/disasm.cpp1142
-rw-r--r--src/ToolBox/SOS/Strike/disasm.h453
-rw-r--r--src/ToolBox/SOS/Strike/disasmARM.cpp626
-rw-r--r--src/ToolBox/SOS/Strike/disasmARM64.cpp392
-rw-r--r--src/ToolBox/SOS/Strike/disasmX86.cpp1707
-rw-r--r--src/ToolBox/SOS/Strike/dllsext.cpp278
-rw-r--r--src/ToolBox/SOS/Strike/eeheap.cpp1913
-rw-r--r--src/ToolBox/SOS/Strike/exts.cpp435
-rw-r--r--src/ToolBox/SOS/Strike/exts.h513
-rw-r--r--src/ToolBox/SOS/Strike/gchist.cpp636
-rw-r--r--src/ToolBox/SOS/Strike/gcroot.cpp2503
-rw-r--r--src/ToolBox/SOS/Strike/inc/.gitmirror1
-rw-r--r--src/ToolBox/SOS/Strike/inc/dbgeng.h16122
-rw-r--r--src/ToolBox/SOS/Strike/inc/dbghelp.h4540
-rw-r--r--src/ToolBox/SOS/Strike/inc/wdbgexts.h2807
-rw-r--r--src/ToolBox/SOS/Strike/metadata.cpp1041
-rw-r--r--src/ToolBox/SOS/Strike/ntinfo.h193
-rw-r--r--src/ToolBox/SOS/Strike/platformspecific.h195
-rw-r--r--src/ToolBox/SOS/Strike/sildasm.cpp1090
-rw-r--r--src/ToolBox/SOS/Strike/sos.cpp888
-rw-r--r--src/ToolBox/SOS/Strike/sos.def231
-rw-r--r--src/ToolBox/SOS/Strike/sos.h792
-rw-r--r--src/ToolBox/SOS/Strike/sos.targets166
-rw-r--r--src/ToolBox/SOS/Strike/sos_md.h926
-rw-r--r--src/ToolBox/SOS/Strike/sos_stacktrace.h174
-rw-r--r--src/ToolBox/SOS/Strike/sos_unixexports.src54
-rw-r--r--src/ToolBox/SOS/Strike/sosdocs.txt2572
-rw-r--r--src/ToolBox/SOS/Strike/sosdocsunix.txt1713
-rw-r--r--src/ToolBox/SOS/Strike/stressLogDump.cpp549
-rw-r--r--src/ToolBox/SOS/Strike/strike.cpp14462
-rw-r--r--src/ToolBox/SOS/Strike/strike.h144
-rw-r--r--src/ToolBox/SOS/Strike/util.cpp6975
-rw-r--r--src/ToolBox/SOS/Strike/util.h3292
-rw-r--r--src/ToolBox/SOS/Strike/vm.cpp732
-rw-r--r--src/ToolBox/SOS/Strike/xplat/.gitmirror1
-rw-r--r--src/ToolBox/SOS/Strike/xplat/dbgeng.h485
-rw-r--r--src/ToolBox/SOS/Strike/xplat/dbghelp.h17
-rw-r--r--src/ToolBox/SOS/Strike/xplat/wdbgexts.h28
57 files changed, 77664 insertions, 0 deletions
diff --git a/src/ToolBox/SOS/Strike/.gitmirror b/src/ToolBox/SOS/Strike/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/ToolBox/SOS/Strike/ApolloNative.rc b/src/ToolBox/SOS/Strike/ApolloNative.rc
new file mode 100644
index 0000000000..ba320af911
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/ApolloNative.rc
@@ -0,0 +1,10 @@
+// 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.
+
+#define FX_VER_FILEDESCRIPTION_STR "Microsoft NTSD extension for .NET Runtime\0"
+
+#include <fxver.h>
+#include <fxver.rc>
+
+DOCUMENTATION TEXT DISCARDABLE "apollososdocs.txt"
diff --git a/src/ToolBox/SOS/Strike/CMakeLists.txt b/src/ToolBox/SOS/Strike/CMakeLists.txt
new file mode 100644
index 0000000000..5d25865780
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/CMakeLists.txt
@@ -0,0 +1,201 @@
+# 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)
+
+if(CLR_CMAKE_PLATFORM_ARCH_AMD64)
+ add_definitions(-DSOS_TARGET_AMD64=1)
+ add_definitions(-D_TARGET_WIN64_=1)
+ add_definitions(-DDBG_TARGET_64BIT)
+ add_definitions(-DDBG_TARGET_WIN64=1)
+ if(WIN32)
+ add_definitions(-DSOS_TARGET_ARM64=1)
+ endif(WIN32)
+ remove_definitions(-D_TARGET_ARM64_=1)
+ add_definitions(-D_TARGET_AMD64_)
+ add_definitions(-DDBG_TARGET_AMD64)
+elseif(CLR_CMAKE_PLATFORM_ARCH_I386)
+ add_definitions(-DSOS_TARGET_X86=1)
+ add_definitions(-D_TARGET_X86_=1)
+ add_definitions(-DDBG_TARGET_32BIT)
+ add_definitions(-DSOS_TARGET_ARM=1)
+elseif(CLR_CMAKE_PLATFORM_ARCH_ARM)
+ add_definitions(-DSOS_TARGET_ARM=1)
+ add_definitions(-D_TARGET_WIN32_=1)
+ add_definitions(-D_TARGET_ARM_=1)
+ add_definitions(-DDBG_TARGET_32BIT)
+elseif(CLR_CMAKE_PLATFORM_ARCH_ARM64)
+ add_definitions(-DSOS_TARGET_ARM64=1)
+ add_definitions(-D_TARGET_WIN64_=1)
+ add_definitions(-DDBG_TARGET_64BIT)
+ add_definitions(-DDBG_TARGET_WIN64=1)
+endif()
+add_definitions(-DSTRIKE)
+
+remove_definitions(-DUNICODE)
+remove_definitions(-D_UNICODE)
+
+include_directories(BEFORE ${VM_DIR})
+include_directories(${CLR_DIR}/src/gcdump)
+include_directories(${CLR_DIR}/src/debug/shim)
+include_directories(${CLR_DIR}/src/coreclr/hosts/unixcoreruncommon)
+include_directories(${CLR_DIR}/src/coreclr/hosts/inc)
+
+if(WIN32)
+ include_directories(inc)
+ include_directories("$ENV{VSInstallDir}/DIA SDK/include")
+
+ add_definitions(-DUSE_STL)
+
+ #use static crt
+ add_definitions(-MT)
+
+ set(SOS_SOURCES
+ disasm.cpp
+ dllsext.cpp
+ eeheap.cpp
+ EventCallbacks.cpp
+ ExpressionNode.cpp
+ exts.cpp
+ gchist.cpp
+ gcroot.cpp
+ metadata.cpp
+ sildasm.cpp
+ sos.cpp
+ stressLogDump.cpp
+ strike.cpp
+ util.cpp
+ vm.cpp
+ WatchCmd.cpp
+ Native.rc
+ )
+
+ add_definitions(-DFX_VER_INTERNALNAME_STR=SOS.dll)
+
+ #Preprocess exports definition file
+ preprocess_def_file(${CMAKE_CURRENT_SOURCE_DIR}/sos.def ${CMAKE_CURRENT_BINARY_DIR}/sos.def)
+ list(APPEND SOS_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/sos.def)
+
+ set(SOS_LIBRARY
+ corguids
+ debugshim
+ dbgutil
+ ${STATIC_MT_CRT_LIB}
+ ${STATIC_MT_CPP_LIB}
+ ${STATIC_MT_VCRT_LIB}
+ kernel32.lib
+ user32.lib
+ ole32.lib
+ oleaut32.lib
+ dbghelp.lib
+ uuid.lib
+ version.lib
+ dbgeng.lib
+ advapi32.lib
+ psapi.lib
+ ntdll.lib
+ )
+else(WIN32)
+ add_definitions(-DFEATURE_ENABLE_HARDWARE_EXCEPTIONS)
+ add_definitions(-DPAL_STDCPP_COMPAT=1)
+ add_compile_options(-Wno-null-arithmetic)
+ add_compile_options(-Wno-format)
+
+ include_directories(BEFORE xplat)
+ include_directories(BEFORE ../lldbplugin/inc)
+
+ add_compile_options(-fPIC)
+
+ set(SOS_SOURCES
+ disasm.cpp
+ datatarget.cpp
+ eeheap.cpp
+ exts.cpp
+ gchist.cpp
+ gcroot.cpp
+ metadata.cpp
+ sildasm.cpp
+ stressLogDump.cpp
+ strike.cpp
+ sos.cpp
+ util.cpp
+ ../../../coreclr/hosts/unixcoreruncommon/coreruncommon.cpp
+ )
+
+ set(SOS_LIBRARY
+ corguids
+ debugshim
+ dbgutil
+ # share the PAL in the dac module
+ mscordaccore
+ palrt
+ )
+
+ set(DEF_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/sos_unixexports.src)
+ set(EXPORTS_FILE ${CMAKE_CURRENT_BINARY_DIR}/sos.exports)
+ generate_exports_file(${DEF_SOURCES} ${EXPORTS_FILE})
+endif(WIN32)
+
+if(CLR_CMAKE_PLATFORM_ARCH_AMD64)
+ set(SOS_SOURCES_ARCH
+ disasmX86.cpp
+ )
+ if(WIN32)
+ list(APPEND
+ SOS_SOURCES_ARCH
+ disasmARM64.cpp
+ )
+ endif(WIN32)
+elseif(CLR_CMAKE_PLATFORM_ARCH_I386)
+ set(SOS_SOURCES_ARCH
+ disasmX86.cpp
+ disasmARM.cpp
+ )
+elseif(CLR_CMAKE_PLATFORM_ARCH_ARM)
+ set(SOS_SOURCES_ARCH
+ disasmARM.cpp
+ )
+elseif(CLR_CMAKE_PLATFORM_ARCH_ARM64)
+ set(SOS_SOURCES_ARCH
+ disasmARM64.cpp
+ )
+endif()
+
+list(APPEND SOS_SOURCES ${SOS_SOURCES_ARCH})
+
+if(CLR_CMAKE_PLATFORM_LINUX OR CLR_CMAKE_PLATFORM_FREEBSD OR CLR_CMAKE_PLATFORM_NETBSD)
+ # Add linker exports file option
+ set(EXPORTS_LINKER_OPTION -Wl,--version-script=${EXPORTS_FILE})
+endif(CLR_CMAKE_PLATFORM_LINUX OR CLR_CMAKE_PLATFORM_FREEBSD OR CLR_CMAKE_PLATFORM_NETBSD)
+
+if(CLR_CMAKE_PLATFORM_DARWIN)
+ # Add linker exports file option
+ set(EXPORTS_LINKER_OPTION -Wl,-exported_symbols_list,${EXPORTS_FILE})
+endif(CLR_CMAKE_PLATFORM_DARWIN)
+
+add_library_clr(sos SHARED ${SOS_SOURCES})
+
+if(CLR_CMAKE_PLATFORM_UNIX)
+ add_custom_target(sos_exports DEPENDS ${EXPORTS_FILE})
+ add_dependencies(sos sos_exports)
+
+ set_property(TARGET sos APPEND_STRING PROPERTY LINK_FLAGS ${EXPORTS_LINKER_OPTION})
+ set_property(TARGET sos APPEND_STRING PROPERTY LINK_DEPENDS ${EXPORTS_FILE})
+
+ add_dependencies(sos mscordaccore)
+endif(CLR_CMAKE_PLATFORM_UNIX)
+
+target_link_libraries(sos ${SOS_LIBRARY})
+
+# add the install targets
+install_clr(sos)
+
+if(NOT WIN32)
+ install(FILES sosdocsunix.txt DESTINATION .)
+endif(NOT WIN32)
diff --git a/src/ToolBox/SOS/Strike/EventCallbacks.cpp b/src/ToolBox/SOS/Strike/EventCallbacks.cpp
new file mode 100644
index 0000000000..0066dfa1e8
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/EventCallbacks.cpp
@@ -0,0 +1,160 @@
+// 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 "EventCallbacks.h"
+
+EventCallbacks::EventCallbacks(IDebugClient* pDebugClient) : m_refCount(1), m_pDebugClient(pDebugClient)
+{
+}
+
+EventCallbacks::~EventCallbacks()
+{
+ if(m_pDebugClient != NULL)
+ m_pDebugClient->Release();
+}
+
+ // IUnknown implementation
+HRESULT __stdcall EventCallbacks::QueryInterface(REFIID riid, VOID** ppInterface)
+{
+ if(riid == __uuidof(IDebugEventCallbacks))
+ {
+ *ppInterface = static_cast<IDebugEventCallbacks*>(this);
+ AddRef();
+ return S_OK;
+ }
+ else if(riid == __uuidof(IUnknown))
+ {
+ *ppInterface = static_cast<IUnknown*>(this);
+ AddRef();
+ return S_OK;
+ }
+ else
+ {
+ return E_NOINTERFACE;
+ }
+}
+
+ULONG __stdcall EventCallbacks::AddRef()
+{
+ return InterlockedIncrement((volatile LONG *) &m_refCount);
+}
+
+ULONG __stdcall EventCallbacks::Release()
+{
+ ULONG count = InterlockedDecrement((volatile LONG *) &m_refCount);
+ if(count == 0)
+ {
+ delete this;
+ }
+ return count;
+}
+
+// IDebugEventCallbacks implementation
+HRESULT __stdcall EventCallbacks::Breakpoint(PDEBUG_BREAKPOINT bp)
+{
+ return DEBUG_STATUS_NO_CHANGE;
+}
+
+HRESULT __stdcall EventCallbacks::ChangeDebuggeeState(ULONG Flags, ULONG64 Argument)
+{
+ return DEBUG_STATUS_NO_CHANGE;
+}
+
+HRESULT __stdcall EventCallbacks::ChangeEngineState(ULONG Flags, ULONG64 Argument)
+{
+ return DEBUG_STATUS_NO_CHANGE;
+}
+HRESULT __stdcall EventCallbacks::ChangeSymbolState(ULONG Flags, ULONG64 Argument)
+{
+ return DEBUG_STATUS_NO_CHANGE;
+}
+HRESULT __stdcall EventCallbacks::CreateProcess(ULONG64 ImageFileHandle,
+ ULONG64 Handle,
+ ULONG64 BaseOffset,
+ ULONG ModuleSize,
+ PCSTR ModuleName,
+ PCSTR ImageName,
+ ULONG CheckSum,
+ ULONG TimeDateStamp,
+ ULONG64 InitialThreadHandle,
+ ULONG64 ThreadDataOffset,
+ ULONG64 StartOffset)
+{
+ return DEBUG_STATUS_NO_CHANGE;
+}
+
+HRESULT __stdcall EventCallbacks::CreateThread(ULONG64 Handle,
+ ULONG64 DataOffset,
+ ULONG64 StartOffset)
+{
+ return DEBUG_STATUS_NO_CHANGE;
+}
+
+HRESULT __stdcall EventCallbacks::Exception(PEXCEPTION_RECORD64 Exception, ULONG FirstChance)
+{
+ return DEBUG_STATUS_NO_CHANGE;
+}
+
+HRESULT __stdcall EventCallbacks::ExitProcess(ULONG ExitCode)
+{
+ UninitCorDebugInterface();
+ return DEBUG_STATUS_NO_CHANGE;
+}
+
+HRESULT __stdcall EventCallbacks::ExitThread(ULONG ExitCode)
+{
+ return DEBUG_STATUS_NO_CHANGE;
+}
+
+HRESULT __stdcall EventCallbacks::GetInterestMask(PULONG Mask)
+{
+ *Mask = DEBUG_EVENT_LOAD_MODULE | DEBUG_EVENT_EXIT_PROCESS;
+ return S_OK;
+}
+
+extern BOOL g_fAllowJitOptimization;
+
+HRESULT __stdcall EventCallbacks::LoadModule(ULONG64 ImageFileHandle,
+ ULONG64 BaseOffset,
+ ULONG ModuleSize,
+ PCSTR ModuleName,
+ PCSTR ImageName,
+ ULONG CheckSum,
+ ULONG TimeDateStamp)
+{
+ HRESULT handleEventStatus = DEBUG_STATUS_NO_CHANGE;
+ ExtQuery(m_pDebugClient);
+
+ if (ModuleName != NULL && _stricmp(ModuleName, MAIN_CLR_MODULE_NAME_A) == 0)
+ {
+ // if we don't want the JIT to optimize, we should also disable optimized NGEN images
+ if(!g_fAllowJitOptimization)
+ {
+ // if we aren't succesful SetNGENCompilerFlags will print relevant error messages
+ // and then we need to stop the debugger so the user can intervene if desired
+ if(FAILED(SetNGENCompilerFlags(CORDEBUG_JIT_DISABLE_OPTIMIZATION)))
+ {
+ handleEventStatus = DEBUG_STATUS_BREAK;
+ }
+ }
+ }
+
+ ExtRelease();
+ return handleEventStatus;
+}
+
+HRESULT __stdcall EventCallbacks::SessionStatus(ULONG Status)
+{
+ return DEBUG_STATUS_NO_CHANGE;
+}
+
+HRESULT __stdcall EventCallbacks::SystemError(ULONG Error, ULONG Level)
+{
+ return DEBUG_STATUS_NO_CHANGE;
+}
+
+HRESULT __stdcall EventCallbacks::UnloadModule(PCSTR ImageBaseName, ULONG64 BaseOffset)
+{
+ return DEBUG_STATUS_NO_CHANGE;
+}
diff --git a/src/ToolBox/SOS/Strike/EventCallbacks.h b/src/ToolBox/SOS/Strike/EventCallbacks.h
new file mode 100644
index 0000000000..acd5e412d1
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/EventCallbacks.h
@@ -0,0 +1,68 @@
+// 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.
+
+#ifndef __EventCallbacks__
+#define __EventCallbacks__
+
+#include "exts.h"
+
+// A set of callbacks that are registered with windbg whenever SOS is loaded
+// Right now these callbacks only act on the module load event for CLR, but
+// feel free to add other event hooks as needed
+//
+// TODO: we should probably be using these callbacks to hook clrnotify exceptions
+// rather than attaching a user handler on the clrn event. That handler is both
+// visible to the user and could be accidentally erased by them.
+class EventCallbacks : IDebugEventCallbacks
+{
+public:
+ EventCallbacks(IDebugClient* pDebugClient);
+ ~EventCallbacks();
+
+ // IUnknown implementation
+ HRESULT __stdcall QueryInterface(REFIID riid, VOID** ppInterface);
+ ULONG __stdcall AddRef();
+ ULONG __stdcall Release();
+
+ // IDebugEventCallbacks implementation
+ HRESULT __stdcall Breakpoint(PDEBUG_BREAKPOINT bp);
+ HRESULT __stdcall ChangeDebuggeeState(ULONG Flags, ULONG64 Argument);
+ HRESULT __stdcall ChangeEngineState(ULONG Flags, ULONG64 Argument);
+ HRESULT __stdcall ChangeSymbolState(ULONG Flags, ULONG64 Argument);
+ HRESULT __stdcall CreateProcess(ULONG64 ImageFileHandle,
+ ULONG64 Handle,
+ ULONG64 BaseOffset,
+ ULONG ModuleSize,
+ PCSTR ModuleName,
+ PCSTR ImageName,
+ ULONG CheckSum,
+ ULONG TimeDateStamp,
+ ULONG64 InitialThreadHandle,
+ ULONG64 ThreadDataOffset,
+ ULONG64 StartOffset);
+ HRESULT __stdcall CreateThread(ULONG64 Handle,
+ ULONG64 DataOffset,
+ ULONG64 StartOffset);
+ HRESULT __stdcall Exception(PEXCEPTION_RECORD64 Exception, ULONG FirstChance);
+ HRESULT __stdcall ExitProcess(ULONG ExitCode);
+ HRESULT __stdcall ExitThread(ULONG ExitCode);
+ HRESULT __stdcall GetInterestMask(PULONG Mask);
+ HRESULT __stdcall LoadModule(ULONG64 ImageFileHandle,
+ ULONG64 BaseOffset,
+ ULONG ModuleSize,
+ PCSTR ModuleName,
+ PCSTR ImageName,
+ ULONG CheckSum,
+ ULONG TimeDateStamp);
+ HRESULT __stdcall SessionStatus(ULONG Status);
+ HRESULT __stdcall SystemError(ULONG Error, ULONG Level);
+ HRESULT __stdcall UnloadModule(PCSTR ImageBaseName, ULONG64 BaseOffset);
+
+
+private:
+ volatile ULONG m_refCount;
+ IDebugClient* m_pDebugClient;
+};
+
+#endif
diff --git a/src/ToolBox/SOS/Strike/ExpressionNode.cpp b/src/ToolBox/SOS/Strike/ExpressionNode.cpp
new file mode 100644
index 0000000000..6516823aaa
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/ExpressionNode.cpp
@@ -0,0 +1,2178 @@
+// 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 "ExpressionNode.h"
+
+
+#ifndef IfFailRet
+#define IfFailRet(EXPR) do { Status = (EXPR); if(FAILED(Status)) { return (Status); } } while (0)
+#endif
+
+// Returns the complete expression being evaluated to get the value for this node
+// The returned pointer is a string interior to this object - once you release
+// all references to this object the string is invalid.
+WCHAR* ExpressionNode::GetAbsoluteExpression() { return pAbsoluteExpression; }
+
+// Returns the sub expression that logically indicates how the parent expression
+// was built upon to reach this node. This relative value has no purpose other
+// than an identifier and to convey UI meaning to the user. At present typical values
+// are the name of type, a local, a parameter, a field, an array index, or '<basetype>'
+// for a baseclass casting operation
+// The returned pointer is a string interior to this object - once you release
+// all references to this object the string is invalid.
+WCHAR* ExpressionNode::GetRelativeExpression() { return pRelativeExpression; }
+
+// Returns a text representation of the type of value that this node refers to
+// It is possible this node doesn't evaluate to anything and therefore has no
+// type
+// The returned pointer is a string interior to this object - once you release
+// all references to this object the string is invalid.
+WCHAR* ExpressionNode::GetTypeName() { PopulateType(); return pTypeName; }
+
+// Returns a text representation of the value for this node. It is possible that
+// this node doesn't evaluate to anything and therefore has no value text.
+// The returned pointer is a string interior to this object - once you release
+// all references to this object the string is invalid.
+WCHAR* ExpressionNode::GetTextValue() { PopulateTextValue(); return pTextValue; }
+
+// If there is any error during the evaluation of this node's expression, it is
+// returned here.
+// The returned pointer is a string interior to this object - once you release
+// all references to this object the string is invalid.
+WCHAR* ExpressionNode::GetErrorMessage() { return pErrorMessage; }
+
+// Factory function for creating the expression node at the root of a tree
+HRESULT ExpressionNode::CreateExpressionNode(__in_z WCHAR* pExpression, ExpressionNode** ppExpressionNode)
+{
+ *ppExpressionNode = NULL;
+ HRESULT Status = CreateExpressionNodeHelper(pExpression,
+ pExpression,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ ppExpressionNode);
+ if(FAILED(Status) && *ppExpressionNode == NULL)
+ {
+
+ WCHAR pErrorMessage[MAX_ERROR];
+ _snwprintf_s(pErrorMessage, MAX_ERROR, _TRUNCATE, L"Error 0x%x while parsing expression", Status);
+ *ppExpressionNode = new ExpressionNode(pExpression, pErrorMessage);
+ Status = S_OK;
+ if(*ppExpressionNode == NULL)
+ Status = E_OUTOFMEMORY;
+ }
+ return Status;
+}
+
+// Performs recursive expansion within the tree for nodes that are along the path to varToExpand.
+// Expansion involves calulating a set of child expressions from the current expression via
+// field dereferencing, array index dereferencing, or casting to a base type.
+// For example if a tree was rooted with expression 'foo.bar' and varToExpand is '(Baz)foo.bar[9]'
+// then 'foo.bar', 'foo.bar[9]', and '(Baz)foo.bar[9]' nodes would all be expanded.
+HRESULT ExpressionNode::Expand(__in_z WCHAR* varToExpand)
+{
+ if(!ShouldExpandVariable(varToExpand))
+ return S_FALSE;
+ if(pValue == NULL && pTypeCast == NULL)
+ return S_OK;
+
+ // if the node evaluates to a type, then the children are static fields of the type
+ if(pValue == NULL)
+ return ExpandFields(NULL, varToExpand);
+
+ // If the value is a null reference there is nothing to expand
+ HRESULT Status = S_OK;
+ BOOL isNull = TRUE;
+ ToRelease<ICorDebugValue> pInnerValue;
+ IfFailRet(DereferenceAndUnboxValue(pValue, &pInnerValue, &isNull));
+ if(isNull)
+ {
+ return S_OK;
+ }
+
+
+ CorElementType corElemType;
+ IfFailRet(pValue->GetType(&corElemType));
+ if (corElemType == ELEMENT_TYPE_SZARRAY)
+ {
+ //If its an array, add children representing the indexed elements
+ return ExpandSzArray(pInnerValue, varToExpand);
+ }
+ else if(corElemType == ELEMENT_TYPE_CLASS || corElemType == ELEMENT_TYPE_VALUETYPE)
+ {
+ // If its a class or struct (not counting string, array, or object) then add children representing
+ // the fields.
+ return ExpandFields(pInnerValue, varToExpand);
+ }
+ else
+ {
+ // nothing else expands
+ return S_OK;
+ }
+}
+
+// Standard depth first search tree traversal pattern with a callback
+VOID ExpressionNode::DFSVisit(ExpressionNodeVisitorCallback pFunc, VOID* pUserData, int depth)
+{
+ pFunc(this, depth, pUserData);
+ ExpressionNode* pCurChild = pChild;
+ while(pCurChild != NULL)
+ {
+ pCurChild->DFSVisit(pFunc, pUserData, depth+1);
+ pCurChild = pCurChild->pNextSibling;
+ }
+}
+
+
+// Creates a new expression with a given debuggee value and frame
+ExpressionNode::ExpressionNode(__in_z WCHAR* pExpression, ICorDebugValue* pValue, ICorDebugILFrame* pFrame)
+{
+ Init(pValue, NULL, pFrame);
+ _snwprintf_s(pAbsoluteExpression, MAX_EXPRESSION, _TRUNCATE, L"%s", pExpression);
+ _snwprintf_s(pRelativeExpression, MAX_EXPRESSION, _TRUNCATE, L"%s", pExpression);
+}
+
+// Creates a new expression that has an error and no value
+ExpressionNode::ExpressionNode(__in_z WCHAR* pExpression, __in_z WCHAR* pErrorMessage)
+{
+ Init(NULL, NULL, NULL);
+ _snwprintf_s(pAbsoluteExpression, MAX_EXPRESSION, _TRUNCATE, L"%s", pExpression);
+ _snwprintf_s(pRelativeExpression, MAX_EXPRESSION, _TRUNCATE, L"%s", pExpression);
+ _snwprintf_s(this->pErrorMessage, MAX_ERROR, _TRUNCATE, L"%s", pErrorMessage);
+}
+
+// Creates a new child expression
+ExpressionNode::ExpressionNode(__in_z WCHAR* pParentExpression, ChildKind ck, __in_z WCHAR* pRelativeExpression, ICorDebugValue* pValue, ICorDebugType* pType, ICorDebugILFrame* pFrame, UVCP_CONSTANT pDefaultValue, ULONG cchDefaultValue)
+{
+ Init(pValue, pType, pFrame);
+ if(ck == ChildKind_BaseClass)
+ {
+ _snwprintf_s(pAbsoluteExpression, MAX_EXPRESSION, _TRUNCATE, L"%s", pParentExpression);
+ }
+ else
+ {
+ _snwprintf_s(pAbsoluteExpression, MAX_EXPRESSION, _TRUNCATE, ck == ChildKind_Field ? L"%s.%s" : L"%s[%s]", pParentExpression, pRelativeExpression);
+ }
+ _snwprintf_s(this->pRelativeExpression, MAX_EXPRESSION, _TRUNCATE, ck == ChildKind_Index ? L"[%s]" : L"%s", pRelativeExpression);
+ this->pDefaultValue = pDefaultValue;
+ this->cchDefaultValue = cchDefaultValue;
+}
+
+// Common member initialization for the constructors
+VOID ExpressionNode::Init(ICorDebugValue* pValue, ICorDebugType* pTypeCast, ICorDebugILFrame* pFrame)
+{
+ this->pValue = pValue;
+ this->pTypeCast = pTypeCast;
+ this->pILFrame = pFrame;
+ pChild = NULL;
+ pNextSibling = NULL;
+ pTextValue[0] = 0;
+ pErrorMessage[0] = 0;
+ pAbsoluteExpression[0] = 0;
+ pRelativeExpression[0] = 0;
+ pTypeName[0] = 0;
+
+ pDefaultValue = NULL;
+ cchDefaultValue = 0;
+
+ // The ToRelease holders don't automatically AddRef
+ if(pILFrame != NULL)
+ pILFrame->AddRef();
+ if(pTypeCast != NULL)
+ pTypeCast->AddRef();
+ if(pValue != NULL)
+ {
+ pValue->AddRef();
+ PopulateMetaDataImport();
+ }
+}
+
+// Retreves the correct IMetaDataImport for the type represented in this node and stores it
+// in pMD.
+HRESULT ExpressionNode::PopulateMetaDataImport()
+{
+ if(pMD != NULL)
+ return S_OK;
+
+ HRESULT Status = S_OK;
+ ToRelease<ICorDebugType> pType;
+ if(pTypeCast != NULL)
+ {
+ pType = pTypeCast;
+ pType->AddRef();
+ }
+ else
+ {
+ BOOL isNull;
+ ToRelease<ICorDebugValue> pInnerValue;
+ IfFailRet(DereferenceAndUnboxValue(pValue, &pInnerValue, &isNull));
+ ToRelease<ICorDebugValue2> pValue2;
+ IfFailRet(pInnerValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2));
+
+ IfFailRet(pValue2->GetExactType(&pType));
+
+ // for array, pointer, and byref types we can't directly get a class, we must unwrap first
+ CorElementType et;
+ IfFailRet(pType->GetType(&et));
+ while(et == ELEMENT_TYPE_ARRAY || et == ELEMENT_TYPE_SZARRAY || et == ELEMENT_TYPE_BYREF || et == ELEMENT_TYPE_PTR)
+ {
+ pType->GetFirstTypeParameter(&pType);
+ IfFailRet(pType->GetType(&et));
+ }
+ }
+ ToRelease<ICorDebugClass> pClass;
+ IfFailRet(pType->GetClass(&pClass));
+ ToRelease<ICorDebugModule> pModule;
+ IfFailRet(pClass->GetModule(&pModule));
+ ToRelease<IUnknown> pMDUnknown;
+ IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
+ IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
+ return Status;
+}
+
+// Determines the string representation of pType and stores it in typeName
+HRESULT ExpressionNode::CalculateTypeName(ICorDebugType * pType, __inout_ecount(typeNameLen) WCHAR* typeName, DWORD typeNameLen)
+{
+ HRESULT Status = S_OK;
+
+ CorElementType corElemType;
+ IfFailRet(pType->GetType(&corElemType));
+
+ switch (corElemType)
+ {
+ //List of unsupported CorElementTypes:
+ //ELEMENT_TYPE_END = 0x0,
+ //ELEMENT_TYPE_VAR = 0x13, // a class type variable VAR <U1>
+ //ELEMENT_TYPE_GENERICINST = 0x15, // GENERICINST <generic type> <argCnt> <arg1> ... <argn>
+ //ELEMENT_TYPE_TYPEDBYREF = 0x16, // TYPEDREF (it takes no args) a typed referece to some other type
+ //ELEMENT_TYPE_MVAR = 0x1e, // a method type variable MVAR <U1>
+ //ELEMENT_TYPE_CMOD_REQD = 0x1F, // required C modifier : E_T_CMOD_REQD <mdTypeRef/mdTypeDef>
+ //ELEMENT_TYPE_CMOD_OPT = 0x20, // optional C modifier : E_T_CMOD_OPT <mdTypeRef/mdTypeDef>
+ //ELEMENT_TYPE_INTERNAL = 0x21, // INTERNAL <typehandle>
+ //ELEMENT_TYPE_MAX = 0x22, // first invalid element type
+ //ELEMENT_TYPE_MODIFIER = 0x40,
+ //ELEMENT_TYPE_SENTINEL = 0x01 | ELEMENT_TYPE_MODIFIER, // sentinel for varargs
+ //ELEMENT_TYPE_PINNED = 0x05 | ELEMENT_TYPE_MODIFIER,
+ default:
+ swprintf_s(typeName, typeNameLen, L"(Unhandled CorElementType: 0x%x)\0", corElemType);
+ break;
+
+ case ELEMENT_TYPE_VALUETYPE:
+ case ELEMENT_TYPE_CLASS:
+ {
+ //Defaults in case we fail...
+ if(corElemType == ELEMENT_TYPE_VALUETYPE) swprintf_s(typeName, typeNameLen, L"struct\0");
+ else swprintf_s(typeName, typeNameLen, L"class\0");
+
+ mdTypeDef typeDef;
+ ToRelease<ICorDebugClass> pClass;
+ if(SUCCEEDED(pType->GetClass(&pClass)) && SUCCEEDED(pClass->GetToken(&typeDef)))
+ {
+ ToRelease<ICorDebugModule> pModule;
+ IfFailRet(pClass->GetModule(&pModule));
+
+ ToRelease<IUnknown> pMDUnknown;
+ ToRelease<IMetaDataImport> pMD;
+ IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
+ IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
+
+ if(SUCCEEDED(NameForToken_s(TokenFromRid(typeDef, mdtTypeDef), pMD, g_mdName, mdNameLen, false)))
+ swprintf_s(typeName, typeNameLen, L"%s\0", g_mdName);
+ }
+ AddGenericArgs(pType, typeName, typeNameLen);
+ }
+ break;
+ case ELEMENT_TYPE_VOID:
+ swprintf_s(typeName, typeNameLen, L"void\0");
+ break;
+ case ELEMENT_TYPE_BOOLEAN:
+ swprintf_s(typeName, typeNameLen, L"bool\0");
+ break;
+ case ELEMENT_TYPE_CHAR:
+ swprintf_s(typeName, typeNameLen, L"char\0");
+ break;
+ case ELEMENT_TYPE_I1:
+ swprintf_s(typeName, typeNameLen, L"signed byte\0");
+ break;
+ case ELEMENT_TYPE_U1:
+ swprintf_s(typeName, typeNameLen, L"byte\0");
+ break;
+ case ELEMENT_TYPE_I2:
+ swprintf_s(typeName, typeNameLen, L"short\0");
+ break;
+ case ELEMENT_TYPE_U2:
+ swprintf_s(typeName, typeNameLen, L"unsigned short\0");
+ break;
+ case ELEMENT_TYPE_I4:
+ swprintf_s(typeName, typeNameLen, L"int\0");
+ break;
+ case ELEMENT_TYPE_U4:
+ swprintf_s(typeName, typeNameLen, L"unsigned int\0");
+ break;
+ case ELEMENT_TYPE_I8:
+ swprintf_s(typeName, typeNameLen, L"long\0");
+ break;
+ case ELEMENT_TYPE_U8:
+ swprintf_s(typeName, typeNameLen, L"unsigned long\0");
+ break;
+ case ELEMENT_TYPE_R4:
+ swprintf_s(typeName, typeNameLen, L"float\0");
+ break;
+ case ELEMENT_TYPE_R8:
+ swprintf_s(typeName, typeNameLen, L"double\0");
+ break;
+ case ELEMENT_TYPE_OBJECT:
+ swprintf_s(typeName, typeNameLen, L"object\0");
+ break;
+ case ELEMENT_TYPE_STRING:
+ swprintf_s(typeName, typeNameLen, L"string\0");
+ break;
+ case ELEMENT_TYPE_I:
+ swprintf_s(typeName, typeNameLen, L"IntPtr\0");
+ break;
+ case ELEMENT_TYPE_U:
+ swprintf_s(typeName, typeNameLen, L"UIntPtr\0");
+ break;
+ case ELEMENT_TYPE_SZARRAY:
+ case ELEMENT_TYPE_ARRAY:
+ case ELEMENT_TYPE_BYREF:
+ case ELEMENT_TYPE_PTR:
+ {
+ // get a name for the type we are building from
+ ToRelease<ICorDebugType> pFirstParameter;
+ if(SUCCEEDED(pType->GetFirstTypeParameter(&pFirstParameter)))
+ CalculateTypeName(pFirstParameter, typeName, typeNameLen);
+ else
+ swprintf_s(typeName, typeNameLen, L"<unknown>\0");
+
+ // append the appropriate [], *, &
+ switch(corElemType)
+ {
+ case ELEMENT_TYPE_SZARRAY:
+ wcsncat_s(typeName, typeNameLen, L"[]", typeNameLen);
+ return S_OK;
+ case ELEMENT_TYPE_ARRAY:
+ {
+ ULONG32 rank = 0;
+ pType->GetRank(&rank);
+ wcsncat_s(typeName, typeNameLen, L"[", typeNameLen);
+ for(ULONG32 i = 0; i < rank - 1; i++)
+ {
+ // todo- could we print out exact boundaries?
+ wcsncat_s(typeName, typeNameLen, L",", typeNameLen);
+ }
+ wcsncat_s(typeName, typeNameLen, L"]", typeNameLen);
+ }
+ return S_OK;
+ case ELEMENT_TYPE_BYREF:
+ wcsncat_s(typeName, typeNameLen, L"&", typeNameLen);
+ return S_OK;
+ case ELEMENT_TYPE_PTR:
+ wcsncat_s(typeName, typeNameLen, L"*", typeNameLen);
+ return S_OK;
+ }
+ }
+ break;
+ case ELEMENT_TYPE_FNPTR:
+ swprintf_s(typeName, typeNameLen, L"*(...)");
+ break;
+ case ELEMENT_TYPE_TYPEDBYREF:
+ swprintf_s(typeName, typeNameLen, L"typedbyref");
+ break;
+ }
+ return S_OK;
+}
+
+
+// Appends angle brackets and the generic argument list to a type name
+HRESULT ExpressionNode::AddGenericArgs(ICorDebugType * pType, __inout_ecount(typeNameLen) WCHAR* typeName, DWORD typeNameLen)
+{
+ bool isFirst = true;
+ ToRelease<ICorDebugTypeEnum> pTypeEnum;
+ if(SUCCEEDED(pType->EnumerateTypeParameters(&pTypeEnum)))
+ {
+ ULONG numTypes = 0;
+ ToRelease<ICorDebugType> pCurrentTypeParam;
+
+ while(SUCCEEDED(pTypeEnum->Next(1, &pCurrentTypeParam, &numTypes)))
+ {
+ if(numTypes == 0) break;
+
+ if(isFirst)
+ {
+ isFirst = false;
+ wcsncat_s(typeName, typeNameLen, L"<", typeNameLen);
+ }
+ else wcsncat_s(typeName, typeNameLen, L",", typeNameLen);
+
+ WCHAR typeParamName[mdNameLen];
+ typeParamName[0] = L'\0';
+ CalculateTypeName(pCurrentTypeParam, typeParamName, mdNameLen);
+ wcsncat_s(typeName, typeNameLen, typeParamName, typeNameLen);
+ }
+ if(!isFirst)
+ wcsncat_s(typeName, typeNameLen, L">", typeNameLen);
+ }
+
+ return S_OK;
+}
+
+// Determines the text name for the type of this node and caches it
+HRESULT ExpressionNode::PopulateType()
+{
+ HRESULT Status = S_OK;
+ if(pTypeName[0] != 0)
+ return S_OK;
+
+ //default value
+ swprintf_s(pTypeName, MAX_EXPRESSION, L"<unknown>");
+
+ // if we are displaying this type as a specific sub-type, use that
+ if(pTypeCast != NULL)
+ return CalculateTypeName(pTypeCast, pTypeName, MAX_EXPRESSION);
+
+ // if there is no value then either we succesfully already determined the type
+ // name, or this node has no value or type and thus no type name.
+ if(pValue == NULL)
+ return S_OK;
+
+ // get the type from the value and then calculate a name based on that
+ ToRelease<ICorDebugType> pType;
+ ToRelease<ICorDebugValue2> pValue2;
+ if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugValue2, (void**) &pValue2)) && SUCCEEDED(pValue2->GetExactType(&pType)))
+ return CalculateTypeName(pType, pTypeName, MAX_EXPRESSION);
+
+ return S_OK;
+}
+
+// Node expansion helpers
+
+// Inserts a new child at the end of the linked list of children
+// PERF: This has O(N) insert time but these lists should never be large
+VOID ExpressionNode::AddChild(ExpressionNode* pNewChild)
+{
+ if(pChild == NULL)
+ pChild = pNewChild;
+ else
+ {
+ ExpressionNode* pCursor = pChild;
+ while(pCursor->pNextSibling != NULL)
+ pCursor = pCursor->pNextSibling;
+ pCursor->pNextSibling = pNewChild;
+ }
+}
+
+// Helper that determines if the current node is on the path of nodes represented by
+// expression varToExpand
+BOOL ExpressionNode::ShouldExpandVariable(__in_z WCHAR* varToExpand)
+{
+ if(pAbsoluteExpression == NULL || varToExpand == NULL) return FALSE;
+
+ // if there is a cast operation, move past it
+ WCHAR* pEndCast = _wcschr(varToExpand, L')');
+ varToExpand = (pEndCast == NULL) ? varToExpand : pEndCast+1;
+
+ size_t varToExpandLen = _wcslen(varToExpand);
+ size_t currentExpansionLen = _wcslen(pAbsoluteExpression);
+ if(currentExpansionLen > varToExpandLen) return FALSE;
+ if(currentExpansionLen < varToExpandLen &&
+ varToExpand[currentExpansionLen] != L'.' &&
+ varToExpand[currentExpansionLen] != L'[')
+ return FALSE;
+ if(_wcsncmp(pAbsoluteExpression, varToExpand, currentExpansionLen) != 0) return FALSE;
+
+ return TRUE;
+}
+
+// Expands this array node by creating child nodes with expressions refering to individual array elements
+HRESULT ExpressionNode::ExpandSzArray(ICorDebugValue* pInnerValue, __in_z WCHAR* varToExpand)
+{
+ HRESULT Status = S_OK;
+ ToRelease<ICorDebugArrayValue> pArrayValue;
+ IfFailRet(pInnerValue->QueryInterface(IID_ICorDebugArrayValue, (LPVOID*) &pArrayValue));
+
+ ULONG32 nRank;
+ IfFailRet(pArrayValue->GetRank(&nRank));
+ if (nRank != 1)
+ {
+ _snwprintf_s(pErrorMessage, MAX_ERROR, _TRUNCATE, L"Multi-dimensional arrays NYI");
+ return E_UNEXPECTED;
+ }
+
+ ULONG32 cElements;
+ IfFailRet(pArrayValue->GetCount(&cElements));
+
+ //TODO: do we really want all the elements? This could be huge!
+ for (ULONG32 i=0; i < cElements; i++)
+ {
+ WCHAR index[20];
+ swprintf_s(index, 20, L"%d", i);
+
+ ToRelease<ICorDebugValue> pElementValue;
+ IfFailRet(pArrayValue->GetElementAtPosition(i, &pElementValue));
+ ExpressionNode* pExpr = new ExpressionNode(pAbsoluteExpression, ChildKind_Index, index, pElementValue, NULL, pILFrame);
+ AddChild(pExpr);
+ pExpr->Expand(varToExpand);
+ }
+ return S_OK;
+}
+
+// Expands this struct/class node by creating child nodes with expressions refering to individual field values
+// and one node for the basetype value
+HRESULT ExpressionNode::ExpandFields(ICorDebugValue* pInnerValue, __in_z WCHAR* varToExpand)
+{
+ HRESULT Status = S_OK;
+
+ mdTypeDef currentTypeDef;
+ ToRelease<ICorDebugClass> pClass;
+ ToRelease<ICorDebugType> pType;
+ ToRelease<ICorDebugModule> pModule;
+ if(pTypeCast == NULL)
+ {
+ ToRelease<ICorDebugValue2> pValue2;
+ IfFailRet(pInnerValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2));
+ IfFailRet(pValue2->GetExactType(&pType));
+ }
+ else
+ {
+ pType = pTypeCast;
+ pType->AddRef();
+ }
+ IfFailRet(pType->GetClass(&pClass));
+ IfFailRet(pClass->GetModule(&pModule));
+ IfFailRet(pClass->GetToken(&currentTypeDef));
+
+ ToRelease<IUnknown> pMDUnknown;
+ ToRelease<IMetaDataImport> pMD;
+ IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
+ IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
+
+ // If the current type has a base type that isn't object, enum, or ValueType then add a node for the base type
+ WCHAR baseTypeName[mdNameLen] = L"\0";
+ ToRelease<ICorDebugType> pBaseType;
+ ExpressionNode* pBaseTypeNode = NULL;
+ if(SUCCEEDED(pType->GetBase(&pBaseType)) && pBaseType != NULL && SUCCEEDED(CalculateTypeName(pBaseType, baseTypeName, mdNameLen)))
+ {
+ if(_wcsncmp(baseTypeName, L"System.Enum", 11) == 0)
+ return S_OK;
+ else if(_wcsncmp(baseTypeName, L"System.Object", 13) != 0 && _wcsncmp(baseTypeName, L"System.ValueType", 16) != 0)
+ {
+ pBaseTypeNode = new ExpressionNode(pAbsoluteExpression, ChildKind_BaseClass, L"<baseclass>", pInnerValue, pBaseType, pILFrame);
+ AddChild(pBaseTypeNode);
+ }
+ }
+
+ // add nodes for all the fields in this object
+ ULONG numFields = 0;
+ HCORENUM fEnum = NULL;
+ mdFieldDef fieldDef;
+ BOOL fieldExpanded = FALSE;
+ while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
+ {
+ mdTypeDef classDef = 0;
+ ULONG nameLen = 0;
+ DWORD fieldAttr = 0;
+ WCHAR mdName[mdNameLen];
+ WCHAR typeName[mdNameLen];
+ CorElementType fieldDefaultValueEt;
+ UVCP_CONSTANT pDefaultValue;
+ ULONG cchDefaultValue;
+ if(SUCCEEDED(pMD->GetFieldProps(fieldDef, &classDef, mdName, mdNameLen, &nameLen, &fieldAttr, NULL, NULL, (DWORD*)&fieldDefaultValueEt, &pDefaultValue, &cchDefaultValue)))
+ {
+ ToRelease<ICorDebugType> pFieldType;
+ ToRelease<ICorDebugValue> pFieldVal;
+
+ // static fields (of any kind - AppDomain, thread, context, RVA)
+ if (fieldAttr & fdStatic)
+ {
+ pType->GetStaticFieldValue(fieldDef, pILFrame, &pFieldVal);
+ }
+ // non-static fields on an object instance
+ else if(pInnerValue != NULL)
+ {
+ ToRelease<ICorDebugObjectValue> pObjValue;
+ if (SUCCEEDED(pInnerValue->QueryInterface(IID_ICorDebugObjectValue, (LPVOID*) &pObjValue)))
+ pObjValue->GetFieldValue(pClass, fieldDef, &pFieldVal);
+ }
+ // skip over non-static fields on static types
+ else
+ {
+ continue;
+ }
+
+ // we didn't get a value yet and there is default value available
+ // need to calculate the type because there won't be a ICorDebugValue to derive it from
+ if(pFieldVal == NULL && pDefaultValue != NULL)
+ {
+ FindTypeFromElementType(fieldDefaultValueEt, &pFieldType);
+ }
+
+ ExpressionNode* pNewChildNode = new ExpressionNode(pAbsoluteExpression, ChildKind_Field, mdName, pFieldVal, pFieldType, pILFrame, pDefaultValue, cchDefaultValue);
+ AddChild(pNewChildNode);
+ if(pNewChildNode->Expand(varToExpand) != S_FALSE)
+ fieldExpanded = TRUE;
+ }
+ }
+ pMD->CloseEnum(fEnum);
+
+ // Only recurse to expand the base type if all of these hold:
+ // 1) base type exists
+ // 2) no field was expanded
+ // 3) the non-casting portion of the varToExpand doesn't match the current expression
+ // OR the cast exists and doesn't match
+
+ if(pBaseTypeNode == NULL) return Status;
+ if(fieldExpanded) return Status;
+
+ WCHAR* pEndCast = _wcschr(varToExpand, L')');
+ WCHAR* pNonCast = (pEndCast == NULL) ? varToExpand : pEndCast+1;
+ if(_wcscmp(pNonCast, pAbsoluteExpression) != 0)
+ {
+ pBaseTypeNode->Expand(varToExpand);
+ return Status;
+ }
+
+ if(varToExpand[0] == L'(' && pEndCast != NULL)
+ {
+ int cchCastTypeName = ((int)(pEndCast-1)-(int)varToExpand)/2;
+ PopulateType();
+ if(_wcslen(pTypeName) != (cchCastTypeName) ||
+ _wcsncmp(varToExpand+1, pTypeName, cchCastTypeName) != 0)
+ {
+ pBaseTypeNode->Expand(varToExpand);
+ return Status;
+ }
+ }
+
+ return Status;
+}
+
+
+// Value Population functions
+
+//Helper for unwrapping values
+HRESULT ExpressionNode::DereferenceAndUnboxValue(ICorDebugValue * pInputValue, ICorDebugValue** ppOutputValue, BOOL * pIsNull)
+{
+ HRESULT Status = S_OK;
+ *ppOutputValue = NULL;
+ if(pIsNull != NULL) *pIsNull = FALSE;
+
+ ToRelease<ICorDebugReferenceValue> pReferenceValue;
+ Status = pInputValue->QueryInterface(IID_ICorDebugReferenceValue, (LPVOID*) &pReferenceValue);
+ if (SUCCEEDED(Status))
+ {
+ BOOL isNull = FALSE;
+ IfFailRet(pReferenceValue->IsNull(&isNull));
+ if(!isNull)
+ {
+ ToRelease<ICorDebugValue> pDereferencedValue;
+ IfFailRet(pReferenceValue->Dereference(&pDereferencedValue));
+ return DereferenceAndUnboxValue(pDereferencedValue, ppOutputValue);
+ }
+ else
+ {
+ if(pIsNull != NULL) *pIsNull = TRUE;
+ *ppOutputValue = pInputValue;
+ (*ppOutputValue)->AddRef();
+ return S_OK;
+ }
+ }
+
+ ToRelease<ICorDebugBoxValue> pBoxedValue;
+ Status = pInputValue->QueryInterface(IID_ICorDebugBoxValue, (LPVOID*) &pBoxedValue);
+ if (SUCCEEDED(Status))
+ {
+ ToRelease<ICorDebugObjectValue> pUnboxedValue;
+ IfFailRet(pBoxedValue->GetObject(&pUnboxedValue));
+ return DereferenceAndUnboxValue(pUnboxedValue, ppOutputValue);
+ }
+ *ppOutputValue = pInputValue;
+ (*ppOutputValue)->AddRef();
+ return S_OK;
+}
+
+// Returns TRUE if the value derives from System.Enum
+BOOL ExpressionNode::IsEnum(ICorDebugValue * pInputValue)
+{
+ ToRelease<ICorDebugValue> pValue;
+ if(FAILED(DereferenceAndUnboxValue(pInputValue, &pValue, NULL))) return FALSE;
+
+ WCHAR baseTypeName[mdNameLen];
+ ToRelease<ICorDebugValue2> pValue2;
+ ToRelease<ICorDebugType> pType;
+ ToRelease<ICorDebugType> pBaseType;
+
+ if(FAILED(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2))) return FALSE;
+ if(FAILED(pValue2->GetExactType(&pType))) return FALSE;
+ if(FAILED(pType->GetBase(&pBaseType)) || pBaseType == NULL) return FALSE;
+ if(FAILED(CalculateTypeName(pBaseType, baseTypeName, mdNameLen))) return FALSE;
+
+ return (_wcsncmp(baseTypeName, L"System.Enum", 11) == 0);
+}
+
+// Calculates the value text for nodes that have enum values
+HRESULT ExpressionNode::PopulateEnumValue(ICorDebugValue* pEnumValue, BYTE* enumValue)
+{
+ HRESULT Status = S_OK;
+
+ mdTypeDef currentTypeDef;
+ ToRelease<ICorDebugClass> pClass;
+ ToRelease<ICorDebugValue2> pValue2;
+ ToRelease<ICorDebugType> pType;
+ ToRelease<ICorDebugModule> pModule;
+ IfFailRet(pEnumValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2));
+ IfFailRet(pValue2->GetExactType(&pType));
+ IfFailRet(pType->GetClass(&pClass));
+ IfFailRet(pClass->GetModule(&pModule));
+ IfFailRet(pClass->GetToken(&currentTypeDef));
+
+ ToRelease<IUnknown> pMDUnknown;
+ ToRelease<IMetaDataImport> pMD;
+ IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
+ IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
+
+
+ //First, we need to figure out the underlying enum type so that we can correctly type cast the raw values of each enum constant
+ //We get that from the non-static field of the enum variable (I think the field is called __value or something similar)
+ ULONG numFields = 0;
+ HCORENUM fEnum = NULL;
+ mdFieldDef fieldDef;
+ CorElementType enumUnderlyingType = ELEMENT_TYPE_END;
+ while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
+ {
+ DWORD fieldAttr = 0;
+ PCCOR_SIGNATURE pSignatureBlob = NULL;
+ ULONG sigBlobLength = 0;
+ if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, NULL, 0, NULL, &fieldAttr, &pSignatureBlob, &sigBlobLength, NULL, NULL, NULL)))
+ {
+ if((fieldAttr & fdStatic) == 0)
+ {
+ CorSigUncompressCallingConv(pSignatureBlob);
+ enumUnderlyingType = CorSigUncompressElementType(pSignatureBlob);
+ break;
+ }
+ }
+ }
+ pMD->CloseEnum(fEnum);
+
+
+ //Now that we know the underlying enum type, let's decode the enum variable into OR-ed, human readable enum contants
+ fEnum = NULL;
+ bool isFirst = true;
+ ULONG64 remainingValue = *((ULONG64*)enumValue);
+ WCHAR* pTextValueCursor = pTextValue;
+ DWORD cchTextValueCursor = MAX_EXPRESSION;
+ while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
+ {
+ ULONG nameLen = 0;
+ DWORD fieldAttr = 0;
+ WCHAR mdName[mdNameLen];
+ WCHAR typeName[mdNameLen];
+ UVCP_CONSTANT pRawValue = NULL;
+ ULONG rawValueLength = 0;
+ if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, mdName, mdNameLen, &nameLen, &fieldAttr, NULL, NULL, NULL, &pRawValue, &rawValueLength)))
+ {
+ DWORD enumValueRequiredAttributes = fdPublic | fdStatic | fdLiteral | fdHasDefault;
+ if((fieldAttr & enumValueRequiredAttributes) != enumValueRequiredAttributes)
+ continue;
+
+ ULONG64 currentConstValue = 0;
+ switch (enumUnderlyingType)
+ {
+ case ELEMENT_TYPE_CHAR:
+ case ELEMENT_TYPE_I1:
+ currentConstValue = (ULONG64)(*((CHAR*)pRawValue));
+ break;
+ case ELEMENT_TYPE_U1:
+ currentConstValue = (ULONG64)(*((BYTE*)pRawValue));
+ break;
+ case ELEMENT_TYPE_I2:
+ currentConstValue = (ULONG64)(*((SHORT*)pRawValue));
+ break;
+ case ELEMENT_TYPE_U2:
+ currentConstValue = (ULONG64)(*((USHORT*)pRawValue));
+ break;
+ case ELEMENT_TYPE_I4:
+ currentConstValue = (ULONG64)(*((INT32*)pRawValue));
+ break;
+ case ELEMENT_TYPE_U4:
+ currentConstValue = (ULONG64)(*((UINT32*)pRawValue));
+ break;
+ case ELEMENT_TYPE_I8:
+ currentConstValue = (ULONG64)(*((LONG*)pRawValue));
+ break;
+ case ELEMENT_TYPE_U8:
+ currentConstValue = (ULONG64)(*((ULONG*)pRawValue));
+ break;
+ case ELEMENT_TYPE_I:
+ currentConstValue = (ULONG64)(*((int*)pRawValue));
+ break;
+ case ELEMENT_TYPE_U:
+ case ELEMENT_TYPE_R4:
+ case ELEMENT_TYPE_R8:
+ // Technically U and the floating-point ones are options in the CLI, but not in the CLS or C#, so these are NYI
+ default:
+ currentConstValue = 0;
+ }
+
+ if((currentConstValue == remainingValue) || ((currentConstValue != 0) && ((currentConstValue & remainingValue) == currentConstValue)))
+ {
+ remainingValue &= ~currentConstValue;
+ DWORD charsCopied = 0;
+ if(isFirst)
+ {
+ charsCopied = _snwprintf_s(pTextValueCursor, cchTextValueCursor, _TRUNCATE, L"= %s", mdName);
+ isFirst = false;
+ }
+ else
+ charsCopied = _snwprintf_s(pTextValueCursor, cchTextValueCursor, _TRUNCATE, L" | %s", mdName);
+
+ // if an error or truncation occurred, stop copying
+ if(charsCopied == -1)
+ {
+ cchTextValueCursor = 0;
+ pTextValueCursor = NULL;
+ }
+ else
+ {
+ // charsCopied is the number of characters copied, not counting the terminating null
+ // this advances the cursor to point right at the terminating null so that future copies
+ // will concatenate the string
+ pTextValueCursor += charsCopied;
+ cchTextValueCursor -= charsCopied;
+ }
+ }
+ }
+ }
+ pMD->CloseEnum(fEnum);
+
+ return Status;
+}
+
+// Helper that caches the textual value for nodes that evaluate to a string object
+HRESULT ExpressionNode::GetDebuggeeStringValue(ICorDebugValue* pInputValue, __inout_ecount(cchBuffer) WCHAR* wszBuffer, DWORD cchBuffer)
+{
+ HRESULT Status;
+
+ ToRelease<ICorDebugStringValue> pStringValue;
+ IfFailRet(pInputValue->QueryInterface(IID_ICorDebugStringValue, (LPVOID*) &pStringValue));
+
+ ULONG32 cchValueReturned;
+ IfFailRet(pStringValue->GetString(cchBuffer, &cchValueReturned, wszBuffer));
+
+ return S_OK;
+}
+
+// Retrieves the string value for a constant
+HRESULT ExpressionNode::GetConstantStringValue(__inout_ecount(cchBuffer) WCHAR* wszBuffer, DWORD cchBuffer)
+{
+ // The string encoded in metadata isn't null-terminated
+ // so we need to copy it to a null terminated buffer
+ DWORD copyLen = cchDefaultValue;
+ if(copyLen > cchBuffer-1)
+ copyLen = cchDefaultValue;
+
+ wcsncpy_s(wszBuffer, cchBuffer, (WCHAR*)pDefaultValue, copyLen);
+ return S_OK;
+}
+
+// Helper that caches the textual value for nodes that evaluate to array objects
+HRESULT ExpressionNode::PopulateSzArrayValue(ICorDebugValue* pInputValue)
+{
+ HRESULT Status = S_OK;
+
+ ToRelease<ICorDebugArrayValue> pArrayValue;
+ IfFailRet(pInputValue->QueryInterface(IID_ICorDebugArrayValue, (LPVOID*) &pArrayValue));
+
+ ULONG32 nRank;
+ IfFailRet(pArrayValue->GetRank(&nRank));
+ if (nRank != 1)
+ {
+ _snwprintf_s(pErrorMessage, MAX_EXPRESSION, _TRUNCATE, L"Multi-dimensional arrays NYI");
+ return E_UNEXPECTED;
+ }
+
+ ULONG32 cElements;
+ IfFailRet(pArrayValue->GetCount(&cElements));
+
+ if (cElements == 0)
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"(empty)");
+ else if (cElements == 1)
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"(1 element)");
+ else
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"(%d elements)", cElements);
+
+ return S_OK;
+}
+
+// Helper that caches the textual value for nodes of any type
+HRESULT ExpressionNode::PopulateTextValueHelper()
+{
+ HRESULT Status = S_OK;
+
+ BOOL isNull = TRUE;
+ ToRelease<ICorDebugValue> pInnerValue;
+ CorElementType corElemType;
+ ULONG32 cbSize = 0;
+ if(pValue != NULL)
+ {
+ IfFailRet(DereferenceAndUnboxValue(pValue, &pInnerValue, &isNull));
+
+ if(isNull)
+ {
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= null");
+ return S_OK;
+ }
+ IfFailRet(pInnerValue->GetSize(&cbSize));
+ IfFailRet(pInnerValue->GetType(&corElemType));
+ }
+ else if(pDefaultValue != NULL)
+ {
+ if(pTypeCast == NULL)
+ {
+ // this shouldn't happen, but just print nothing if it does
+ return S_OK;
+ }
+ // This works around an irritating issue in ICorDebug. For default values
+ // we have to construct the ICorDebugType ourselves, however ICorDebug
+ // doesn't allow type construction using the correct element types. The
+ // caller must past CLASS or VALUETYPE even when a more specific short
+ // form element type is applicable. That means that later, here, we get
+ // back the wrong answer. To work around this we format the type as a
+ // string, and check it against all the known types. That allows us determine
+ // everything except VALUETYPE/CLASS. Thankfully that distinction is the
+ // one piece of data ICorDebugType will tell us if needed.
+ if(FAILED(GetCanonicalElementTypeForTypeName(GetTypeName(), &corElemType)))
+ {
+ pTypeCast->GetType(&corElemType);
+ }
+
+ switch(corElemType)
+ {
+ case ELEMENT_TYPE_BOOLEAN:
+ case ELEMENT_TYPE_I1:
+ case ELEMENT_TYPE_U1:
+ cbSize = 1;
+ break;
+
+ case ELEMENT_TYPE_CHAR:
+ case ELEMENT_TYPE_I2:
+ case ELEMENT_TYPE_U2:
+ cbSize = 2;
+ break;
+
+ case ELEMENT_TYPE_I:
+ case ELEMENT_TYPE_U:
+ case ELEMENT_TYPE_I4:
+ case ELEMENT_TYPE_U4:
+ case ELEMENT_TYPE_R4:
+ cbSize = 4;
+ break;
+
+ case ELEMENT_TYPE_I8:
+ case ELEMENT_TYPE_U8:
+ case ELEMENT_TYPE_R8:
+ cbSize = 8;
+ break;
+ }
+ }
+
+ if (corElemType == ELEMENT_TYPE_STRING)
+ {
+ WCHAR buffer[MAX_EXPRESSION];
+ buffer[0] = L'\0';
+ if(pInnerValue != NULL)
+ GetDebuggeeStringValue(pInnerValue, buffer, MAX_EXPRESSION);
+ else
+ GetConstantStringValue(buffer, MAX_EXPRESSION);
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= \"%s\"", buffer);
+ }
+ else if (corElemType == ELEMENT_TYPE_SZARRAY)
+ {
+ return PopulateSzArrayValue(pInnerValue);
+ }
+
+
+ ArrayHolder<BYTE> rgbValue = new BYTE[cbSize];
+ memset(rgbValue.GetPtr(), 0, cbSize * sizeof(BYTE));
+ if(pInnerValue != NULL)
+ {
+ ToRelease<ICorDebugGenericValue> pGenericValue;
+ IfFailRet(pInnerValue->QueryInterface(IID_ICorDebugGenericValue, (LPVOID*) &pGenericValue));
+ IfFailRet(pGenericValue->GetValue((LPVOID) &(rgbValue[0])));
+ }
+ else
+ {
+ memcpy((LPVOID) &(rgbValue[0]), pDefaultValue, cbSize);
+ }
+
+ //TODO: this should really be calculated from the type
+ if(pInnerValue != NULL && IsEnum(pInnerValue))
+ {
+ Status = PopulateEnumValue(pInnerValue, rgbValue);
+ return Status;
+ }
+
+ switch (corElemType)
+ {
+ default:
+ _snwprintf_s(pErrorMessage, MAX_ERROR, _TRUNCATE, L"Unhandled CorElementType: 0x%x", corElemType);
+ Status = E_FAIL;
+ break;
+
+ case ELEMENT_TYPE_PTR:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"<pointer>");
+ break;
+
+ case ELEMENT_TYPE_FNPTR:
+ {
+ CORDB_ADDRESS addr = 0;
+ ToRelease<ICorDebugReferenceValue> pReferenceValue = NULL;
+ if(SUCCEEDED(pInnerValue->QueryInterface(IID_ICorDebugReferenceValue, (LPVOID*) &pReferenceValue)))
+ pReferenceValue->GetValue(&addr);
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"<function pointer 0x%x>", addr);
+ }
+ break;
+
+ case ELEMENT_TYPE_VALUETYPE:
+ case ELEMENT_TYPE_CLASS:
+ case ELEMENT_TYPE_OBJECT:
+ ULONG64 pointer;
+ if(pInnerValue != NULL && SUCCEEDED(pInnerValue->GetAddress(&pointer)))
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"@ 0x%p", (void *) pointer);
+ break;
+
+ case ELEMENT_TYPE_BOOLEAN:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= %s", rgbValue[0] == 0 ? L"false" : L"true");
+ break;
+
+ case ELEMENT_TYPE_CHAR:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= '%C'", *(WCHAR *) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_I1:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= %d", *(char*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_U1:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= %d", *(unsigned char*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_I2:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= %hd", *(short*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_U2:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= %hu", *(unsigned short*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_I:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= %d", *(int*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_U:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= %u", *(unsigned int*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_I4:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= %d", *(int*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_U4:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= %u", *(unsigned int*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_I8:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= %I64d", *(__int64*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_U8:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= %I64u", *(unsigned __int64*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_R4:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"= %f", (double) *(float*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_R8:
+ _snwprintf_s(pTextValue, MAX_EXPRESSION, _TRUNCATE, L"%f", *(double*) &(rgbValue[0]));
+ break;
+
+ // TODO: The following corElementTypes are not yet implemented here. Array
+ // might be interesting to add, though the others may be of rather limited use:
+ // ELEMENT_TYPE_ARRAY = 0x14, // MDARRAY <type> <rank> <bcount> <bound1> ... <lbcount> <lb1> ...
+ //
+ // ELEMENT_TYPE_GENERICINST = 0x15, // GENERICINST <generic type> <argCnt> <arg1> ... <argn>
+ }
+
+ return Status;
+}
+
+// Caches the textual value of this node
+HRESULT ExpressionNode::PopulateTextValue()
+{
+ if(pErrorMessage[0] != 0)
+ return E_UNEXPECTED;
+ if(pValue == NULL && pDefaultValue == NULL)
+ return S_OK;
+ HRESULT Status = PopulateTextValueHelper();
+ if(FAILED(Status) && pErrorMessage[0] == 0)
+ {
+ _snwprintf_s(pErrorMessage, MAX_ERROR, _TRUNCATE, L"Error in PopulateTextValueHelper: 0x%x", Status);
+ }
+ return Status;
+}
+
+
+// Expression parsing and search
+
+//Callback that searches a frame to determine if it contains a local variable or parameter of a given name
+VOID ExpressionNode::EvaluateExpressionFrameScanCallback(ICorDebugFrame* pFrame, VOID* pUserData)
+{
+ EvaluateExpressionFrameScanData* pData = (EvaluateExpressionFrameScanData*)pUserData;
+
+ // we already found what we were looking for, just continue
+ if(pData->pFoundValue != NULL)
+ return;
+
+ // if any of these fail we just continue on
+ // querying for ILFrame will frequently fail because many frames aren't IL
+ ToRelease<ICorDebugILFrame> pILFrame;
+ HRESULT Status = pFrame->QueryInterface(IID_ICorDebugILFrame, (LPVOID*) &pILFrame);
+ if (FAILED(Status))
+ {
+ return;
+ }
+ // we need to save off the first frame we find regardless of whether we find the
+ // local or not. We might need this frame later for static field lookup.
+ if(pData->pFirstFrame == NULL)
+ {
+ pData->pFirstFrame = pILFrame;
+ pData->pFirstFrame->AddRef();
+ }
+ // not all IL frames map to an assembly (ex. LCG)
+ ToRelease<ICorDebugFunction> pFunction;
+ Status = pFrame->GetFunction(&pFunction);
+ if (FAILED(Status))
+ {
+ return;
+ }
+ // from here down shouldn't generally fail, but just in case
+ mdMethodDef methodDef;
+ Status = pFunction->GetToken(&methodDef);
+ if (FAILED(Status))
+ {
+ return;
+ }
+ ToRelease<ICorDebugModule> pModule;
+ Status = pFunction->GetModule(&pModule);
+ if (FAILED(Status))
+ {
+ return;
+ }
+ ToRelease<IUnknown> pMDUnknown;
+ Status = pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown);
+ if (FAILED(Status))
+ {
+ return;
+ }
+ ToRelease<IMetaDataImport> pMD;
+ Status = pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD);
+ if (FAILED(Status))
+ {
+ return;
+ }
+
+ pData->pFoundFrame = pILFrame;
+ pData->pFoundFrame->AddRef();
+ // Enumerate all the parameters
+ EnumerateParameters(pMD, methodDef, pILFrame, EvaluateExpressionVariableScanCallback, pUserData);
+ // Enumerate all the locals
+ EnumerateLocals(pMD, methodDef, pILFrame, EvaluateExpressionVariableScanCallback, pUserData);
+
+ // if we didn't find it in this frame then clear the frame back out
+ if(pData->pFoundValue == NULL)
+ {
+ pData->pFoundFrame = NULL;
+ }
+
+ return;
+}
+
+//Callback checks to see if a given local/parameter has name pName
+VOID ExpressionNode::EvaluateExpressionVariableScanCallback(ICorDebugValue* pValue, __in_z WCHAR* pName, __out_z WCHAR* pErrorMessage, VOID* pUserData)
+{
+ EvaluateExpressionFrameScanData* pData = (EvaluateExpressionFrameScanData*)pUserData;
+ if(_wcscmp(pName, pData->pIdentifier) == 0)
+ {
+ // found it
+ pData->pFoundValue = pValue;
+ pValue->AddRef();
+ }
+ return;
+}
+
+//Factory method that recursively parses pExpression and create an ExpressionNode
+// pExpression - the entire expression being parsed
+// pExpressionRemainder - the portion of the expression that remains to be parsed in this
+// recursive invocation
+// charactersParsed - the number of characters that have been parsed from pExpression
+// so far (todo: this is basically the difference between remainder and
+// full expression, do we need it?)
+// pParsedValue - A debuggee value that should be used as the context for interpreting
+// pExpressionRemainder
+// pParsedType - A debuggee type that should be used as the context for interpreting
+// pExpressionRemainder.
+// pParsedDefaultValue - A fixed value from metadata that should be used as context for
+// interpretting pExpressionRemainder
+// cchParsedDefaultValue- Size of pParsedDefaultValue
+// pFrame - A debuggee IL frame that disambiguates the thread and context needed
+// to evaluate a thread-static or context-static value
+// ppExpressionNode - OUT - the resulting expression node
+//
+//
+// Valid combinations of state comming into this method:
+// The expression up to charactersParsed isn't recognized yet:
+// pParsedValue = pParsedType = pParsedDefaultValue = NULL
+// cchParsedDefaultValue = 0
+// The expression up to charactersParsed is a recognized type:
+// pParsedType = <parsed type>
+// pParsedValue = pParsedDefaultValue = NULL
+// cchParsedDefaultValue = 0
+// The expression up to charactersParsed is a recognized value in the debuggee:
+// pParsedValue = <parsed value>
+// pParsedType = pParsedDefaultValue = NULL
+// cchParsedDefaultValue = 0
+// The expression up to charactersParsed is a recognized default value stored in metadata:
+// pParsedValue = NULL
+// pParsedType = <type calculated from metadata>
+// pParsedDefaultValue = <value from metadata>
+// cchParsedDefaultValue = <size of metadata value>
+//
+//
+// REFACTORING NOTE: This method is very similar (but not identical) to the expansion logic
+// in ExpressionNode. The primary difference is that the nodes expand all
+// fields/indices whereas this function only expands along a precise route.
+// If the ExpressionNode code where enhanced to support expanding precisely
+// large portions of this function could be disposed of. As soon as the function
+// matched the initial name it could create an ExpressionNode and then use the
+// ExpressionNode expansion functions to drill down to the actual node required.
+// Also need to make sure the nodes manage lifetime ok when a parent is destroyed
+// but a child node is still referenced.
+HRESULT ExpressionNode::CreateExpressionNodeHelper(__in_z WCHAR* pExpression,
+ __in_z WCHAR* pExpressionParseRemainder,
+ DWORD charactersParsed,
+ ICorDebugValue* pParsedValue,
+ ICorDebugType* pParsedType,
+ UVCP_CONSTANT pParsedDefaultValue,
+ ULONG cchParsedDefaultValue,
+ ICorDebugILFrame* pFrame,
+ ExpressionNode** ppExpressionNode)
+{
+ HRESULT Status = S_OK;
+ WCHAR* pExpressionCursor = pExpressionParseRemainder;
+ DWORD currentCharsParsed = charactersParsed;
+ WCHAR pIdentifier[mdNameLen];
+ pIdentifier[0] = 0;
+ BOOL isArray = FALSE;
+ WCHAR pResultBuffer[MAX_EXPRESSION];
+
+ // Get the next name from the expression string
+ if(FAILED(Status = ParseNextIdentifier(&pExpressionCursor, pIdentifier, mdNameLen, pResultBuffer, MAX_EXPRESSION, &currentCharsParsed, &isArray)))
+ {
+ *ppExpressionNode = new ExpressionNode(pExpression, pResultBuffer);
+ if(*ppExpressionNode == NULL)
+ return E_OUTOFMEMORY;
+ else
+ return S_OK;
+ }
+
+ // we've gone as far as we need, nothing left to parse
+ if(Status == S_FALSE)
+ {
+ ToRelease<ICorDebugValue> pValue;
+ *ppExpressionNode = new ExpressionNode(pExpression, ChildKind_BaseClass, pExpression, pParsedValue, pParsedType, pFrame, pParsedDefaultValue, cchParsedDefaultValue);
+ if(*ppExpressionNode == NULL)
+ return E_OUTOFMEMORY;
+ else
+ return S_OK;
+ }
+ // if we are just starting and have no context then we need to search locals/parameters/type names
+ else if(pParsedValue == NULL && pParsedType == NULL)
+ {
+ // the first identifier must be a name, not an indexing expression
+ if(isArray)
+ {
+ *ppExpressionNode = new ExpressionNode(pExpression, L"Expression must begin with a local variable, parameter, or fully qualified type name");
+ return S_OK;
+ }
+
+ // scan for root on stack
+ EvaluateExpressionFrameScanData data;
+ data.pIdentifier = pIdentifier;
+ data.pFoundValue = NULL;
+ data.pFoundFrame = NULL;
+ data.pFirstFrame = NULL;
+ data.pErrorMessage = pResultBuffer;
+ data.cchErrorMessage = MAX_EXPRESSION;
+ EnumerateFrames(EvaluateExpressionFrameScanCallback, (VOID*) &data);
+
+ if(data.pFoundValue != NULL)
+ {
+ // found the root, now recurse along the expression
+ return CreateExpressionNodeHelper(pExpression, pExpressionCursor, currentCharsParsed, data.pFoundValue, NULL, NULL, 0, data.pFoundFrame, ppExpressionNode);
+ }
+
+ // didn't find it - search the type table for a matching name
+ WCHAR pName[MAX_EXPRESSION];
+ while(true)
+ {
+ wcsncpy_s(pName, MAX_EXPRESSION, pExpression, currentCharsParsed);
+ ToRelease<ICorDebugType> pType;
+ if(SUCCEEDED(FindTypeByName(pName, &pType)))
+ return CreateExpressionNodeHelper(pExpression, pExpressionCursor, currentCharsParsed, NULL, pType, NULL, 0, data.pFirstFrame, ppExpressionNode);
+
+ if(FAILED(Status = ParseNextIdentifier(&pExpressionCursor, pIdentifier, mdNameLen, pResultBuffer, MAX_EXPRESSION, &currentCharsParsed, &isArray)))
+ {
+ *ppExpressionNode = new ExpressionNode(pExpression, pResultBuffer);
+ return S_OK;
+ }
+ else if(Status == S_FALSE)
+ {
+ break;
+ }
+ }
+
+ WCHAR errorMessage[MAX_ERROR];
+ swprintf_s(errorMessage, MAX_ERROR, L"No expression prefix could not be matched to an existing type, parameter, or local");
+ *ppExpressionNode = new ExpressionNode(pExpression, errorMessage);
+ return S_OK;
+ }
+
+ // we've got some context from an earlier portion of the search, now just need to continue
+ // by dereferencing and indexing until we reach the end of the expression
+
+ // Figure out the type, module, and metadata from our context information
+ ToRelease<ICorDebugType> pType;
+ BOOL isNull = TRUE;
+ ToRelease<ICorDebugValue> pInnerValue = NULL;
+ if(pParsedValue != NULL)
+ {
+ IfFailRet(DereferenceAndUnboxValue(pParsedValue, &pInnerValue, &isNull));
+
+ if(isNull)
+ {
+ WCHAR parsedExpression[MAX_EXPRESSION];
+ wcsncpy_s(parsedExpression, MAX_EXPRESSION, pExpression, charactersParsed);
+ WCHAR errorMessage[MAX_ERROR];
+ swprintf_s(errorMessage, MAX_ERROR, L"Dereferencing \'%s\' throws NullReferenceException", parsedExpression);
+ *ppExpressionNode = new ExpressionNode(pExpression, errorMessage);
+ return S_OK;
+ }
+
+ ToRelease<ICorDebugValue2> pValue2;
+ IfFailRet(pInnerValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2));
+ IfFailRet(pValue2->GetExactType(&pType));
+ CorElementType et;
+ IfFailRet(pType->GetType(&et));
+ while(et == ELEMENT_TYPE_ARRAY || et == ELEMENT_TYPE_SZARRAY || et == ELEMENT_TYPE_BYREF || et == ELEMENT_TYPE_PTR)
+ {
+ pType->GetFirstTypeParameter(&pType);
+ IfFailRet(pType->GetType(&et));
+ }
+ }
+ else
+ {
+ pType = pParsedType;
+ pType->AddRef();
+ }
+ ToRelease<ICorDebugClass> pClass;
+ IfFailRet(pType->GetClass(&pClass));
+ ToRelease<ICorDebugModule> pModule;
+ IfFailRet(pClass->GetModule(&pModule));
+ mdTypeDef currentTypeDef;
+ IfFailRet(pClass->GetToken(&currentTypeDef));
+
+ ToRelease<IUnknown> pMDUnknown;
+ ToRelease<IMetaDataImport> pMD;
+ IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
+ IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
+
+
+ // if we are searching along and this is an array index dereference
+ if(isArray)
+ {
+ ToRelease<ICorDebugArrayValue> pArrayValue;
+ if(pInnerValue == NULL || FAILED(Status = pInnerValue->QueryInterface(IID_ICorDebugArrayValue, (LPVOID*) &pArrayValue)))
+ {
+ WCHAR errorMessage[MAX_ERROR];
+ swprintf_s(errorMessage, MAX_ERROR, L"Index notation only supported for instances of an array type");
+ *ppExpressionNode = new ExpressionNode(pExpression, errorMessage);
+ return S_OK;
+ }
+
+ ULONG32 nRank;
+ IfFailRet(pArrayValue->GetRank(&nRank));
+ if (nRank != 1)
+ {
+ WCHAR errorMessage[MAX_ERROR];
+ swprintf_s(errorMessage, MAX_ERROR, L"Multi-dimensional arrays NYI");
+ *ppExpressionNode = new ExpressionNode(pExpression, errorMessage);
+ return S_OK;
+ }
+
+ int index = -1;
+ if(swscanf_s(pIdentifier, L"%d", &index) != 1)
+ {
+ WCHAR errorMessage[MAX_ERROR];
+ swprintf_s(errorMessage, MAX_ERROR, L"Failed to parse expression, missing or invalid index expression at character %d", charactersParsed+1);
+ *ppExpressionNode = new ExpressionNode(pExpression, errorMessage);
+ return S_OK;
+ }
+
+ ULONG32 cElements;
+ IfFailRet(pArrayValue->GetCount(&cElements));
+ if(index < 0 || (ULONG32)index >= cElements)
+ {
+ WCHAR errorMessage[MAX_ERROR];
+ swprintf_s(errorMessage, MAX_ERROR, L"Index is out of range for this array");
+ *ppExpressionNode = new ExpressionNode(pExpression, errorMessage);
+ return S_OK;
+ }
+
+ ToRelease<ICorDebugValue> pElementValue;
+ IfFailRet(pArrayValue->GetElementAtPosition(index, &pElementValue));
+ return CreateExpressionNodeHelper(pExpression, pExpressionCursor, currentCharsParsed, pElementValue, NULL, NULL, 0, pFrame, ppExpressionNode);
+ }
+ // if we are searching along and this is field dereference
+ else
+ {
+ ToRelease<ICorDebugType> pBaseType = pType;
+ pBaseType->AddRef();
+
+ while(pBaseType != NULL)
+ {
+ // get the current base type class/token/MD
+ ToRelease<ICorDebugClass> pBaseClass;
+ IfFailRet(pBaseType->GetClass(&pBaseClass));
+ ToRelease<ICorDebugModule> pBaseTypeModule;
+ IfFailRet(pBaseClass->GetModule(&pBaseTypeModule));
+ mdTypeDef baseTypeDef;
+ IfFailRet(pBaseClass->GetToken(&baseTypeDef));
+ ToRelease<IUnknown> pBaseTypeMDUnknown;
+ ToRelease<IMetaDataImport> pBaseTypeMD;
+ IfFailRet(pBaseTypeModule->GetMetaDataInterface(IID_IMetaDataImport, &pBaseTypeMDUnknown));
+ IfFailRet(pBaseTypeMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pBaseTypeMD));
+
+
+ // iterate through all fields at this level of the class hierarchy
+ ULONG numFields = 0;
+ HCORENUM fEnum = NULL;
+ mdFieldDef fieldDef;
+ while(SUCCEEDED(pMD->EnumFields(&fEnum, baseTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
+ {
+ ULONG nameLen = 0;
+ DWORD fieldAttr = 0;
+ WCHAR mdName[mdNameLen];
+ WCHAR typeName[mdNameLen];
+ CorElementType fieldDefaultValueEt;
+ UVCP_CONSTANT pDefaultValue;
+ ULONG cchDefaultValue;
+ if(SUCCEEDED(pBaseTypeMD->GetFieldProps(fieldDef, NULL, mdName, mdNameLen, &nameLen, &fieldAttr, NULL, NULL, (DWORD*)&fieldDefaultValueEt, &pDefaultValue, &cchDefaultValue)) &&
+ _wcscmp(mdName, pIdentifier) == 0)
+ {
+ ToRelease<ICorDebugType> pFieldValType = NULL;
+ ToRelease<ICorDebugValue> pFieldVal;
+ if (fieldAttr & fdStatic)
+ pBaseType->GetStaticFieldValue(fieldDef, pFrame, &pFieldVal);
+ else if(pInnerValue != NULL)
+ {
+ ToRelease<ICorDebugObjectValue> pObjValue;
+ if (SUCCEEDED(pInnerValue->QueryInterface(IID_ICorDebugObjectValue, (LPVOID*) &pObjValue)))
+ pObjValue->GetFieldValue(pBaseClass, fieldDef, &pFieldVal);
+ }
+
+ // we didn't get a value yet and there is default value available
+ // need to calculate the type because there won't be a ICorDebugValue to derive it from
+ if(pFieldVal == NULL && pDefaultValue != NULL)
+ {
+ FindTypeFromElementType(fieldDefaultValueEt, &pFieldValType);
+ }
+ else
+ {
+ // if we aren't using default value, make sure it is cleared out
+ pDefaultValue = NULL;
+ cchDefaultValue = 0;
+ }
+
+ // if we still don't have a value, check if we are trying to get an instance field from a static type
+ if(pInnerValue == NULL && pFieldVal == NULL && pDefaultValue == NULL)
+ {
+ WCHAR pObjectTypeName[MAX_EXPRESSION];
+ CalculateTypeName(pBaseType, pObjectTypeName, MAX_EXPRESSION);
+ WCHAR errorMessage[MAX_ERROR];
+ swprintf_s(errorMessage, MAX_ERROR, L"Can not evaluate instance field \'%s\' from static type \'%s\'", pIdentifier, pObjectTypeName);
+ *ppExpressionNode = new ExpressionNode(pExpression, errorMessage);
+ return S_OK;
+ }
+ return CreateExpressionNodeHelper(pExpression, pExpressionCursor, currentCharsParsed, pFieldVal, pFieldValType, pDefaultValue, cchDefaultValue, pFrame, ppExpressionNode);
+ }
+ }
+
+ //advance to next base type
+ ICorDebugType* pTemp = NULL;
+ pBaseType->GetBase(&pTemp);
+ pBaseType = pTemp;
+ }
+
+ WCHAR pObjectTypeName[MAX_EXPRESSION];
+ CalculateTypeName(pType, pObjectTypeName, MAX_EXPRESSION);
+ WCHAR errorMessage[MAX_ERROR];
+ swprintf_s(errorMessage, MAX_ERROR, L"Field \'%s\' does not exist in type \'%s\'", pIdentifier, pObjectTypeName);
+ *ppExpressionNode = new ExpressionNode(pExpression, errorMessage);
+ return S_OK;
+ }
+
+ return Status;
+}
+
+// Splits apart a C#-like expression and determines the first identifier in the string and updates expression to point
+// at the remaining unparsed portion
+HRESULT ExpressionNode::ParseNextIdentifier(__in_z WCHAR** expression, __inout_ecount(cchIdentifierName) WCHAR* identifierName, DWORD cchIdentifierName, __inout_ecount(cchErrorMessage) WCHAR* errorMessage, DWORD cchErrorMessage, DWORD* charactersParsed, BOOL* isArrayIndex)
+{
+
+ // This algorithm is best understood as a two stage process. The first stage splits
+ // the expression into two chunks an identifier and a remaining expression. The second stage
+ // normalizes the identifier. The splitting algorithm doesn't care if identifiers are well-formed
+ // at all, we do some error checking in the 2nd stage though. For the splitting stage, an identifier is
+ // any first character, followed by as many characters as possible that aren't a '.' or a '['.
+ // In the 2nd stage any '.' character is removed from the front (the only place it could be)
+ // and enclosing braces are removed. An error is recorded if the identifier ends 0 length or the
+ // opening bracket isn't matched. Here is an example showing how we would parse an expression
+ // which is deliberately not very well formed. Each line is the result of calling this function once... it
+ // takes many calls to break the entire expression down.
+ //
+ // expression 1st stage identifier 2nd stage identifier
+ // foo.bar[18..f[1.][2][[
+ // .bar[18..f[1.][2][[ foo foo
+ // [18..f[1.][2][[ .bar bar
+ // ..f[1.][2][[ [18 error no ]
+ // .f[1.][2][[ . error 0-length
+ // [1.][2][[ .f f
+ // .][2][[ [1 error no ]
+ // [2][[ .] ] (we don't error check legal CLI identifier name characters)
+ // [[ [2] 2
+ // [ [ error no ]
+ // [ error no ]
+
+ // not an error, just the end of the expression
+ if(*expression == NULL || **expression == 0)
+ return S_FALSE;
+
+ WCHAR* expressionStart = *expression;
+ DWORD currentCharsParsed = *charactersParsed;
+ DWORD identifierLen = (DWORD) _wcscspn(expressionStart, L".[");
+ // if the first character was a . or [ skip over it. Note that we don't
+ // do this always in case the first WCHAR was part of a surrogate pair
+ if(identifierLen == 0)
+ {
+ identifierLen = (DWORD) _wcscspn(expressionStart+1, L".[") + 1;
+ }
+
+ *expression += identifierLen;
+ *charactersParsed += identifierLen;
+
+ // done with the first stage splitting, on to 2nd stage
+
+ // a . should be followed by field name
+ if(*expressionStart == L'.')
+ {
+ if(identifierLen == 1) // 0-length after .
+ {
+ swprintf_s(errorMessage, cchErrorMessage, L"Failed to parse expression, missing name after character %d", currentCharsParsed+1);
+ return E_FAIL;
+ }
+ if(identifierLen-1 >= cchIdentifierName)
+ {
+ swprintf_s(errorMessage, cchErrorMessage, L"Failed to parse expression, name at character %d is too long", currentCharsParsed+2);
+ return E_FAIL;
+ }
+ *isArrayIndex = FALSE;
+ wcsncpy_s(identifierName, cchIdentifierName, expressionStart+1, identifierLen-1);
+ return S_OK;
+ }
+ // an open bracket should be followed by a decimal value and then a closing bracket
+ else if(*expressionStart == L'[')
+ {
+ if(*(expressionStart+identifierLen-1) != L']')
+ {
+ swprintf_s(errorMessage, cchErrorMessage, L"Failed to parse expression, missing or invalid index expression at character %d", currentCharsParsed+1);
+ return E_FAIL;
+ }
+ if(identifierLen <= 2) // 0-length between []
+ {
+ swprintf_s(errorMessage, cchErrorMessage, L"Failed to parse expression, missing index after character %d", currentCharsParsed+1);
+ return E_FAIL;
+ }
+ if(identifierLen-2 >= cchIdentifierName)
+ {
+ swprintf_s(errorMessage, cchErrorMessage, L"Failed to parse expression, index at character %d is too large", currentCharsParsed+2);
+ return E_FAIL;
+ }
+ *isArrayIndex = TRUE;
+ wcsncpy_s(identifierName, cchIdentifierName, expressionStart+1, identifierLen-2);
+ return S_OK;
+ }
+ else // no '.' or '[', this is an initial name
+ {
+ if(identifierLen == 0) // 0-length
+ {
+ swprintf_s(errorMessage, cchErrorMessage, L"Failed to parse expression, missing name after character %d", currentCharsParsed+1);
+ return E_FAIL;
+ }
+ if(identifierLen >= cchIdentifierName)
+ {
+ swprintf_s(errorMessage, cchErrorMessage, L"Failed to parse expression, name at character %d is too long", currentCharsParsed+1);
+ return E_FAIL;
+ }
+ *isArrayIndex = FALSE;
+ wcsncpy_s(identifierName, cchIdentifierName, expressionStart, identifierLen);
+ return S_OK;
+ }
+}
+
+
+// Iterate through all parameters in the ILFrame calling the callback function for each of them
+HRESULT ExpressionNode::EnumerateParameters(IMetaDataImport * pMD,
+ mdMethodDef methodDef,
+ ICorDebugILFrame * pILFrame,
+ VariableEnumCallback pCallback,
+ VOID* pUserData)
+{
+ HRESULT Status = S_OK;
+
+ ULONG cParams = 0;
+ ToRelease<ICorDebugValueEnum> pParamEnum;
+ IfFailRet(pILFrame->EnumerateArguments(&pParamEnum));
+ IfFailRet(pParamEnum->GetCount(&cParams));
+ DWORD methAttr = 0;
+ IfFailRet(pMD->GetMethodProps(methodDef, NULL, NULL, 0, NULL, &methAttr, NULL, NULL, NULL, NULL));
+ for (ULONG i=0; i < cParams; i++)
+ {
+ ULONG paramNameLen = 0;
+ mdParamDef paramDef;
+ WCHAR paramName[mdNameLen] = L"\0";
+
+ if(i == 0 && (methAttr & mdStatic) == 0)
+ swprintf_s(paramName, mdNameLen, L"this\0");
+ else
+ {
+ int idx = ((methAttr & mdStatic) == 0)? i : (i + 1);
+ if(SUCCEEDED(pMD->GetParamForMethodIndex(methodDef, idx, &paramDef)))
+ pMD->GetParamProps(paramDef, NULL, NULL, paramName, mdNameLen, &paramNameLen, NULL, NULL, NULL, NULL);
+ }
+ if(_wcslen(paramName) == 0)
+ swprintf_s(paramName, mdNameLen, L"param_%d\0", i);
+
+ ToRelease<ICorDebugValue> pValue;
+ ULONG cArgsFetched;
+ WCHAR pErrorMessage[MAX_ERROR] = L"\0";
+ HRESULT hr = pParamEnum->Next(1, &pValue, &cArgsFetched);
+ if (FAILED(hr))
+ {
+ swprintf_s(pErrorMessage, MAX_ERROR, L" + (Error 0x%x retrieving parameter '%S')\n", hr, paramName);
+ }
+ if (hr == S_FALSE)
+ {
+ break;
+ }
+ pCallback(pValue, paramName, pErrorMessage, pUserData);
+ }
+
+ return Status;
+}
+
+// Enumerate all locals in the given ILFrame, calling the callback method for each of them
+HRESULT ExpressionNode::EnumerateLocals(IMetaDataImport * pMD,
+ mdMethodDef methodDef,
+ ICorDebugILFrame * pILFrame,
+ VariableEnumCallback pCallback,
+ VOID* pUserData)
+{
+ HRESULT Status = S_OK;
+ ULONG cLocals = 0;
+ ToRelease<ICorDebugFunction> pFunction;
+ ToRelease<ICorDebugModule> pModule;
+ if(SUCCEEDED(pILFrame->GetFunction(&pFunction)))
+ {
+ IfFailRet(pFunction->GetModule(&pModule));
+ }
+ ToRelease<ICorDebugValueEnum> pLocalsEnum;
+ IfFailRet(pILFrame->EnumerateLocalVariables(&pLocalsEnum));
+ IfFailRet(pLocalsEnum->GetCount(&cLocals));
+ if (cLocals > 0)
+ {
+ SymbolReader symReader;
+ bool symbolsAvailable = false;
+ if(pModule != NULL && SUCCEEDED(symReader.LoadSymbols(pMD, pModule)))
+ symbolsAvailable = true;
+
+ for (ULONG i=0; i < cLocals; i++)
+ {
+ ULONG paramNameLen = 0;
+ WCHAR paramName[mdNameLen] = L"\0";
+ WCHAR pErrorMessage[MAX_ERROR] = L"\0";
+ ToRelease<ICorDebugValue> pValue;
+ HRESULT hr = S_OK;
+ if(symbolsAvailable)
+ hr = symReader.GetNamedLocalVariable(pILFrame, i, paramName, mdNameLen, &pValue);
+ else
+ {
+ ULONG cArgsFetched;
+ hr = pLocalsEnum->Next(1, &pValue, &cArgsFetched);
+ }
+ if(_wcslen(paramName) == 0)
+ swprintf_s(paramName, mdNameLen, L"local_%d\0", i);
+
+ if (FAILED(hr))
+ {
+ swprintf_s(pErrorMessage, MAX_ERROR, L" + (Error 0x%x retrieving local variable '%S')\n", hr, paramName);
+ }
+ else if (hr == S_FALSE)
+ {
+ break;
+ }
+ pCallback(pValue, paramName, pErrorMessage, pUserData);
+ }
+ }
+
+ return Status;
+}
+
+// Iterates over all frames on the current thread's stack, calling the callback function for each of them
+HRESULT ExpressionNode::EnumerateFrames(FrameEnumCallback pCallback, VOID* pUserData)
+{
+ HRESULT Status = S_OK;
+ ToRelease<ICorDebugThread> pThread;
+ ToRelease<ICorDebugThread3> pThread3;
+ ToRelease<ICorDebugStackWalk> pStackWalk;
+ ULONG ulThreadID = 0;
+ g_ExtSystem->GetCurrentThreadSystemId(&ulThreadID);
+
+ IfFailRet(g_pCorDebugProcess->GetThread(ulThreadID, &pThread));
+ IfFailRet(pThread->QueryInterface(IID_ICorDebugThread3, (LPVOID *) &pThread3));
+ IfFailRet(pThread3->CreateStackWalk(&pStackWalk));
+
+ InternalFrameManager internalFrameManager;
+ IfFailRet(internalFrameManager.Init(pThread3));
+
+ int currentFrame = -1;
+
+ for (Status = S_OK; ; Status = pStackWalk->Next())
+ {
+ currentFrame++;
+
+ if (Status == CORDBG_S_AT_END_OF_STACK)
+ {
+ break;
+ }
+ IfFailRet(Status);
+
+ if (IsInterrupt())
+ {
+ ExtOut("<interrupted>\n");
+ break;
+ }
+
+ CROSS_PLATFORM_CONTEXT context;
+ ULONG32 cbContextActual;
+ if ((Status=pStackWalk->GetContext(
+ DT_CONTEXT_FULL,
+ sizeof(context),
+ &cbContextActual,
+ (BYTE *)&context))!=S_OK)
+ {
+ ExtOut("GetFrameContext failed: %lx\n",Status);
+ break;
+ }
+
+ ToRelease<ICorDebugFrame> pFrame;
+ IfFailRet(pStackWalk->GetFrame(&pFrame));
+ if (Status == S_FALSE)
+ {
+ Status = S_OK;
+ continue;
+ }
+
+ pCallback(pFrame, pUserData);
+ }
+
+ return Status;
+}
+
+// Determines the corresponding ICorDebugType for a given primitive type
+HRESULT ExpressionNode::FindTypeFromElementType(CorElementType et, ICorDebugType** ppType)
+{
+ HRESULT Status;
+ switch (et)
+ {
+ default:
+ Status = E_FAIL;
+ break;
+
+ case ELEMENT_TYPE_BOOLEAN:
+ Status = FindTypeByName(L"System.Boolean", ppType);
+ break;
+
+ case ELEMENT_TYPE_CHAR:
+ Status = FindTypeByName(L"System.Char", ppType);
+ break;
+
+ case ELEMENT_TYPE_I1:
+ Status = FindTypeByName(L"System.SByte", ppType);
+ break;
+
+ case ELEMENT_TYPE_U1:
+ Status = FindTypeByName(L"System.Byte", ppType);
+ break;
+
+ case ELEMENT_TYPE_I2:
+ Status = FindTypeByName(L"System.Short", ppType);
+ break;
+
+ case ELEMENT_TYPE_U2:
+ Status = FindTypeByName(L"System.UShort", ppType);
+ break;
+
+ case ELEMENT_TYPE_I:
+ Status = FindTypeByName(L"System.Int32", ppType);
+ break;
+
+ case ELEMENT_TYPE_U:
+ Status = FindTypeByName(L"System.UInt32", ppType);
+ break;
+
+ case ELEMENT_TYPE_I4:
+ Status = FindTypeByName(L"System.Int32", ppType);
+ break;
+
+ case ELEMENT_TYPE_U4:
+ Status = FindTypeByName(L"System.UInt32", ppType);
+ break;
+
+ case ELEMENT_TYPE_I8:
+ Status = FindTypeByName(L"System.Int64", ppType);
+ break;
+
+ case ELEMENT_TYPE_U8:
+ Status = FindTypeByName(L"System.UInt64", ppType);
+ break;
+
+ case ELEMENT_TYPE_R4:
+ Status = FindTypeByName(L"System.Single", ppType);
+ break;
+
+ case ELEMENT_TYPE_R8:
+ Status = FindTypeByName(L"System.Double", ppType);
+ break;
+
+ case ELEMENT_TYPE_OBJECT:
+ Status = FindTypeByName(L"System.Object", ppType);
+ break;
+
+ case ELEMENT_TYPE_STRING:
+ Status = FindTypeByName(L"System.String", ppType);
+ break;
+ }
+ return Status;
+}
+
+// Gets the appropriate element type encoding for well-known fully qualified type names
+// This doesn't work for arbitrary types, just types that have CorElementType short forms.
+HRESULT ExpressionNode::GetCanonicalElementTypeForTypeName(__in_z WCHAR* pTypeName, CorElementType *et)
+{
+ //Sadly ICorDebug deliberately prevents creating ICorDebugType instances
+ //that use canonical short form element types... seems like an issue to me.
+
+ if(_wcscmp(pTypeName, L"System.String")==0)
+ {
+ *et = ELEMENT_TYPE_STRING;
+ }
+ else if(_wcscmp(pTypeName, L"System.Object")==0)
+ {
+ *et = ELEMENT_TYPE_OBJECT;
+ }
+ else if(_wcscmp(pTypeName, L"System.Void")==0)
+ {
+ *et = ELEMENT_TYPE_VOID;
+ }
+ else if(_wcscmp(pTypeName, L"System.Boolean")==0)
+ {
+ *et = ELEMENT_TYPE_BOOLEAN;
+ }
+ else if(_wcscmp(pTypeName, L"System.Char")==0)
+ {
+ *et = ELEMENT_TYPE_CHAR;
+ }
+ else if(_wcscmp(pTypeName, L"System.Byte")==0)
+ {
+ *et = ELEMENT_TYPE_U1;
+ }
+ else if(_wcscmp(pTypeName, L"System.Sbyte")==0)
+ {
+ *et = ELEMENT_TYPE_I1;
+ }
+ else if(_wcscmp(pTypeName, L"System.Int16")==0)
+ {
+ *et = ELEMENT_TYPE_I2;
+ }
+ else if(_wcscmp(pTypeName, L"System.UInt16")==0)
+ {
+ *et = ELEMENT_TYPE_U2;
+ }
+ else if(_wcscmp(pTypeName, L"System.UInt32")==0)
+ {
+ *et = ELEMENT_TYPE_U4;
+ }
+ else if(_wcscmp(pTypeName, L"System.Int32")==0)
+ {
+ *et = ELEMENT_TYPE_I4;
+ }
+ else if(_wcscmp(pTypeName, L"System.UInt64")==0)
+ {
+ *et = ELEMENT_TYPE_U8;
+ }
+ else if(_wcscmp(pTypeName, L"System.Int64")==0)
+ {
+ *et = ELEMENT_TYPE_I8;
+ }
+ else if(_wcscmp(pTypeName, L"System.Single")==0)
+ {
+ *et = ELEMENT_TYPE_R4;
+ }
+ else if(_wcscmp(pTypeName, L"System.Double")==0)
+ {
+ *et = ELEMENT_TYPE_R8;
+ }
+ else if(_wcscmp(pTypeName, L"System.IntPtr")==0)
+ {
+ *et = ELEMENT_TYPE_U;
+ }
+ else if(_wcscmp(pTypeName, L"System.UIntPtr")==0)
+ {
+ *et = ELEMENT_TYPE_I;
+ }
+ else if(_wcscmp(pTypeName, L"System.TypedReference")==0)
+ {
+ *et = ELEMENT_TYPE_TYPEDBYREF;
+ }
+ else
+ {
+ return E_FAIL; // can't tell from a name whether it should be valuetype or class
+ }
+ return S_OK;
+}
+
+// Searches the debuggee for any ICorDebugType that matches the given fully qualified name
+// This will search across all AppDomains and Assemblies
+HRESULT ExpressionNode::FindTypeByName(__in_z WCHAR* pTypeName, ICorDebugType** ppType)
+{
+ HRESULT Status = S_OK;
+ ToRelease<ICorDebugAppDomainEnum> pAppDomainEnum;
+ IfFailRet(g_pCorDebugProcess->EnumerateAppDomains(&pAppDomainEnum));
+ DWORD count;
+ IfFailRet(pAppDomainEnum->GetCount(&count));
+ for(DWORD i = 0; i < count; i++)
+ {
+ ToRelease<ICorDebugAppDomain> pAppDomain;
+ DWORD countFetched = 0;
+ IfFailRet(pAppDomainEnum->Next(1, &pAppDomain, &countFetched));
+ Status = FindTypeByName(pAppDomain, pTypeName, ppType);
+ if(SUCCEEDED(Status))
+ break;
+ }
+
+ return Status;
+}
+
+// Searches the debuggee for any ICorDebugType that matches the given fully qualified name
+// This will search across all Assemblies in the given AppDomain
+HRESULT ExpressionNode::FindTypeByName(ICorDebugAppDomain* pAppDomain, __in_z WCHAR* pTypeName, ICorDebugType** ppType)
+{
+ HRESULT Status = S_OK;
+ ToRelease<ICorDebugAssemblyEnum> pAssemblyEnum;
+ IfFailRet(pAppDomain->EnumerateAssemblies(&pAssemblyEnum));
+ DWORD count;
+ IfFailRet(pAssemblyEnum->GetCount(&count));
+ for(DWORD i = 0; i < count; i++)
+ {
+ ToRelease<ICorDebugAssembly> pAssembly;
+ DWORD countFetched = 0;
+ IfFailRet(pAssemblyEnum->Next(1, &pAssembly, &countFetched));
+ Status = FindTypeByName(pAssembly, pTypeName, ppType);
+ if(SUCCEEDED(Status))
+ break;
+ }
+
+ return Status;
+}
+
+// Searches the assembly for any ICorDebugType that matches the given fully qualified name
+HRESULT ExpressionNode::FindTypeByName(ICorDebugAssembly* pAssembly, __in_z WCHAR* pTypeName, ICorDebugType** ppType)
+{
+ HRESULT Status = S_OK;
+ ToRelease<ICorDebugModuleEnum> pModuleEnum;
+ IfFailRet(pAssembly->EnumerateModules(&pModuleEnum));
+ DWORD count;
+ IfFailRet(pModuleEnum->GetCount(&count));
+ for(DWORD i = 0; i < count; i++)
+ {
+ ToRelease<ICorDebugModule> pModule;
+ DWORD countFetched = 0;
+ IfFailRet(pModuleEnum->Next(1, &pModule, &countFetched));
+ Status = FindTypeByName(pModule, pTypeName, ppType);
+ if(SUCCEEDED(Status))
+ break;
+ }
+
+ return Status;
+}
+
+// Searches a given module for any ICorDebugType that matches the given fully qualified type name
+HRESULT ExpressionNode::FindTypeByName(ICorDebugModule* pModule, __in_z WCHAR* pTypeName, ICorDebugType** ppType)
+{
+ HRESULT Status = S_OK;
+ ToRelease<IUnknown> pMDUnknown;
+ ToRelease<IMetaDataImport> pMD;
+ IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
+ IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
+
+ // If the name contains a generic argument list, extract the type name from
+ // before the list
+ WCHAR rootName[mdNameLen];
+ WCHAR* pRootName = NULL;
+ int typeNameLen = (int) _wcslen(pTypeName);
+ int genericParamListStart = (int) _wcscspn(pTypeName, L"<");
+ if(genericParamListStart != typeNameLen)
+ {
+ if(pTypeName[typeNameLen-1] != L'>' || genericParamListStart > mdNameLen)
+ {
+ return E_FAIL; // mal-formed type name
+ }
+ else
+ {
+ wcsncpy_s(rootName, mdNameLen, pTypeName, genericParamListStart);
+ pRootName = rootName;
+ }
+ }
+ else
+ {
+ pRootName = pTypeName;
+ }
+
+ // Convert from name to token to ICorDebugClass
+ mdTypeDef typeDef;
+ IfFailRet(pMD->FindTypeDefByName(pRootName, NULL, &typeDef));
+ DWORD flags;
+ ULONG nameLen;
+ mdToken tkExtends;
+ IfFailRet(pMD->GetTypeDefProps(typeDef, NULL, 0, &nameLen, &flags, &tkExtends));
+ BOOL isValueType;
+ IfFailRet(IsTokenValueTypeOrEnum(tkExtends, pMD, &isValueType));
+ CorElementType et = isValueType ? ELEMENT_TYPE_VALUETYPE : ELEMENT_TYPE_CLASS;
+ ToRelease<ICorDebugClass> pClass;
+ IfFailRet(pModule->GetClassFromToken(typeDef, &pClass));
+ ToRelease<ICorDebugClass2> pClass2;
+ IfFailRet(pClass->QueryInterface(__uuidof(ICorDebugClass2), (void**)&pClass2));
+
+ // Convert from class to type - if generic then recursively resolve the generic
+ // parameter list
+ ArrayHolder<ToRelease<ICorDebugType>> typeParams = NULL;
+ int countTypeParams = 0;
+ if(genericParamListStart != typeNameLen)
+ {
+ ToRelease<ICorDebugAssembly> pAssembly;
+ IfFailRet(pModule->GetAssembly(&pAssembly));
+ ToRelease<ICorDebugAppDomain> pDomain;
+ IfFailRet(pAssembly->GetAppDomain(&pDomain));
+
+ countTypeParams = 1;
+ for(int i = genericParamListStart+1; i < typeNameLen; i++)
+ {
+ if(pTypeName[i] == L',') countTypeParams++;
+ }
+ typeParams = new ToRelease<ICorDebugType>[countTypeParams];
+
+ WCHAR* pCurName = pTypeName + genericParamListStart+1;
+ for(int i = 0; i < countTypeParams; i++)
+ {
+ WCHAR typeParamName[mdNameLen];
+ WCHAR* pNextComma = _wcschr(pCurName, L',');
+ int len = (pNextComma != NULL) ? (int)(pNextComma - pCurName) : (int)_wcslen(pCurName)-1;
+ if(len > mdNameLen)
+ return E_FAIL;
+ wcsncpy_s(typeParamName, mdNameLen, pCurName, len);
+ FindTypeByName(pDomain, typeParamName, &(typeParams[i]));
+ pCurName = pNextComma+1;
+ }
+ }
+ IfFailRet(pClass2->GetParameterizedType(et, countTypeParams, &(typeParams[0]), ppType));
+
+ return Status;
+}
+
+// Checks whether the given token is or refers to type System.ValueType or System.Enum
+HRESULT ExpressionNode::IsTokenValueTypeOrEnum(mdToken token, IMetaDataImport* pMetadata, BOOL* pResult)
+{
+ // This isn't a 100% correct check because we aren't verifying the module portion of the
+ // type identity. Arbitrary assemblies could define a type named System.ValueType or System.Enum.
+ // If that happens this code will get the answer wrong... we just assume that happens so rarely
+ // that it isn't worth doing all the overhead of assembly resolution to deal with
+
+ HRESULT Status = S_OK;
+ CorTokenType type = (CorTokenType)(token & 0xFF000000);
+
+ // only need enough space to hold either System.ValueType or System.Enum
+ //System.ValueType -> 16 characters
+ //System.Enum -> 11 characters
+ WCHAR nameBuffer[17];
+ nameBuffer[0] = L'\0';
+
+ if(type == mdtTypeRef)
+ {
+ ULONG chTypeDef;
+ pMetadata->GetTypeRefProps(token, NULL, NULL, 0, &chTypeDef);
+ if(chTypeDef > _countof(nameBuffer))
+ {
+ *pResult = FALSE;
+ return Status;
+ }
+ IfFailRet(pMetadata->GetTypeRefProps(token, NULL, nameBuffer, _countof(nameBuffer), &chTypeDef));
+ }
+ else if(type == mdtTypeDef)
+ {
+ ULONG chTypeDef;
+ pMetadata->GetTypeDefProps(token, NULL, 0, &chTypeDef, NULL, NULL);
+ if(chTypeDef > _countof(nameBuffer))
+ {
+ *pResult = FALSE;
+ return Status;
+ }
+ IfFailRet(pMetadata->GetTypeDefProps(token, nameBuffer, _countof(nameBuffer), &chTypeDef, NULL, NULL));
+ }
+
+ if(_wcscmp(nameBuffer, L"System.ValueType") == 0 ||
+ _wcscmp(nameBuffer, L"System.Enum") == 0)
+ {
+ *pResult = TRUE;
+ }
+ else
+ {
+ *pResult = FALSE;
+ }
+ return Status;
+}
diff --git a/src/ToolBox/SOS/Strike/ExpressionNode.h b/src/ToolBox/SOS/Strike/ExpressionNode.h
new file mode 100644
index 0000000000..507a8a53d3
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/ExpressionNode.h
@@ -0,0 +1,307 @@
+// 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.
+
+
+#ifndef _EXPRESSION_NODE_
+#define _EXPRESSION_NODE_
+
+#ifdef FEATURE_PAL
+#error This file isn't designed to build in PAL
+#endif
+
+#include "strike.h"
+#include "sos.h"
+#include "util.h"
+
+#define MAX_EXPRESSION 500
+#define MAX_ERROR 500
+
+
+// Represents one node in a tree of expressions and sub-expressions
+// These nodes are used in the !watch expandable expression tree
+// Each node consists of a string based C#-like expression and its
+// evaluation within the current context of the debuggee.
+//
+// These nodes are also intended for eventual use in ClrStack -i expression tree
+// but ClrStack -i hasn't yet been refactored to use them
+//
+// Each node can evaluate to:
+// nothing - if an error occurs during expression parsing or the expression
+// names don't match to anything in the debuggee
+// a debuggee value - these are values that are backed in memory of the debuggee
+// (ICorDebugValue objects) or build time constants which are
+// stored in the assembly metadata.
+// a debuggee type - instead of refering to a particular instance of a type (the
+// value case above), nodes can directly refer to a type definition
+// represented by an ICorDebugType object
+class ExpressionNode
+{
+public:
+
+ typedef VOID (*ExpressionNodeVisitorCallback)(ExpressionNode* pExpressionNode, int depth, VOID* pUserData);
+
+ // Returns the complete expression being evaluated to get the value for this node
+ // The returned pointer is a string interior to this object - once you release
+ // all references to this object the string is invalid.
+ WCHAR* GetAbsoluteExpression();
+
+ // Returns the sub expression that logically indicates how the parent expression
+ // was built upon to reach this node. This relative value has no purpose other
+ // than an identifier and to convey UI meaning to the user. At present typical values
+ // are the name of type, a local, a parameter, a field, an array index, or '<basetype>'
+ // for a baseclass casting operation
+ // The returned pointer is a string interior to this object - once you release
+ // all references to this object the string is invalid.
+ WCHAR* GetRelativeExpression();
+
+ // Returns a text representation of the type of value that this node refers to
+ // It is possible this node doesn't evaluate to anything and therefore has no
+ // type
+ // The returned pointer is a string interior to this object - once you release
+ // all references to this object the string is invalid.
+ WCHAR* GetTypeName();
+
+ // Returns a text representation of the value for this node. It is possible that
+ // this node doesn't evaluate to anything and therefore has no value text.
+ // The returned pointer is a string interior to this object - once you release
+ // all references to this object the string is invalid.
+ WCHAR* GetTextValue();
+
+ // If there is any error during the evaluation of this node's expression, it is
+ // returned here.
+ // The returned pointer is a string interior to this object - once you release
+ // all references to this object the string is invalid.
+ WCHAR* GetErrorMessage();
+
+ // Factory function for creating the expression node at the root of a tree
+ static HRESULT CreateExpressionNode(__in_z WCHAR* pExpression, ExpressionNode** ppExpressionNode);
+
+ // Performs recursive expansion within the tree for nodes that are along the path to varToExpand.
+ // Expansion involves calulating a set of child expressions from the current expression via
+ // field dereferencing, array index dereferencing, or casting to a base type.
+ // For example if a tree was rooted with expression 'foo.bar' and varToExpand is '(Baz)foo.bar[9]'
+ // then 'foo.bar', 'foo.bar[9]', and '(Baz)foo.bar[9]' nodes would all be expanded.
+ HRESULT Expand(__in_z WCHAR* varToExpand);
+
+ // Standard depth first search tree traversal pattern with a callback
+ VOID DFSVisit(ExpressionNodeVisitorCallback pFunc, VOID* pUserData, int depth=0);
+
+private:
+ // for nodes that evaluate to a type, this is that type
+ // for nodes that evaluate to a debuggee value, this is the type of that
+ // value or one of its base types. It represents the type the value should
+ // displayed and expanded as.
+ ToRelease<ICorDebugType> pTypeCast;
+
+ // for nodes that evaluate to a memory backed debuggee value, this is that value
+ ToRelease<ICorDebugValue> pValue;
+
+ // if this node gets expanded and it has thread-static or context-static sub-fields,
+ // this frame disambiguates which thread and context to use.
+ ToRelease<ICorDebugILFrame> pILFrame;
+
+ // TODO: exactly which metadata is this supposed to be? try to get rid of this
+ ToRelease<IMetaDataImport> pMD;
+
+ // PERF: this could be a lot more memory efficient
+ WCHAR pTextValue[MAX_EXPRESSION];
+ WCHAR pErrorMessage[MAX_ERROR];
+ WCHAR pAbsoluteExpression[MAX_EXPRESSION];
+ WCHAR pRelativeExpression[MAX_EXPRESSION];
+ WCHAR pTypeName[MAX_EXPRESSION];
+
+ // if this value represents a build time constant debuggee value, this is a pointer
+ // to the value data stored in metadata and its size
+ UVCP_CONSTANT pDefaultValue;
+ ULONG cchDefaultValue;
+
+ // Pointer in a linked list of sibling nodes that all share the same parent
+ ExpressionNode* pNextSibling;
+ // Pointer to the first child node of this node, other children can be found
+ // by following the child's sibling list.
+ ExpressionNode* pChild;
+
+ typedef VOID (*VariableEnumCallback)(ICorDebugValue* pValue, WCHAR* pName, WCHAR* pErrorMessage, VOID* pUserData);
+ typedef VOID (*FrameEnumCallback)(ICorDebugFrame* pFrame, VOID* pUserData);
+
+ // Indicates how a child node was derived from its parent
+ enum ChildKind
+ {
+ ChildKind_Field,
+ ChildKind_Index,
+ ChildKind_BaseClass
+ };
+
+ // Creates a new expression with a given debuggee value and frame
+ ExpressionNode(__in_z WCHAR* pExpression, ICorDebugValue* pValue, ICorDebugILFrame* pFrame);
+
+ // Creates a new expression that has an error and no value
+ ExpressionNode(__in_z WCHAR* pExpression, __in_z WCHAR* pErrorMessage);
+
+ // Creates a new child expression
+ ExpressionNode(__in_z WCHAR* pParentExpression, ChildKind ck, __in_z WCHAR* pRelativeExpression, ICorDebugValue* pValue, ICorDebugType* pType, ICorDebugILFrame* pFrame, UVCP_CONSTANT pDefaultValue = NULL, ULONG cchDefaultValue = 0);
+
+ // Common member initialization for the constructors
+ VOID Init(ICorDebugValue* pValue, ICorDebugType* pTypeCast, ICorDebugILFrame* pFrame);
+
+ // Retreves the correct IMetaDataImport for the type represented in this node and stores it
+ // in pMD.
+ HRESULT PopulateMetaDataImport();
+
+ // Determines the string representation of pType and stores it in typeName
+ static HRESULT CalculateTypeName(ICorDebugType * pType, __inout_ecount(typeNameLen) WCHAR* typeName, DWORD typeNameLen);
+
+
+ // Appends angle brackets and the generic argument list to a type name
+ static HRESULT AddGenericArgs(ICorDebugType * pType, __inout_ecount(typeNameLen) WCHAR* typeName, DWORD typeNameLen);
+
+ // Determines the text name for the type of this node and caches it
+ HRESULT PopulateType();
+
+ // Node expansion helpers
+
+ // Inserts a new child at the end of the linked list of children
+ // PERF: This has O(N) insert time but these lists should never be large
+ VOID AddChild(ExpressionNode* pNewChild);
+
+ // Helper that determines if the current node is on the path of nodes represented by
+ // expression varToExpand
+ BOOL ShouldExpandVariable(__in_z WCHAR* varToExpand);
+
+ // Expands this array node by creating child nodes with expressions refering to individual array elements
+ HRESULT ExpandSzArray(ICorDebugValue* pInnerValue, __in_z WCHAR* varToExpand);
+
+ // Expands this struct/class node by creating child nodes with expressions refering to individual field values
+ // and one node for the basetype value
+ HRESULT ExpandFields(ICorDebugValue* pInnerValue, __in_z WCHAR* varToExpand);
+
+ // Value Population functions
+
+ //Helper for unwrapping values
+ static HRESULT DereferenceAndUnboxValue(ICorDebugValue * pInputValue, ICorDebugValue** ppOutputValue, BOOL * pIsNull = NULL);
+
+ // Returns TRUE if the value derives from System.Enum
+ static BOOL IsEnum(ICorDebugValue * pInputValue);
+
+ // Calculates the value text for nodes that have enum values
+ HRESULT PopulateEnumValue(ICorDebugValue* pEnumValue, BYTE* enumValue);
+
+ // Helper that fetches the text value of a string ICorDebugValue
+ HRESULT GetDebuggeeStringValue(ICorDebugValue* pInputValue, __inout_ecount(cchBuffer) WCHAR* wszBuffer, DWORD cchBuffer);
+
+ // Helper that fetches the text value of a string build-time literal
+ HRESULT GetConstantStringValue(__inout_ecount(cchBuffer) WCHAR* wszBuffer, DWORD cchBuffer);
+
+ // Helper that caches the textual value for nodes that evaluate to array objects
+ HRESULT PopulateSzArrayValue(ICorDebugValue* pInputValue);
+
+ // Helper that caches the textual value for nodes of any type
+ HRESULT PopulateTextValueHelper();
+
+ // Caches the textual value of this node
+ HRESULT PopulateTextValue();
+
+
+ // Expression parsing and search
+
+ // In/Out parameters for the EvaluateExpressionFrameScanCallback
+ typedef struct _EvaluateExpressionFrameScanData
+ {
+ WCHAR* pIdentifier;
+ ToRelease<ICorDebugValue> pFoundValue;
+ ToRelease<ICorDebugILFrame> pFoundFrame;
+ ToRelease<ICorDebugILFrame> pFirstFrame;
+ WCHAR* pErrorMessage;
+ DWORD cchErrorMessage;
+ } EvaluateExpressionFrameScanData;
+
+ //Callback that searches a frame to determine if it contains a local variable or parameter of a given name
+ static VOID EvaluateExpressionFrameScanCallback(ICorDebugFrame* pFrame, VOID* pUserData);
+
+ //Callback checks to see if a given local/parameter has name pName
+ static VOID EvaluateExpressionVariableScanCallback(ICorDebugValue* pValue, __in_z WCHAR* pName, __out_z WCHAR* pErrorMessage, VOID* pUserData);
+
+ //Factory method that recursively parses pExpression and create an ExpressionNode
+ // pExpression - the entire expression being parsed
+ // pExpressionRemainder - the portion of the expression that remains to be parsed in this
+ // recursive invocation
+ // charactersParsed - the number of characters that have been parsed from pExpression
+ // so far (todo: this is basically the difference between remainder and
+ // full expression, do we need it?)
+ // pParsedValue - A debuggee value that should be used as the context for interpreting
+ // pExpressionRemainder
+ // pParsedType - A debuggee type that should be used as the context for interpreting
+ // pExpressionRemainder.
+ // pParsedDefaultValue - A fixed value from metadata that should be used as context for
+ // interpretting pExpressionRemainder
+ // cchParsedDefaultValue- Size of pParsedDefaultValue
+ // pFrame - A debuggee IL frame that disambiguates the thread and context needed
+ // to evaluate a thread-static or context-static value
+ // ppExpressionNode - OUT - the resulting expression node
+ //
+ //
+ static HRESULT CreateExpressionNodeHelper(__in_z WCHAR* pExpression,
+ __in_z WCHAR* pExpressionParseRemainder,
+ DWORD charactersParsed,
+ ICorDebugValue* pParsedValue,
+ ICorDebugType* pParsedType,
+ UVCP_CONSTANT pParsedDefaultValue,
+ ULONG cchParsedDefaultValue,
+ ICorDebugILFrame* pFrame,
+ ExpressionNode** ppExpressionNode);
+
+ // Splits apart a C#-like expression and determines the first identifier in the string and updates expression to point
+ // at the remaining unparsed portion
+ static HRESULT ParseNextIdentifier(__in_z WCHAR** expression,
+ __inout_ecount(cchIdentifierName) WCHAR* identifierName,
+ DWORD cchIdentifierName,
+ __inout_ecount(cchErrorMessage) WCHAR* errorMessage,
+ DWORD cchErrorMessage,
+ DWORD* charactersParsed,
+ BOOL* isArrayIndex);
+
+
+ // Iterate through all parameters in the ILFrame calling the callback function for each of them
+ static HRESULT EnumerateParameters(IMetaDataImport * pMD,
+ mdMethodDef methodDef,
+ ICorDebugILFrame * pILFrame,
+ VariableEnumCallback pCallback,
+ VOID* pUserData);
+
+ // Enumerate all locals in the given ILFrame, calling the callback method for each of them
+ static HRESULT EnumerateLocals(IMetaDataImport * pMD,
+ mdMethodDef methodDef,
+ ICorDebugILFrame * pILFrame,
+ VariableEnumCallback pCallback,
+ VOID* pUserData);
+
+ // Iterates over all frames on the current thread's stack, calling the callback function for each of them
+ static HRESULT EnumerateFrames(FrameEnumCallback pCallback, VOID* pUserData);
+
+ // Determines the corresponding ICorDebugType for a given primitive type
+ static HRESULT FindTypeFromElementType(CorElementType et, ICorDebugType** ppType);
+
+ // Gets the appropriate element type encoding for well-known fully qualified type names
+ // This doesn't work for arbitrary types, just types that have CorElementType short forms.
+ static HRESULT GetCanonicalElementTypeForTypeName(__in_z WCHAR* pTypeName, CorElementType *et);
+
+ // Searches the debuggee for any ICorDebugType that matches the given fully qualified name
+ // This will search across all AppDomains and Assemblies
+ static HRESULT FindTypeByName(__in_z WCHAR* pTypeName, ICorDebugType** ppType);
+
+ // Searches the debuggee for any ICorDebugType that matches the given fully qualified name
+ // This will search across all Assemblies in the given AppDomain
+ static HRESULT FindTypeByName(ICorDebugAppDomain* pAppDomain, __in_z WCHAR* pTypeName, ICorDebugType** ppType);
+
+ // Searches the assembly for any ICorDebugType that matches the given fully qualified name
+ static HRESULT FindTypeByName(ICorDebugAssembly* pAssembly, __in_z WCHAR* pTypeName, ICorDebugType** ppType);
+
+ // Searches a given module for any ICorDebugType that matches the given fully qualified type name
+ static HRESULT FindTypeByName(ICorDebugModule* pModule, __in_z WCHAR* pTypeName, ICorDebugType** ppType);
+
+ // Checks whether the given token is or refers to type System.ValueType or System.Enum
+ static HRESULT IsTokenValueTypeOrEnum(mdToken token, IMetaDataImport* pMetadata, BOOL* pResult);
+};
+
+#endif
diff --git a/src/ToolBox/SOS/Strike/Native.rc b/src/ToolBox/SOS/Strike/Native.rc
new file mode 100644
index 0000000000..179ddfd24a
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/Native.rc
@@ -0,0 +1,10 @@
+// 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.
+
+#define FX_VER_FILEDESCRIPTION_STR "Microsoft NTSD extension for .NET Runtime\0"
+
+#include <fxver.h>
+#include <fxver.rc>
+
+DOCUMENTATION TEXT DISCARDABLE "sosdocs.txt"
diff --git a/src/ToolBox/SOS/Strike/SOS.nativeproj b/src/ToolBox/SOS/Strike/SOS.nativeproj
new file mode 100644
index 0000000000..4c0fc7616f
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/SOS.nativeproj
@@ -0,0 +1,7 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\xplat\SetHostLocal.props"/>
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\toolbox\sos\strike\sos.targets" />
+ <PropertyGroup>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ </PropertyGroup>
+</Project>
diff --git a/src/ToolBox/SOS/Strike/SOS.sln b/src/ToolBox/SOS/Strike/SOS.sln
new file mode 100644
index 0000000000..08f4a64836
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/SOS.sln
@@ -0,0 +1,76 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SOS", "SOS.vcxproj", "{3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mscordacwks", "..\..\..\mscordacwks.vcproj", "{C5716445-C233-4491-85A4-31B75731DD95}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mscordbi", "..\..\..\mscordbi.vcproj", "{95A6AE03-EC45-4450-93DB-9B21890F79E7}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ amd64chk|Win32 = amd64chk|Win32
+ amd64dbg|Win32 = amd64dbg|Win32
+ amd64ret|Win32 = amd64ret|Win32
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ x86chk|Win32 = x86chk|Win32
+ x86dbg|Win32 = x86dbg|Win32
+ x86ret|Win32 = x86ret|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.amd64chk|Win32.ActiveCfg = amd64chk|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.amd64chk|Win32.Build.0 = amd64chk|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.amd64dbg|Win32.ActiveCfg = amd64dbg|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.amd64dbg|Win32.Build.0 = amd64dbg|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.amd64ret|Win32.ActiveCfg = amd64ret|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.amd64ret|Win32.Build.0 = amd64ret|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.Debug|Win32.ActiveCfg = x86chk|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.Debug|Win32.Build.0 = x86chk|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.Release|Win32.ActiveCfg = x86chk|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.Release|Win32.Build.0 = x86chk|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.x86chk|Win32.ActiveCfg = x86chk|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.x86chk|Win32.Build.0 = x86chk|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.x86dbg|Win32.ActiveCfg = x86dbg|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.x86dbg|Win32.Build.0 = x86dbg|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.x86ret|Win32.ActiveCfg = x86ret|Win32
+ {3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}.x86ret|Win32.Build.0 = x86ret|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.amd64chk|Win32.ActiveCfg = Debug|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.amd64chk|Win32.Build.0 = Debug|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.amd64dbg|Win32.ActiveCfg = Debug|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.amd64dbg|Win32.Build.0 = Debug|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.amd64ret|Win32.ActiveCfg = Debug|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.amd64ret|Win32.Build.0 = Debug|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.Debug|Win32.ActiveCfg = Debug|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.Debug|Win32.Build.0 = Debug|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.Release|Win32.ActiveCfg = Debug|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.Release|Win32.Build.0 = Debug|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.x86chk|Win32.ActiveCfg = x86chk|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.x86chk|Win32.Build.0 = x86chk|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.x86dbg|Win32.ActiveCfg = Debug|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.x86dbg|Win32.Build.0 = Debug|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.x86ret|Win32.ActiveCfg = Debug|Win32
+ {C5716445-C233-4491-85A4-31B75731DD95}.x86ret|Win32.Build.0 = Debug|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.amd64chk|Win32.ActiveCfg = amd64chk|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.amd64chk|Win32.Build.0 = amd64chk|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.amd64dbg|Win32.ActiveCfg = amd64dbg|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.amd64dbg|Win32.Build.0 = amd64dbg|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.amd64ret|Win32.ActiveCfg = amd64ret|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.amd64ret|Win32.Build.0 = amd64ret|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.Debug|Win32.ActiveCfg = x86chk|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.Debug|Win32.Build.0 = x86chk|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.Release|Win32.ActiveCfg = amd64ret|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.Release|Win32.Build.0 = amd64ret|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.x86chk|Win32.ActiveCfg = x86chk|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.x86chk|Win32.Build.0 = x86chk|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.x86dbg|Win32.ActiveCfg = x86dbg|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.x86dbg|Win32.Build.0 = x86dbg|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.x86ret|Win32.ActiveCfg = x86ret|Win32
+ {95A6AE03-EC45-4450-93DB-9B21890F79E7}.x86ret|Win32.Build.0 = x86ret|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/src/ToolBox/SOS/Strike/SOS.vcproj b/src/ToolBox/SOS/Strike/SOS.vcproj
new file mode 100644
index 0000000000..aff5e7cc60
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/SOS.vcproj
@@ -0,0 +1,303 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="SOS"
+ ProjectGUID="{3941DEDB-8183-4F82-9193-5EC0D5B6D4A6}"
+ Keyword="MakeFileProj"
+ TargetFrameworkVersion="131072"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="x86chk|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(ProjectDir)..\..\..\..\bin\runjs buildSOS /quickBuild=$(ProjectPath)/buildArch:x86/buildType:chk"
+ ReBuildCommandLine="$(ProjectDir)..\..\..\..\bin\runjs buildSOS /buildArgs:-c/buildArch:x86/buildType:chk"
+ CleanCommandLine=""
+ Output="$(ProjectDir)\obj2c\i386\SOS.dll"
+ PreprocessorDefinitions="_X86_=1;i386=1"
+ IncludeSearchPath="inc\"
+ ForcedIncludes=""
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ <Configuration
+ Name="x86dbg|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(ProjectDir)..\..\..\..\bin\runjs buildSOS /quickBuild=$(ProjectPath)/buildArch:x86/buildType:dbg"
+ ReBuildCommandLine="$(ProjectDir)..\..\..\..\bin\runjs buildSOS /buildArgs:-c/buildArch:x86/buildType:dbg"
+ CleanCommandLine=""
+ Output="$(ProjectDir)\obj2d\i386\SOS.dll"
+ PreprocessorDefinitions="_X86_=1;i386=1"
+ IncludeSearchPath="inc\"
+ ForcedIncludes=""
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ <Configuration
+ Name="x86ret|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(ProjectDir)..\..\..\..\bin\runjs buildSOS /quickBuild=$(ProjectPath)/buildArch:x86/buildType:ret"
+ ReBuildCommandLine="$(ProjectDir)..\..\..\..\bin\runjs buildSOS /buildArgs:-c/buildArch:x86/buildType:ret"
+ CleanCommandLine=""
+ Output="$(ProjectDir)\obj2r\i386\SOS.dll"
+ PreprocessorDefinitions="_X86_=1;i386=1"
+ IncludeSearchPath="inc\"
+ ForcedIncludes=""
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ <Configuration
+ Name="amd64chk|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(ProjectDir)..\..\..\..\bin\runjs buildSOS /quickBuild=$(ProjectPath)/buildArch:amd64/buildType:chk"
+ ReBuildCommandLine="$(ProjectDir)..\..\..\..\bin\runjs buildSOS /buildArgs:-c/buildArch:amd64/buildType:chk"
+ CleanCommandLine=""
+ Output="$(ProjectDir)\obj2c\amd64\SOS.dll"
+ PreprocessorDefinitions="_AMD64_=1;_WIN64=1;_DEBUG=1"
+ IncludeSearchPath="inc\"
+ ForcedIncludes=""
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ <Configuration
+ Name="amd64dbg|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(ProjectDir)..\..\..\..\bin\runjs buildSOS /quickBuild=$(ProjectPath)/buildArch:amd64/buildType:dbg"
+ ReBuildCommandLine="$(ProjectDir)..\..\..\..\bin\runjs buildSOS /buildArgs:-c/buildArch:amd64/buildType:dbg"
+ CleanCommandLine=""
+ Output="$(ProjectDir)\obj2d\amd64\SOS.dll"
+ PreprocessorDefinitions="_AMD64_=1;_WIN64=1"
+ IncludeSearchPath="inc\"
+ ForcedIncludes=""
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ <Configuration
+ Name="amd64ret|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(ProjectDir)..\..\..\..\bin\runjs buildSOS /quickBuild=$(ProjectPath)/buildArch:amd64/buildType:ret"
+ ReBuildCommandLine="$(ProjectDir)..\..\..\..\bin\runjs buildSOS /buildArgs:-c/buildArch:amd64/buildType:ret"
+ CleanCommandLine=""
+ Output="$(ProjectDir)\obj2r\amd64\SOS.dll"
+ PreprocessorDefinitions="_AMD64_=1;_WIN64=1"
+ IncludeSearchPath="inc\"
+ ForcedIncludes=""
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath=".\data.h"
+ >
+ </File>
+ <File
+ RelativePath=".\inc\dbgeng.h"
+ >
+ </File>
+ <File
+ RelativePath=".\inc\dbghelp.h"
+ >
+ </File>
+ <File
+ RelativePath=".\disasm.h"
+ >
+ </File>
+ <File
+ RelativePath=".\exts.h"
+ >
+ </File>
+ <File
+ RelativePath=".\ntinfo.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sos_md.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sos_stacktrace.h"
+ >
+ </File>
+ <File
+ RelativePath=".\strike.h"
+ >
+ </File>
+ <File
+ RelativePath=".\symbol.h"
+ >
+ </File>
+ <File
+ RelativePath=".\util.h"
+ >
+ </File>
+ <File
+ RelativePath=".\UtilCode.h"
+ >
+ </File>
+ <File
+ RelativePath=".\inc\wdbgexts.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ <File
+ RelativePath=".\Native.rc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\disasm.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\disasmIA64.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\disasmX86.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\dllsext.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\eeheap.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\exts.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\gchist.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\gcroot.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\metadata.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\sildasm.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\sos.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\sos.def"
+ >
+ </File>
+ <File
+ RelativePath=".\sos.h"
+ >
+ </File>
+ <File
+ RelativePath=".\stressLogDump.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\strike.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\util.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\utilIA64.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\utilX86.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\vm.cpp"
+ >
+ </File>
+ </Filter>
+ <File
+ RelativePath=".\sosdocs.txt"
+ >
+ </File>
+ <File
+ RelativePath=".\sources"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/src/ToolBox/SOS/Strike/UtilCode.h b/src/ToolBox/SOS/Strike/UtilCode.h
new file mode 100644
index 0000000000..a002edc89e
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/UtilCode.h
@@ -0,0 +1,11 @@
+// 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.
+
+// ==++==
+//
+
+//
+// ==--==
+// An empty file so that gcdump.cpp does not include the one from other
+// places.
diff --git a/src/ToolBox/SOS/Strike/WatchCmd.cpp b/src/ToolBox/SOS/Strike/WatchCmd.cpp
new file mode 100644
index 0000000000..443f1dd6ef
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/WatchCmd.cpp
@@ -0,0 +1,331 @@
+// 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 "WatchCmd.h"
+
+#ifndef IfFailRet
+#define IfFailRet(EXPR) do { Status = (EXPR); if(FAILED(Status)) { return (Status); } } while (0)
+#endif
+
+_PersistList::~_PersistList()
+{
+ PersistWatchExpression* pCur = pHeadExpr;
+ while(pCur != NULL)
+ {
+ PersistWatchExpression* toDelete = pCur;
+ pCur = pCur->pNext;
+ delete toDelete;
+ }
+}
+
+WatchCmd::WatchCmd() :
+pExpressionListHead(NULL)
+{ }
+WatchCmd::~WatchCmd()
+{
+ Clear();
+ PersistList* pCur = pPersistListHead;
+ while(pCur != NULL)
+ {
+ PersistList* toDelete = pCur;
+ pCur = pCur->pNext;
+ delete toDelete;
+ }
+}
+
+// Deletes all current watch expressions from the watch list
+// (does not delete persisted watch lists though)
+HRESULT WatchCmd::Clear()
+{
+ WatchExpression* pCurrent = pExpressionListHead;
+ while(pCurrent != NULL)
+ {
+ WatchExpression* toDelete = pCurrent;
+ pCurrent = pCurrent->pNext;
+ delete toDelete;
+ }
+ pExpressionListHead = NULL;
+ return S_OK;
+}
+
+// Adds a new expression to the active watch list
+HRESULT WatchCmd::Add(__in_z WCHAR* pExpression)
+{
+ WatchExpression* pExpr = new WatchExpression;
+ if(pExpr == NULL)
+ return E_OUTOFMEMORY;
+ wcsncpy_s(pExpr->pExpression, MAX_EXPRESSION, pExpression, _TRUNCATE);
+ pExpr->pNext = NULL;
+
+ WatchExpression** ppCurrent = &pExpressionListHead;
+ while(*ppCurrent != NULL)
+ ppCurrent = &((*ppCurrent)->pNext);
+ *ppCurrent = pExpr;
+ return S_OK;
+}
+
+// removes an expression at the given index in the active watch list
+HRESULT WatchCmd::Remove(int index)
+{
+ HRESULT Status = S_FALSE;
+ WatchExpression** ppCurrent = &pExpressionListHead;
+ for(int i=1; *ppCurrent != NULL; i++)
+ {
+ if(i == index)
+ {
+ WatchExpression* toDelete = *ppCurrent;
+ *ppCurrent = (*ppCurrent)->pNext;
+ delete toDelete;
+ Status = S_OK;
+ break;
+ }
+ ppCurrent = &((*ppCurrent)->pNext);
+
+ }
+ return Status;
+}
+
+// Evaluates and prints a tree version of the active watch list
+// The tree will be expanded along the nodes in expansionPath
+// Optionally the list is filtered to only show differences from pFilterName (the name of a persisted watch list)
+HRESULT WatchCmd::Print(int expansionIndex, __in_z WCHAR* expansionPath, __in_z WCHAR* pFilterName)
+{
+ HRESULT Status = S_OK;
+ INIT_API_EE();
+ INIT_API_DAC();
+ EnableDMLHolder dmlHolder(TRUE);
+ IfFailRet(InitCorDebugInterface());
+
+ PersistList* pFilterList = NULL;
+ if(pFilterName != NULL)
+ {
+ pFilterList = pPersistListHead;
+ while(pFilterList != NULL)
+ {
+ if(_wcscmp(pFilterList->pName, pFilterName)==0)
+ break;
+ pFilterList = pFilterList->pNext;
+ }
+ }
+
+ PersistWatchExpression* pHeadFilterExpr = (pFilterList != NULL) ? pFilterList->pHeadExpr : NULL;
+
+ WatchExpression* pExpression = pExpressionListHead;
+ int index = 1;
+ while(pExpression != NULL)
+ {
+ ExpressionNode* pResult = NULL;
+ if(FAILED(Status = ExpressionNode::CreateExpressionNode(pExpression->pExpression, &pResult)))
+ {
+ ExtOut(" %d) Error: HRESULT 0x%x while evaluating expression \'%S\'", index, Status, pExpression->pExpression);
+ }
+ else
+ {
+ //check for matching absolute expression
+ PersistWatchExpression* pCurFilterExpr = pHeadFilterExpr;
+ while(pCurFilterExpr != NULL)
+ {
+ if(_wcscmp(pCurFilterExpr->pExpression, pResult->GetAbsoluteExpression())==0)
+ break;
+ pCurFilterExpr = pCurFilterExpr->pNext;
+ }
+
+ // check for matching persist evaluation on the matching expression
+ BOOL print = TRUE;
+ if(pCurFilterExpr != NULL)
+ {
+ WCHAR pCurPersistResult[MAX_EXPRESSION];
+ FormatPersistResult(pCurPersistResult, MAX_EXPRESSION, pResult);
+ if(_wcscmp(pCurPersistResult, pCurFilterExpr->pPersistResult)==0)
+ {
+ print = FALSE;
+ }
+ }
+
+ //expand and print
+ if(print)
+ {
+ if(index == expansionIndex)
+ pResult->Expand(expansionPath);
+ PrintCallbackData data;
+ data.index = index;
+ WCHAR pCommand[MAX_EXPRESSION];
+ swprintf_s(pCommand, MAX_EXPRESSION, L"!watch -expand %d", index);
+ data.pCommand = pCommand;
+ pResult->DFSVisit(EvalPrintCallback, (VOID*)&data);
+ }
+ delete pResult;
+ }
+ pExpression = pExpression->pNext;
+ index++;
+ }
+ return Status;
+}
+
+// Deletes an persisted watch list by name
+HRESULT WatchCmd::RemoveList(__in_z WCHAR* pListName)
+{
+ PersistList** ppList = &pPersistListHead;
+ while(*ppList != NULL)
+ {
+ if(_wcscmp((*ppList)->pName, pListName) == 0)
+ {
+ PersistList* toDelete = *ppList;
+ *ppList = (*ppList)->pNext;
+ delete toDelete;
+ return S_OK;
+ }
+ ppList = &((*ppList)->pNext);
+ }
+ return S_FALSE;
+}
+
+// Renames a previously saved persisted watch list
+HRESULT WatchCmd::RenameList(__in_z WCHAR* pOldName, __in_z WCHAR* pNewName)
+{
+ if(_wcscmp(pOldName, pNewName)==0)
+ return S_OK;
+ PersistList** ppList = &pPersistListHead;
+ while(*ppList != NULL)
+ {
+ if(_wcscmp((*ppList)->pName, pOldName) == 0)
+ {
+ PersistList* pListToChangeName = *ppList;
+ RemoveList(pNewName);
+ wcsncpy_s(pListToChangeName->pName, MAX_EXPRESSION, pNewName, _TRUNCATE);
+ return S_OK;
+ }
+ ppList = &((*ppList)->pNext);
+ }
+ return S_FALSE;
+}
+
+// Saves the active watch list together with the current evaluations as
+// a new persisted watch list
+HRESULT WatchCmd::SaveList(__in_z WCHAR* pSaveName)
+{
+ HRESULT Status = S_OK;
+ INIT_API_EE();
+ INIT_API_DAC();
+ IfFailRet(InitCorDebugInterface());
+
+ RemoveList(pSaveName);
+ PersistList* pList = new PersistList();
+ wcsncpy_s(pList->pName, MAX_EXPRESSION, pSaveName, _TRUNCATE);
+ pList->pHeadExpr = NULL;
+ PersistCallbackData data;
+ data.ppNext = &(pList->pHeadExpr);
+ WatchExpression* pExpression = pExpressionListHead;
+ while(pExpression != NULL)
+ {
+ ExpressionNode* pResult = NULL;
+ if(SUCCEEDED(Status = ExpressionNode::CreateExpressionNode(pExpression->pExpression, &pResult)))
+ {
+ pResult->DFSVisit(PersistCallback, (VOID*)&data);
+ delete pResult;
+ }
+ pExpression = pExpression->pNext;
+ }
+
+ pList->pNext = pPersistListHead;
+ pPersistListHead = pList;
+ return Status;
+}
+
+// Saves the current watch list to file as a sequence of commands that will
+// recreate the list
+HRESULT WatchCmd::SaveListToFile(FILE* pFile)
+{
+ WatchExpression* pExpression = pExpressionListHead;
+ while(pExpression != NULL)
+ {
+ fprintf_s(pFile, "!watch -a %S\n", pExpression->pExpression);
+ pExpression = pExpression->pNext;
+ }
+ return S_OK;
+}
+
+// Escapes characters that would be interpretted as DML markup, namely angle brackets
+// that often appear in generic type names
+VOID WatchCmd::DmlEscape(__in_ecount(cchInput) WCHAR* pInput, int cchInput, __in_ecount(cchOutput) WCHAR* pEscapedOutput, int cchOutput)
+{
+ pEscapedOutput[0] = L'\0';
+ for(int i = 0; i < cchInput; i++)
+ {
+ if(pInput[i] == L'<')
+ {
+ if(0 != wcscat_s(pEscapedOutput, cchOutput, L"&lt;")) return;
+ pEscapedOutput += 4;
+ cchOutput -= 4;
+ }
+ else if(pInput[i] == L'>')
+ {
+ if(0 != wcscat_s(pEscapedOutput, cchOutput, L"&gt;")) return;
+ pEscapedOutput += 4;
+ cchOutput -= 4;
+ }
+ else if(cchOutput > 1)
+ {
+ pEscapedOutput[0] = pInput[i];
+ pEscapedOutput[1] = '\0';
+ pEscapedOutput++;
+ cchOutput--;
+ }
+ if(pInput[i] == L'\0' || cchOutput == 1) break;
+ }
+}
+
+// A DFS traversal callback for the expression node tree that prints it
+VOID WatchCmd::EvalPrintCallback(ExpressionNode* pExpressionNode, int depth, VOID* pUserData)
+{
+ PrintCallbackData* pData = (PrintCallbackData*)pUserData;
+ for(int i = 0; i < depth; i++) ExtOut(" ");
+ if(depth == 0)
+ ExtOut(" %d) ", pData->index);
+ else
+ ExtOut(" |- ");
+ if(pExpressionNode->GetErrorMessage()[0] != 0)
+ {
+ ExtOut("%S (%S)\n", pExpressionNode->GetRelativeExpression(), pExpressionNode->GetErrorMessage());
+ }
+ else
+ {
+ // names can have '<' and '>' in them, need to escape
+ WCHAR pEscapedTypeName[MAX_EXPRESSION];
+ DmlEscape(pExpressionNode->GetTypeName(), (int)_wcslen(pExpressionNode->GetTypeName()), pEscapedTypeName, MAX_EXPRESSION);
+ WCHAR pRelativeExpression[MAX_EXPRESSION];
+ DmlEscape(pExpressionNode->GetRelativeExpression(), (int)_wcslen(pExpressionNode->GetRelativeExpression()), pRelativeExpression, MAX_EXPRESSION);
+ DMLOut("%S <exec cmd=\"%S (%S)%S\">%S</exec> %S\n", pEscapedTypeName, pData->pCommand, pEscapedTypeName, pExpressionNode->GetAbsoluteExpression(), pRelativeExpression, pExpressionNode->GetTextValue());
+ }
+}
+
+// A DFS traversal callback for the expression node tree that saves all the values into a new
+// persisted watch list
+VOID WatchCmd::PersistCallback(ExpressionNode* pExpressionNode, int depth, VOID* pUserData)
+{
+ PersistCallbackData* pData = (PersistCallbackData*)pUserData;
+ if(depth != 0)
+ return;
+
+ PersistWatchExpression* pPersistExpr = new PersistWatchExpression();
+ wcsncpy_s(pPersistExpr->pExpression, MAX_EXPRESSION, pExpressionNode->GetAbsoluteExpression(), _TRUNCATE);
+ FormatPersistResult(pPersistExpr->pPersistResult, MAX_EXPRESSION, pExpressionNode);
+ pPersistExpr->pNext = NULL;
+ *(pData->ppNext) = pPersistExpr;
+ pData->ppNext = &(pPersistExpr->pNext);
+}
+
+// Determines how the value of an expression node is saved as a persisted result. This effectively determines
+// the definition of equality when determining if an expression has changed value
+VOID WatchCmd::FormatPersistResult(__inout_ecount(cchPersistResult) WCHAR* pPersistResult, DWORD cchPersistResult, ExpressionNode* pExpressionNode)
+{
+ if(pExpressionNode->GetErrorMessage()[0] != 0)
+ {
+ _snwprintf_s(pPersistResult, MAX_EXPRESSION, _TRUNCATE, L"%s (%s)\n", pExpressionNode->GetRelativeExpression(), pExpressionNode->GetErrorMessage());
+ }
+ else
+ {
+ _snwprintf_s(pPersistResult, MAX_EXPRESSION, _TRUNCATE, L"%s %s %s\n", pExpressionNode->GetTypeName(), pExpressionNode->GetRelativeExpression(), pExpressionNode->GetTextValue());
+ }
+}
diff --git a/src/ToolBox/SOS/Strike/WatchCmd.h b/src/ToolBox/SOS/Strike/WatchCmd.h
new file mode 100644
index 0000000000..a34e391b79
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/WatchCmd.h
@@ -0,0 +1,110 @@
+// 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.
+
+#ifndef _WATCH_CMD_
+#define _WATCH_CMD_
+
+#ifdef FEATURE_PAL
+#error This file not designed for use with FEATURE_PAL
+#endif
+
+#include "ExpressionNode.h"
+#include "windows.h"
+
+// A linked list node for watch expressions
+typedef struct _WatchExpression
+{
+ WCHAR pExpression[MAX_EXPRESSION];
+ _WatchExpression* pNext;
+
+} WatchExpression;
+
+// A linked list node that stores both the watch expression and a persisted result
+// of the evaluation at some point in the past
+typedef struct _PersistWatchExpression
+{
+ WCHAR pExpression[MAX_EXPRESSION];
+ WCHAR pPersistResult[MAX_EXPRESSION];
+ _PersistWatchExpression* pNext;
+
+} PersistWatchExpression;
+
+// A named list of persisted watch expressions, each of which has an expression and
+// a saved value
+typedef struct _PersistList
+{
+ ~_PersistList();
+ WCHAR pName[MAX_EXPRESSION];
+ PersistWatchExpression* pHeadExpr;
+ _PersistList* pNext;
+} PersistList;
+
+// An API for the functionality in the !watch command
+class WatchCmd
+{
+public:
+ WatchCmd();
+ ~WatchCmd();
+
+ // Deletes all current watch expressions from the watch list
+ // (does not delete persisted watch lists though)
+ HRESULT Clear();
+
+ // Adds a new expression to the active watch list
+ HRESULT Add(__in_z WCHAR* pExpression);
+
+ // removes an expression at the given index in the active watch list
+ HRESULT Remove(int index);
+
+ // Evaluates and prints a tree version of the active watch list
+ // The tree will be expanded along the nodes in expansionPath
+ // Optionally the list is filtered to only show differences from pFilterName (the name of a persisted watch list)
+ HRESULT Print(int expansionIndex, __in_z WCHAR* expansionPath, __in_z WCHAR* pFilterName);
+
+ // Deletes an persisted watch list by name
+ HRESULT RemoveList(__in_z WCHAR* pListName);
+
+ // Renames a previously saved persisted watch list
+ HRESULT RenameList(__in_z WCHAR* pOldName, __in_z WCHAR* pNewName);
+
+ // Saves the active watch list together with the current evaluations as
+ // a new persisted watch list
+ HRESULT SaveList(__in_z WCHAR* pSaveName);
+
+ // Saves the current watch list to file as a sequence of commands that will
+ // recreate the list
+ HRESULT SaveListToFile(FILE* pFile);
+
+private:
+ WatchExpression* pExpressionListHead;
+ PersistList* pPersistListHead;
+
+ // Escapes characters that would be interpretted as DML markup, namely angle brackets
+ // that often appear in generic type names
+ static VOID DmlEscape(__in_z WCHAR* pInput, int cchInput, __inout_ecount(cchOutput) WCHAR* pEscapedOutput, int cchOutput);
+
+ typedef struct _PrintCallbackData
+ {
+ int index;
+ WCHAR* pCommand;
+ } PrintCallbackData;
+
+ // A DFS traversal callback for the expression node tree that prints it
+ static VOID EvalPrintCallback(ExpressionNode* pExpressionNode, int depth, VOID* pUserData);
+
+ typedef struct _PersistCallbackData
+ {
+ PersistWatchExpression** ppNext;
+ } PersistCallbackData;
+
+ // A DFS traversal callback for the expression node tree that saves all the values into a new
+ // persisted watch list
+ static VOID PersistCallback(ExpressionNode* pExpressionNode, int depth, VOID* pUserData);
+
+ // Determines how the value of an expression node is saved as a persisted result. This effectively determines
+ // the definition of equality when determining if an expression has changed value
+ static VOID FormatPersistResult(__inout_ecount(cchPersistResult) WCHAR* pPersistResult, DWORD cchPersistResult, ExpressionNode* pExpressionNode);
+};
+
+#endif
diff --git a/src/ToolBox/SOS/Strike/apollososdocs.txt b/src/ToolBox/SOS/Strike/apollososdocs.txt
new file mode 100644
index 0000000000..71fefcf4d7
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/apollososdocs.txt
@@ -0,0 +1,2727 @@
+-------------------------------------------------------------------------------
+NOTE: THIS FILE CONTAINS SOS DOCUMENTATION. THE FORMAT OF THE FILE IS:
+
+<optional comments>
+COMMAND: <cmd name, all lower case>
+<descriptive text of the command>
+\\ <these are two backslashes, immediately followed by a newline>
+
+<repeat the sequence above>
+
+The first command is "contents" which is the general help screen. The rest
+correspond to SOS command names. This file is embedded as a resource in the SOS
+binary. Be sure to list any new commands here.
+-------------------------------------------------------------------------------
+
+
+
+COMMAND: contents.
+SOS is a debugger extension DLL designed to aid in the debugging of managed
+programs. Functions are listed by category, then roughly in order of
+importance. Shortcut names for popular functions are listed in parenthesis.
+Type "!help <functionname>" for detailed info on that function.
+
+Object Inspection Examining code and stacks
+----------------------------- -----------------------------
+DumpObj (do) Threads
+DumpArray (da) ThreadState
+DumpStackObjects (dso) IP2MD
+DumpHeap U
+DumpVC DumpStack
+GCRoot EEStack
+ObjSize CLRStack
+FinalizeQueue GCInfo
+PrintException (pe) EHInfo
+TraverseHeap BPMD
+Watch COMState
+ StopOnCatch
+ SuppressJitOptimization
+
+Examining CLR data structures Diagnostic Utilities
+----------------------------- -----------------------------
+DumpDomain VerifyHeap
+EEHeap VerifyObj
+Name2EE FindRoots
+SyncBlk HeapStat
+DumpMT GCWhere
+DumpClass ListNearObj (lno)
+DumpMD GCHandles
+Token2EE GCHandleLeaks
+EEVersion FinalizeQueue (fq)
+DumpModule FindAppDomain
+ThreadPool SaveModule
+DumpAssembly ProcInfo
+DumpSigElem StopOnException (soe)
+DumpRuntimeTypes DumpLog
+DumpSig VMMap
+RCWCleanupList VMStat
+DumpIL MinidumpMode
+DumpRCW AnalyzeOOM (ao)
+DumpCCW
+
+Examining the GC history Other
+----------------------------- -----------------------------
+HistInit FAQ
+HistRoot SaveState
+HistObj
+HistObjFind
+HistClear
+\\
+
+COMMAND: faq.
+>> Where can I get the right version of SOS for my build?
+
+If you are running version 1.1 or 2.0 of the CLR, SOS.DLL is installed in the
+same directory as the main CLR dll (CLR.DLL). Newer versions of the
+Windows Debugger provide a command to make it easy to load the right copy of
+SOS.DLL:
+
+ ".loadby sos clr"
+
+That will load the SOS extension DLL from the same place that CLR.DLL is
+loaded in the process. You shouldn't attempt to use a version of SOS.DLL that
+doesn't match the version of CLR.DLL. You can find the version of
+CLR.DLL by running
+
+ "lmvm clr"
+
+in the debugger. Note that if you are running CoreCLR (e.g. Silverlight)
+then you should replace "clr" with "coreclr".
+
+If you are using a dump file created on another machine, it is a little bit
+more complex. You need to make sure the mscordacwks.dll file that came with
+that install is on your symbol path, and you need to load the corresponding
+version of sos.dll (typing .load <full path to sos.dll> rather than using the
+.loadby shortcut). Within the Microsoft corpnet, we keep tagged versions
+of mscordacwks.dll, with names like mscordacwks_<architecture>_<version>.dll
+that the Windows Debugger can load. If you have the correct symbol path to the
+binaries for that version of the Runtime, the Windows Debugger will load the
+correct mscordacwks.dll file.
+
+>> I have a chicken and egg problem. I want to use SOS commands, but the CLR
+ isn't loaded yet. What can I do?
+
+In the debugger at startup you can type:
+
+ "sxe clrn"
+
+Let the program run, and it will stop with the notice
+
+ "CLR notification: module 'mscorlib' loaded"
+
+At this time you can use SOS commands. To turn off spurious notifications,
+type:
+
+ "sxd clrn"
+
+>> I got the following error message. Now what?
+
+ 0:000> .loadby sos clr
+ 0:000> !DumpStackObjects
+ Failed to find runtime DLL (clr.dll), 0x80004005
+ Extension commands need clr.dll in order to have something to do.
+ 0:000>
+
+This means that the CLR is not loaded yet, or has been unloaded. You need to
+wait until your managed program is running in order to use these commands. If
+you have just started the program a good way to do this is to type
+
+ bp clr!EEStartup "g @$ra"
+
+in the debugger, and let it run. After the function EEStartup is finished,
+there will be a minimal managed environment for executing SOS commands.
+
+>> I have a partial memory minidump, and !DumpObj doesn't work. Why?
+
+In order to run SOS commands, many CLR data structures need to be traversed.
+When creating a minidump without full memory, special functions are called at
+dump creation time to bring those structures into the minidump, and allow a
+minimum set of SOS debugging commands to work. At this time, those commands
+that can provide full or partial output are:
+
+CLRStack
+Threads
+Help
+PrintException
+EEVersion
+
+For a minidump created with this minimal set of functionality in mind, you
+will get an error message when running any other commands. A full memory dump
+(obtained with ".dump /ma <filename>" in the Windows Debugger) is often the
+best way to debug a managed program at this level.
+
+>> What other tools can I use to find my bug?
+
+Turn on Managed Debugging Assistants. These enable additional runtime diagnostics,
+particularly in the area of PInvoke/Interop. Adam Nathan has written some great
+information about that:
+
+http://blogs.msdn.com/adam_nathan/
+
+>> Does SOS support DML?
+
+Yes. SOS respects the .prefer_dml option in the debugger. If this setting is
+turned on, then SOS will output DML by default. Alternatively, you may leave
+it off and add /D to the beginning of a command to get DML based output for it.
+Not all SOS commands support DML output.
+
+\\
+
+COMMAND: stoponexception.
+!StopOnException [-derived]
+ [-create | -create2]
+ <Exception>
+ [<Pseudo-register number>]
+
+!StopOnException helps when you want the Windows Debugger to stop on a
+particular managed exception, say a System.OutOfMemoryException, but continue
+running if other exceptions are thrown. The command can be used in two ways:
+
+1) When you just want to stop on one particular CLR exception
+
+ At the debugger prompt, anytime after loading SOS, type:
+
+ !StopOnException -create System.OutOfMemoryException 1
+
+ The pseudo-register number (1) indicates that SOS can use register $t1 for
+ maintaining the breakpoint. The -create parameter allows SOS to go ahead
+ and set up the breakpoint as a first-chance exception. -create2 would set
+ it up as a 2nd-chance exception.
+
+2) When you need more complex logic for stopping on a CLR exception
+
+ !StopOnException can be used purely as a predicate in a larger expression.
+ If you type:
+
+ !StopOnException System.OutOfMemoryException 3
+
+ then register $t3 will be set to 1 if the last thrown exception on the
+ current thread is a System.OutOfMemoryException. Otherwise, $t3 will be set
+ to 0. Using the Windows Debugger scripting language, you could chain
+ such calls together to stop on various exception types. You'll have to
+ manually create such predicates, for example:
+
+ sxe -c "!soe System.OutOfMemoryException 3;
+ !soe -derived System.IOException 4;
+ .if(@$t3==1 || @$t4==1) { .echo 'stop' } .else {g}"
+
+The -derived option will cause StopOnException to set the pseudo-register to
+1 even if the thrown exception type doesn't exactly match the exception type
+given, but merely derives from it. So, "-derived System.Exception" would catch
+every exception in the System.Exception heirarchy.
+
+The pseudo-register number is optional. If you don't pass a number, SOS will
+use pseudo-register $t1.
+
+Note that !PrintException with no parameters will print out the last thrown
+exception on the current thread (if any). You can use !soe as a shortcut for
+!StopOnException.
+\\
+
+COMMAND: minidumpmode.
+!MinidumpMode <0 or 1>
+
+Minidumps created with ".dump /m" or ".dump" have a very small set of
+CLR-specific data, just enough to run a subset of SOS commands correctly. You
+are able to run other SOS commands, but they may fail with unexpected errors
+because required areas of memory are not mapped in or only partially mapped
+in. At this time, SOS cannot reliably detect if a dump file is of this type
+(for one thing, custom dump commands can map in additional memory, but there
+is no facility to read meta-information about this memory). You can turn this
+option on to protect against running unsafe commands against small minidumps.
+
+By default, MinidumpMode is 0, so there is no restriction on commands that will
+run against a minidump.
+\\
+
+COMMAND: dumpobj.
+!DumpObj [-nofields] <object address>
+
+This command allows you to examine the fields of an object, as well as learn
+important properties of the object such as the EEClass, the MethodTable, and
+the size.
+
+You might find an object pointer by running !DumpStackObjects and choosing
+from the resultant list. Here is a simple object:
+
+ 0:000> !DumpObj a79d40
+ Name: Customer
+ MethodTable: 009038ec
+ EEClass: 03ee1b84
+ Size: 20(0x14) bytes
+ (C:\pub\unittest.exe)
+ Fields:
+ MT Field Offset Type VT Attr Value Name
+ 009038ec 4000008 4 Customer 0 instance 00a79ce4 name
+ 009038ec 4000009 8 Bank 0 instance 00a79d2c bank
+
+Note that fields of type Customer and Bank are themselves objects, and you can
+run !DumpObj on them too. You could look at the field directly in memory using
+the offset given. "dd a79d40+8 l1" would allow you to look at the bank field
+directly. Be careful about using this to set memory breakpoints, since objects
+can move around in the garbage collected heap.
+
+What else can you do with an object? You might run !GCRoot, to determine what
+roots are keeping it alive. Or you can find all objects of that type with
+"!DumpHeap -type Customer".
+
+The column VT contains the value 1 if the field is a valuetype structure, and
+0 if the field contains a pointer to another object. For valuetypes, you can
+take the MethodTable pointer in the MT column, and the Value and pass them to
+the command !DumpVC.
+
+The abbreviation !do can be used for brevity.
+
+The arguments in detail:
+-nofields: do not print fields of the object, useful for objects like
+ String
+\\
+
+COMMAND: dumparray.
+!DumpArray
+ [-start <startIndex>]
+ [-length <length>]
+ [-details]
+ [-nofields]
+ <array object address>
+
+This command allows you to examine elements of an array object.
+The arguments in detail:
+ -start <startIndex>: optional, only supported for single dimension array.
+ Specify from which index the command shows the elements.
+ -length <length>: optional, only supported for single dimension array.
+ Specify how many elements to show.
+ -details: optional. Ask the command to print out details
+ of the element using !DumpObj and !DumpVC format.
+ -nofields: optional, only takes effect when -details is used. Do
+ not print fields of the elements. Useful for arrays of
+ objects like String
+
+ Example output:
+
+ 0:000> !dumparray -start 2 -length 3 -details 00ad28d0
+ Name: Value[]
+ MethodTable: 03e41044
+ EEClass: 03e40fc0
+ Size: 132(0x84) bytes
+ Array: Rank 1, Number of elements 10, Type VALUETYPE
+ Element Type: Value
+ [2] 00ad28f0
+ Name: Value
+ MethodTable 03e40f4c
+ EEClass: 03ef1698
+ Size: 20(0x14) bytes
+ (C:\bugs\225271\arraytest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 5b9a628c 4000001 0 System.Int32 instance 2 x
+ 5b9a628c 4000002 4 System.Int32 instance 4 y
+ 5b9a628c 4000003 8 System.Int32 instance 6 z
+ [3] 00ad28fc
+ Name: Value
+ MethodTable 03e40f4c
+ EEClass: 03ef1698
+ Size: 20(0x14) bytes
+ (C:\bugs\225271\arraytest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 5b9a628c 4000001 0 System.Int32 instance 3 x
+ 5b9a628c 4000002 4 System.Int32 instance 6 y
+ 5b9a628c 4000003 8 System.Int32 instance 9 z
+ [4] 00ad2908
+ Name: Value
+ MethodTable 03e40f4c
+ EEClass: 03ef1698
+ Size: 20(0x14) bytes
+ (C:\bugs\225271\arraytest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 5b9a628c 4000001 0 System.Int32 instance 4 x
+ 5b9a628c 4000002 4 System.Int32 instance 8 y
+ 5b9a628c 4000003 8 System.Int32 instance 12 z
+
+
+\\
+
+COMMAND: dumpstackobjects.
+!DumpStackObjects [-verify] [top stack [bottom stack]]
+
+This command will display any managed objects it finds within the bounds of
+the current stack. Combined with the stack tracing commands like K and
+!CLRStack, it is a good aid to determining the values of locals and
+parameters.
+
+If you use the -verify option, each non-static CLASS field of an object
+candidate is validated. This helps to eliminate false positives. It is not
+on by default because very often in a debugging scenario, you are
+interested in objects with invalid fields.
+
+The abbreviation !dso can be used for brevity.
+\\
+
+COMMAND: dumpheap.
+!DumpHeap [-stat]
+ [-strings]
+ [-short]
+ [-min <size>]
+ [-max <size>]
+ [-thinlock]
+ [-startAtLowerBound]
+ [-mt <MethodTable address>]
+ [-type <partial type name>]
+ [start [end]]
+
+!DumpHeap is a powerful command that traverses the garbage collected heap,
+collection statistics about objects. With it's various options, it can look for
+particular types, restrict to a range, or look for ThinLocks (see !SyncBlk
+documentation). Finally, it will provide a warning if it detects excessive
+fragmentation in the GC heap.
+
+When called without options, the output is first a list of objects in the heap,
+followed by a report listing all the types found, their size and number:
+
+ 0:000> !dumpheap
+ Address MT Size
+ 00a71000 0015cde8 12 Free
+ 00a7100c 0015cde8 12 Free
+ 00a71018 0015cde8 12 Free
+ 00a71024 5ba58328 68
+ 00a71068 5ba58380 68
+ 00a710ac 5ba58430 68
+ 00a710f0 5ba5dba4 68
+ ...
+ total 619 objects
+ Statistics:
+ MT Count TotalSize Class Name
+ 5ba7607c 1 12 System.Security.Permissions.HostProtectionResource
+ 5ba75d54 1 12 System.Security.Permissions.SecurityPermissionFlag
+ 5ba61f18 1 12 System.Collections.CaseInsensitiveComparer
+ ...
+ 0015cde8 6 10260 Free
+ 5ba57bf8 318 18136 System.String
+ ...
+
+"Free" objects are simply regions of space the garbage collector can use later.
+If 30% or more of the heap contains "Free" objects, the process may suffer from
+heap fragmentation. This is usually caused by pinning objects for a long time
+combined with a high rate of allocation. Here is example output where !DumpHeap
+provides a warning about fragmentation:
+
+ <After the Statistics section>
+ Fragmented blocks larger than 1MB:
+ Addr Size Followed by
+ 00a780c0 1.5MB 00bec800 System.Byte[]
+ 00da4e38 1.2MB 00ed2c00 System.Byte[]
+ 00f16df0 1.2MB 01044338 System.Byte[]
+
+The arguments in detail:
+
+-stat Restrict the output to the statistical type summary
+-strings Restrict the output to a statistical string value summary
+-short Limits output to just the address of each object. This allows you
+ to easily pipe output from the command to another debugger
+ command for automation.
+-min Ignore objects less than the size given in bytes
+-max Ignore objects larger than the size given in bytes
+-thinlock Report on any ThinLocks (an efficient locking scheme, see !SyncBlk
+ documentation for more info)
+-startAtLowerBound
+ Force heap walk to begin at lower bound of a supplied address range.
+ (During plan phase, the heap is often not walkable because objects
+ are being moved. In this case, DumpHeap may report spurious errors,
+ in particular bad objects. It may be possible to traverse more of
+ the heap after the reported bad object. Even if you specify an
+ address range, !DumpHeap will start its walk from the beginning of
+ the heap by default. If it finds a bad object before the specified
+ range, it will stop before displaying the part of the heap in which
+ you are interested. This switch will force !DumpHeap to begin its
+ walk at the specified lower bound. You must supply the address of a
+ good object as the lower bound for this to work. Display memory at
+ the address of the bad object to manually find the next method
+ table (use !dumpmt to verify). If the GC is currently in a call to
+ memcopy, You may also be able to find the next object's address by
+ adding the size to the start address given as parameters.)
+-mt List only those objects with the MethodTable given
+-type List only those objects whose type name is a substring match of the
+ string provided.
+start Begin listing from this address
+end Stop listing at this address
+
+A special note about -type: Often, you'd like to find not only Strings, but
+System.Object arrays that are constrained to contain Strings. ("new
+String[100]" actually creates a System.Object array, but it can only hold
+System.String object pointers). You can use -type in a special way to find
+these arrays. Just pass "-type System.String[]" and those Object arrays will
+be returned. More generally, "-type <Substring of interesting type>[]".
+
+The start/end parameters can be obtained from the output of !EEHeap -gc. For
+example, if you only want to list objects in the large heap segment:
+
+ 0:000> !eeheap -gc
+ Number of GC Heaps: 1
+ generation 0 starts at 0x00c32754
+ generation 1 starts at 0x00c32748
+ generation 2 starts at 0x00a71000
+ segment begin allocated size
+ 00a70000 00a71000 010443a8 005d33a8(6108072)
+ Large object heap starts at 0x01a71000
+ segment begin allocated size
+ 01a70000 01a71000 01a75000 0x00004000(16384)
+ Total Size 0x5d73a8(6124456)
+ ------------------------------
+ GC Heap Size 0x5d73a8(6124456)
+
+ 0:000> !dumpheap 1a71000 1a75000
+ Address MT Size
+ 01a71000 5ba88bd8 2064
+ 01a71810 0019fe48 2032 Free
+ 01a72000 5ba88bd8 4096
+ 01a73000 0019fe48 4096 Free
+ 01a74000 5ba88bd8 4096
+ total 5 objects
+ Statistics:
+ MT Count TotalSize Class Name
+ 0019fe48 2 6128 Free
+ 5ba88bd8 3 10256 System.Object[]
+ Total 5 objects
+
+Finally, if GC heap corruption is present, you may see an error like this:
+
+ 0:000> !dumpheap -stat
+ object 00a73d24: does not have valid MT
+ curr_object : 00a73d24
+ Last good object: 00a73d14
+ ----------------
+
+That indicates a serious problem. See the help for !VerifyHeap for more
+information on diagnosing the cause.
+\\
+
+COMMAND: dumpvc.
+!DumpVC <MethodTable address> <Address>
+
+!DumpVC allows you to examine the fields of a value class. In C#, this is a
+struct, and lives on the stack or within an Object on the GC heap. You need
+to know the MethodTable address to tell SOS how to interpret the fields, as
+a value class is not a first-class object with it's own MethodTable as the
+first field. For example:
+
+ 0:000> !DumpObj a79d98
+ Name: Mainy
+ MethodTable: 009032d8
+ EEClass: 03ee1424
+ Size: 28(0x1c) bytes
+ (C:\pub\unittest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 0090320c 4000010 4 VALUETYPE instance 00a79d9c m_valuetype
+ 009032d8 400000f 4 CLASS static 00a79d54 m_sExcep
+
+m_valuetype is a value type. The value in the MT column (0090320c) is the
+MethodTable for it, and the Value column provides the start address:
+
+ 0:000> !DumpVC 0090320c 00a79d9c
+ Name: Funny
+ MethodTable 0090320c
+ EEClass: 03ee14b8
+ Size: 28(0x1c) bytes
+ (C:\pub\unittest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 0090320c 4000001 0 CLASS instance 00a743d8 signature
+ 0090320c 4000002 8 System.Int32 instance 2345 m1
+ 0090320c 4000003 10 System.Boolean instance 1 b1
+ 0090320c 4000004 c System.Int32 instance 1234 m2
+ 0090320c 4000005 4 CLASS instance 00a79d98 backpointer
+
+!DumpVC is quite a specialized function. Some managed programs make heavy use
+of value classes, while others do not.
+\\
+
+COMMAND: gcroot.
+!GCRoot [-nostacks] <Object address>
+
+!GCRoot looks for references (or roots) to an object. These can exist in four
+places:
+
+ 1. On the stack
+ 2. Within a GC Handle
+ 3. In an object ready for finalization
+ 4. As a member of an object found in 1, 2 or 3 above.
+
+First, all stacks will be searched for roots, then handle tables, and finally
+the freachable queue of the finalizer. Some caution about the stack roots:
+!GCRoot doesn't attempt to determine if a stack root it encountered is valid
+or is old (discarded) data. You would have to use !CLRStack and !U to
+disassemble the frame that the local or argument value belongs to in order to
+determine if it is still in use.
+
+Because people often want to restrict the search to gc handles and freachable
+objects, there is a -nostacks option.
+\\
+
+COMMAND: objsize.
+!ObjSize [<Object address>] | [-aggregate] [-stat]
+
+With no parameters, !ObjSize lists the size of all objects found on managed
+threads. It also enumerates all GCHandles in the process, and totals the size
+of any objects pointed to by those handles. In calculating object size,
+!ObjSize includes the size of all child objects in addition to the parent.
+
+For example, !DumpObj lists a size of 20 bytes for this Customer object:
+
+ 0:000> !do a79d40
+ Name: Customer
+ MethodTable: 009038ec
+ EEClass: 03ee1b84
+ Size: 20(0x14) bytes
+ (C:\pub\unittest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 009038ec 4000008 4 CLASS instance 00a79ce4 name
+ 009038ec 4000009 8 CLASS instance 00a79d2c bank
+ 009038ec 400000a c System.Boolean instance 1 valid
+
+but !ObjSize lists 152 bytes:
+
+ 0:000> !ObjSize a79d40
+ sizeof(00a79d40) = 152 ( 0x98) bytes (Customer)
+
+This is because a Customer points to a Bank, has a name, and the Bank points to
+an Address string. You can use !ObjSize to identify any particularly large
+objects, such as a managed cache in a web server.
+
+While running ObjSize with no arguments may point to specific roots that hold
+onto large amounts of memory it does not provide information regarding the
+amount of managed memory that is still alive. This is due to the fact that a
+number of roots can share a common subgraph, and that part will be reported in
+the size of all the roots that reference the subgraph. The -aggregate argument
+is meant to answer this question:
+
+The -aggregate option can be used in conjunction with the -stat argument to get
+a detailed view of what are the types that are still rooted. Using !dumpheap
+-stat and !objsize -aggregate -stat one can determine what are the the objects
+that are not rooted any more and diagnose various memory issues.
+
+Sample output when using the -aggregate and -stat:
+
+ 0:003> !ObjSize -aggregate -stat
+ Scan Thread 0 OSTHread f70
+ Scan Thread 2 OSTHread ef8
+ Statistics:
+ MT Count TotalSize Class Name
+ 01e63768 1 12 Test+d
+ 01e63660 1 16 Test+c
+ 01e63548 1 16 Test+b
+ 01e632f8 1 16 Test+a
+ ...
+ 5b6c6d40 9 504 System.Collections.Hashtable
+ 5b6ebe28 3 552 System.Byte[]
+ 5b6ec0bc 9 1296 System.Collections.Hashtable+bucket[]
+ 5b6ec200 9 1436 System.Char[]
+ 5b6c447c 77 2468 System.String
+ 5b6ebd64 8 9020 System.Object[]
+ Total 203 objects
+ Total Memory Size 18332 (0x479c) bytes
+
+\\
+
+COMMAND: finalizequeue.
+!FinalizeQueue [-detail] | [-allReady] [-short]
+
+This command lists the objects registered for finalization. Here is output from
+a simple program:
+
+ 0:000> !finalizequeue
+ SyncBlocks to be cleaned up: 0
+ MTA Interfaces to be released: 0
+ STA Interfaces to be released: 1
+ generation 0 has 4 finalizable objects (0015bc90->0015bca0)
+ generation 1 has 0 finalizable objects (0015bc90->0015bc90)
+ generation 2 has 0 finalizable objects (0015bc90->0015bc90)
+ Ready for finalization 0 objects (0015bca0->0015bca0)
+ Statistics:
+ MT Count TotalSize Class Name
+ 5ba6cf78 1 24 Microsoft.Win32.SafeHandles.SafeFileHandle
+ 5ba5db04 1 68 System.Threading.Thread
+ 5ba73e28 2 112 System.IO.StreamWriter
+ Total 4 objects
+
+The GC heap is divided into generations, and objects are listed accordingly. We
+see that only generation 0 (the youngest generation) has any objects registered
+for finalization. The notation "(0015bc90->0015bca0)" means that if you look at
+memory in that range, you'll see the object pointers that are registered:
+
+0:000> dd 15bc90 15bca0-4
+0015bc90 00a743f4 00a79f00 00a7b3d8 00a7b47c
+
+You could run !DumpObj on any of those pointers to learn more. In this example,
+there are no objects ready for finalization, presumably because they still have
+roots (You can use !GCRoot to find out). The statistics section provides a
+higher-level summary of the objects registered for finalization. Note that
+objects ready for finalization are also included in the statistics (if any).
+
+Specifying -short will inhibit any display related to SyncBlocks or RCWs.
+
+The arguments in detail:
+
+-allReady Specifying this argument will allow for the display of all objects
+ that are ready for finalization, whether they are already marked by
+ the GC as such, or whether the next GC will. The objects that are
+ not in the "Ready for finalization" list are finalizable objects that
+ are no longer rooted. This option can be very expensive, as it
+ verifies whether all the objects in the finalizable queues are still
+ rooted or not.
+-short Limits the output to just the address of each object. If used in
+ conjunction with -allReady it enumerates all objects that have a
+ finalizer that are no longer rooted. If used independently it lists
+ all objects in the finalizable and "ready for finalization" queues.
+-detail Will display extra information on any SyncBlocks that need to be
+ cleaned up, and on any RuntimeCallableWrappers (RCWs) that await
+ cleanup. Both of these data structures are cached and cleaned up by
+ the finalizer thread when it gets a chance to run.
+\\
+
+COMMAND: printexception.
+!PrintException [-nested] [-lines] [<Exception object address>]
+
+This will format fields of any object derived from System.Exception. One of the
+more useful aspects is that it will format the _stackTrace field, which is a
+binary array. If _stackTraceString field is not filled in, that can be helpful
+for debugging. You can of course use !DumpObj on the same exception object to
+explore more fields.
+
+If called with no parameters, PrintException will look for the last outstanding
+exception on the current thread and print it. This will be the same exception
+that shows up in a run of !Threads.
+
+!PrintException will notify you if there are any nested exceptions on the
+current managed thread. (A nested exception occurs when you throw another
+exception within a catch handler already being called for another exception).
+If there are nested exceptions, you can re-run !PrintException with the
+"-nested" option to get full details on the nested exception objects. The
+!Threads command will also tell you which threads have nested exceptions.
+
+!PrintException can display source information if available, by specifying the
+-lines command line argument.
+
+The abbreviation !pe can be used for brevity.
+\\
+
+COMMAND: traverseheap.
+!TraverseHeap [-xml] [-verify] <filename>
+
+!TraverseHeap writes out a file in a format understood by the CLR Profiler.
+You can download the CLR Profiler from this link:
+
+http://www.microsoft.com/downloads/details.aspx?FamilyId=86CE6052-D7F4-4AEB-
+9B7A-94635BEEBDDA&displaylang=en
+
+It creates a graphical display of the GC heap to help you analyze the state of
+your application.
+
+If you pass the -verify option it will do more sanity checking of the heap
+as it dumps it. Use this option if heap corruption is suspected.
+
+If you pass the "-xml" flag, the file is instead written out in an easy to
+understand xml format:
+
+ <gcheap>
+ <types>
+ <type id="1" name="System.String">
+ ...
+ </types>
+ <roots>
+ <root kind="handle" address="0x00a73ff0"/>
+ <root kind="stack" address="0x0069f0e0"/>
+ ...
+ </roots>
+ <objects>
+ <object address="0x00b73030" typeid="1" size="300"/>
+ <object address="0x00b75054" typeid="5" size="20">
+ <member address="0x00b75088" />
+ ...
+ </object>
+ ...
+ </objects>
+ </gcheap>
+
+You can break into your process, load SOS, take a snapshot of your heap with
+this function, then continue.
+\\
+COMMAND: threadstate.
+!ThreadState value
+
+The !Threads command outputs, among other things, the state of the thread.
+This is a bit field which corresponds to various states the thread is in.
+To check the state of the thread, simply pass that bit field from the
+output of !Threads into !ThreadState.
+
+Example:
+ 0:003> !Threads
+ ThreadCount: 2
+ UnstartedThread: 0
+ BackgroundThread: 1
+ PendingThread: 0
+ DeadThread: 0
+ Hosted Runtime: no
+ PreEmptive GC Alloc Lock
+ ID OSID ThreadOBJ State GC Context Domain Count APT Exception
+ 0 1 250 0019b068 a020 Disabled 02349668:02349fe8 0015def0 0 MTA
+ 2 2 944 001a6020 b220 Enabled 00000000:00000000 0015def0 0 MTA (Finalizer)
+ 0:003> !ThreadState b220
+ Legal to Join
+ Background
+ CLR Owns
+ CoInitialized
+ In Multi Threaded Apartment
+
+Possible thread states:
+ Thread Abort Requested
+ GC Suspend Pending
+ User Suspend Pending
+ Debug Suspend Pending
+ GC On Transitions
+ Legal to Join
+ Yield Requested
+ Hijacked by the GC
+ Blocking GC for Stack Overflow
+ Background
+ Unstarted
+ Dead
+ CLR Owns
+ CoInitialized
+ In Single Threaded Apartment
+ In Multi Threaded Apartment
+ Reported Dead
+ Fully initialized
+ Task Reset
+ Sync Suspended
+ Debug Will Sync
+ Stack Crawl Needed
+ Suspend Unstarted
+ Aborted
+ Thread Pool Worker Thread
+ Interruptible
+ Interrupted
+ Completion Port Thread
+ Abort Initiated
+ Finalized
+ Failed to Start
+ Detached
+\\
+COMMAND: threads.
+!Threads [-live] [-special]
+
+!Threads lists all the mananaged threads in the process.
+
+-live: optional. Only print threads associated with a live thread.
+-special: optional. With this switch, the command will display all the special
+ threads created by CLR. Those threads might not be managed threads
+ so they might not be shown in the first part of the command's
+ output. Example of special threads include: GC threads (in
+ concurrent GC and server GC), Debugger helper threads, Finalizer
+ threads, AppDomain Unload threads, and Threadpool timer threads.
+
+Each thread has many attributes, many of which can be ignored. The important
+ones are discussed below:
+
+There are three ID columns:
+
+1) The debugger shorthand ID (When the runtime is hosted this column might
+ display the special string "<<<<" when this internal thread object is not
+ associated with any physical thread - this may happen when the host reuses
+ the runtime internal thread object)
+2) The CLR Thread ID
+3) The OS thread ID.
+
+If PreEmptiveGC is enabled for a thread, then a garbage collection
+can occur while that thread is running. For example, if you break in while
+a managed thread is making a PInvoke call to a Win32 function, that thread
+will be in PreEmptive GC mode.
+
+The Domain column indicates what AppDomain the thread is currently executing
+in. You can pass this value to !DumpDomain to find out more.
+
+The APT column gives the COM apartment mode.
+
+Exception will list the last thrown exception (if any) for the thread. More
+details can be obtained by passing the pointer value to !PrintException. If
+you get the notation "(nested exceptions)", you can get details on those
+exceptions by switching to the thread in question, and running
+"!PrintException -nested".
+\\
+
+COMMAND: clrstack.
+!CLRStack [-a] [-l] [-p] [-n]
+!CLRStack [-a] [-l] [-p] [-i] [variable name] [frame]
+
+CLRStack attempts to provide a true stack trace for managed code only. It is
+handy for clean, simple traces when debugging straightforward managed
+programs. The -p parameter will show arguments to the managed function. The
+-l parameter can be used to show information on local variables in a frame.
+SOS can't retrieve local names at this time, so the output for locals is in
+the format <local address> = <value>. The -a (all) parameter is a short-cut
+for -l and -p combined.
+
+If the debugger has the option SYMOPT_LOAD_LINES specified (either by the
+.lines or .symopt commands), SOS will look up the symbols for every managed
+frame and if successful will display the corresponding source file name and
+line number. The -n (No line numbers) parameter can be specified to disable
+this behavior.
+
+When you see methods with the name "[Frame:...", that indicates a transition
+between managed and unmanaged code. You could run !IP2MD on the return
+addresses in the call stack to get more information on each managed method.
+
+On x64 platforms, Transition Frames are not displayed at this time. To avoid
+heavy optimization of parameters and locals one can request the JIT compiler
+to not optimize functions in the managed app by creating a file myapp.ini
+(if your program is myapp.exe) in the same directory. Put the following lines
+in myapp.ini and re-run:
+
+[.NET Framework Debugging Control]
+GenerateTrackingInfo=1
+AllowOptimize=0
+
+The -i option is a new EXPERIMENTAL addition to CLRStack and will use the ICorDebug
+interfaces to display the managed stack and variables. With this option you can also
+view and expand arrays and fields for managed variables. If a stack frame number is
+specified in the command line, CLRStack will show you the parameters and/or locals
+only for that frame (provided you specify -l or -p or -a of course). If a variable
+name and a stack frame number are specified in the command line, CLRStack will show
+you the parameters and/or locals for that frame, and will also show you the fields
+for that variable name you specified. Here are some examples:
+ !CLRStack -i -a : This will show you all parameters and locals for all frames
+ !CLRStack -i -a 3 : This will show you all parameters and locals, for frame 3
+ !CLRStack -i var1 0 : This will show you the fields of 'var1' for frame 0
+ !CLRStack -i var1.abc 2 : This will show you the fields of 'var1', and expand
+ 'var1.abc' to show you the fields of the 'abc' field,
+ for frame 2.
+ !CLRStack -i var1.[basetype] 0 : This will show you the fields of 'var1', and
+ expand the base type of 'var1' to show you its
+ fields.
+ !CLRStack -i var1.[6] 0 : If 'var1' is an array, this will show you the element
+ at index 6 in the array, along with its fields
+The -i options uses DML output for a better debugging experience, so typically you
+should only need to execute "!CLRStack -i", and from there, click on the DML
+hyperlinks to inspect the different managed stack frames and managed variables.
+\\
+
+COMMAND: ip2md.
+!IP2MD <Code address>
+
+Given an address in managed JITTED code, IP2MD attempts to find the MethodDesc
+associated with it. For example, this output from K:
+
+ 0:000> K
+ ChildEBP RetAddr
+ 00a79c78 03ef02ab image00400000!Mainy.Top()+0xb
+ 00a79c78 03ef01a6 image00400000!Mainy.Level(Int32)+0xb
+ 00a79c78 5d3725a1 image00400000!Mainy.Main()+0xee
+ 0012ea04 5d512f59 clr!CallDescrWorkerInternal+0x30
+ 0012ee34 5d7946aa clr!CallDescrWorker+0x109
+
+ 0:000> !IP2MD 03ef01a6
+ MethodDesc: 00902f40
+ Method Name: Mainy.Main()
+ Class: 03ee1424
+ MethodTable: 009032d8
+ mdToken: 0600000d
+ Module: 001caa38
+ IsJitted: yes
+ CodeAddr: 03ef00b8
+ Transparency: Critical
+ Source file: c:\Code\prj.mini\exc.cs @ 39
+
+We have taken a return address into Mainy.Main, and discovered information
+about that method. You could run !U, !DumpMT, !DumpClass, !DumpMD, or
+!DumpModule on the fields listed to learn more.
+
+The "Source line" output will only be present if the debugger can find the
+symbols for the managed module containing the given <code address>, and if the
+debugger is configured to load line number information.
+\\
+
+COMMAND: u.
+!U [-gcinfo] [-ehinfo] [-n] <MethodDesc address> | <Code address>
+
+Presents an annotated disassembly of a managed method when given a MethodDesc
+pointer for the method, or a code address within the method body. Unlike the
+debugger "U" function, the entire method from start to finish is printed,
+with annotations that convert metadata tokens to names.
+
+ <example output>
+ ...
+ 03ef015d b901000000 mov ecx,0x1
+ 03ef0162 ff156477a25b call dword ptr [mscorlib_dll+0x3c7764 (5ba27764)] (System.Console.InitializeStdOutError(Boolean), mdToken: 06000713)
+ 03ef0168 a17c20a701 mov eax,[01a7207c] (Object: SyncTextWriter)
+ 03ef016d 89442414 mov [esp+0x14],eax
+
+If you pass the -gcinfo flag, you'll get inline display of the GCInfo for
+the method. You can also obtain this information with the !GCInfo command.
+
+If you pass the -ehinfo flag, you'll get inline display of exception info
+for the method. (Beginning and end of try/finally/catch handlers, etc.).
+You can also obtain this information with the !EHInfo command.
+
+If the debugger has the option SYMOPT_LOAD_LINES specified (either by the
+.lines or .symopt commands), and if symbols are available for the managed
+module containing the method being examined, the output of the command will
+include the source file name and line number corresponding to the
+disassembly. The -n (No line numbers) flag can be specified to disable this
+behavior.
+
+ <example output>
+ ...
+ c:\Code\prj.mini\exc.cs @ 38:
+ 001b00b0 8b0d3020ab03 mov ecx,dword ptr ds:[3AB2030h] ("Break in debugger. When done type <Enter> to continue: ")
+ 001b00b6 e8d5355951 call mscorlib_ni+0x8b3690 (51743690) (System.Console.Write(System.String), mdToken: 0600091b)
+ 001b00bb 90 nop
+
+ c:\Code\prj.mini\exc.cs @ 39:
+ 001b00bc e863cdc651 call mscorlib_ni+0xf8ce24 (51e1ce24) (System.Console.ReadLine(), mdToken: 060008f6)
+ >>> 001b00c1 90 nop
+ ...
+\\
+
+COMMAND: dumpstack.
+!DumpStack [-EE] [-n] [top stack [bottom stack]]
+
+[x86 and x64 documentation]
+
+This command provides a verbose stack trace obtained by "scraping." Therefore
+the output is very noisy and potentially confusing. The command is good for
+viewing the complete call stack when "kb" gets confused. For best results,
+make sure you have valid symbols.
+
+-EE will only show managed functions.
+
+If the debugger has the option SYMOPT_LOAD_LINES specified (either by the
+.lines or .symopt commands), SOS will look up the symbols for every managed
+frame and if successful will display the corresponding source file name and
+line number. The -n (No line numbers) parameter can be specified to disable
+this behavior.
+
+You can also pass a stack range to limit the output. Use the debugger
+extension !teb to get the top and bottom stack values.
+
+\\
+
+COMMAND: eestack.
+!EEStack [-short] [-EE]
+
+This command runs !DumpStack on all threads in the process. The -EE option is
+passed directly to !DumpStack. The -short option tries to narrow down the
+output to "interesting" threads only, which is defined by
+
+1) The thread has taken a lock.
+2) The thread has been "hijacked" in order to allow a garbage collection.
+3) The thread is currently in managed code.
+
+See the documentation for !DumpStack for more info.
+\\
+
+COMMAND: ehinfo.
+!EHInfo (<MethodDesc address> | <Code address>)
+
+!EHInfo shows the exception handling blocks in a jitted method. For each
+handler, it shows the type, including code addresses and offsets for the clause
+block and the handler block. For a TYPED handler, this would be the "try" and
+"catch" blocks respectively.
+
+Sample output:
+
+ 0:000> !ehinfo 33bbd3a
+ MethodDesc: 03310f68
+ Method Name: MainClass.Main()
+ Class: 03571358
+ MethodTable: 0331121c
+ mdToken: 0600000b
+ Module: 001e2fd8
+ IsJitted: yes
+ CodeAddr: 033bbca0
+ Transparency: Critical
+
+ EHHandler 0: TYPED catch(System.IO.FileNotFoundException)
+ Clause: [033bbd2b, 033bbd3c] [8b, 9c]
+ Handler: [033bbd3c, 033bbd50] [9c, b0]
+
+ EHHandler 1: FINALLY
+ Clause: [033bbd83, 033bbda3] [e3, 103]
+ Handler: [033bbda3, 033bbdc5] [103, 125]
+
+ EHHandler 2: TYPED catch(System.Exception)
+ Clause: [033bbd7a, 033bbdc5] [da, 125]
+ Handler: [033bbdc5, 033bbdd6] [125, 136]
+
+\\
+
+COMMAND: gcinfo.
+!GCInfo (<MethodDesc address> | <Code address>)
+
+!GCInfo is especially useful for CLR Devs who are trying to determine if there
+is a bug in the JIT Compiler. It parses the GCEncoding for a method, which is a
+compressed stream of data indicating when registers or stack locations contain
+managed objects. It is important to keep track of this information, because if
+a garbage collection occurs, the collector needs to know where roots are so it
+can update them with new object pointer values.
+
+Here is sample output where you can see the change in register state. Normally
+you would print this output out and read it alongside a disassembly of the
+method. For example, the notation "reg EDI becoming live" at offset 0x11 of the
+method might correspond to a "mov edi,ecx" statement.
+
+ 0:000> !gcinfo 5b68dbb8 (5b68dbb8 is the start of a JITTED method)
+ entry point 5b68dbb8
+ preJIT generated code
+ GC info 5b9f2f09
+ Method info block:
+ method size = 0036
+ prolog size = 19
+ epilog size = 8
+ epilog count = 1
+ epilog end = yes
+ saved reg. mask = 000B
+ ebp frame = yes
+ fully interruptible=yes
+ double align = no
+ security check = no
+ exception handlers = no
+ local alloc = no
+ edit & continue = no
+ varargs = no
+ argument count = 4
+ stack frame size = 1
+ untracked count = 5
+ var ptr tab count = 0
+ epilog at 002E
+ 36 D4 8C C7 AA |
+ 93 F3 40 05 |
+
+ Pointer table:
+ 14 | [EBP+14H] an untracked local
+ 10 | [EBP+10H] an untracked local
+ 0C | [EBP+0CH] an untracked local
+ 08 | [EBP+08H] an untracked local
+ 44 | [EBP-04H] an untracked local
+ F1 79 | 0011 reg EDI becoming live
+ 72 | 0013 reg ESI becoming live
+ 83 | 0016 push ptr 0
+ 8B | 0019 push ptr 1
+ 93 | 001C push ptr 2
+ 9B | 001F push ptr 3
+ 56 | 0025 reg EDX becoming live
+ 4A | 0027 reg ECX becoming live
+ 0E | 002D reg ECX becoming dead
+ 10 | 002D reg EDX becoming dead
+ E0 | 002D pop 4 ptrs
+ F0 31 | 0036 reg ESI becoming dead
+ 38 | 0036 reg EDI becoming dead
+ FF |
+
+This function is important for CLR Devs, but very difficult for anyone else to
+make sense of it. You would usually come to use it if you suspect a gc heap
+corruption bug caused by invalid GCEncoding for a particular method.
+\\
+
+COMMAND: comstate.
+!COMState
+
+!COMState lists the com apartment model for each thread, as well as a Context
+pointer if provided.
+\\
+
+COMMAND: bpmd.
+!BPMD [-nofuturemodule] <module name> <method name> [<il offset>]
+!BPMD <source file name>:<line number>
+!BPMD -md <MethodDesc>
+!BPMD -list
+!BPMD -clear <pending breakpoint number>
+!BPMD -clearall
+
+!BPMD provides managed breakpoint support. If it can resolve the method name
+to a loaded, jitted or ngen'd function it will create a breakpoint with "bp".
+If not then either the module that contains the method hasn't been loaded yet
+or the module is loaded, but the function is not jitted yet. In these cases,
+!bpmd asks the Windows Debugger to receive CLR Notifications, and waits to
+receive news of module loads and JITs, at which time it will try to resolve
+the function to a breakpoint. -nofuturemodule can be used to suppress
+creating a breakpoint against a module that has not yet been loaded.
+
+Management of the list of pending breakpoints can be done via !BPMD -list,
+!BPMD -clear, and !BPMD -clearall commands. !BPMD -list generates a list of
+all of the pending breakpoints. If the pending breakpoint has a non-zero
+module id, then that pending breakpoint is specific to function in that
+particular loaded module. If the pending breakpoint has a zero module id, then
+the breakpoint applies to modules that have not yet been loaded. Use
+!BPMD -clear or !BPMD -clearall to remove pending breakpoints from the list.
+
+This brings up a good question: "I want to set a breakpoint on the main
+method of my application. How can I do this?"
+
+ 1) If you know the full path to SOS, use this command and skip to step 6
+ .load <the full path to sos.dll>
+
+ 2) If you don't know the full path to sos, its usually next to clr.dll
+ You can wait for clr to load and then find it.
+ Start the debugger and type:
+ sxe -c "" clrn
+ 3) g
+ 4) You'll get the following notification from the debugger:
+ "CLR notification: module 'mscorlib' loaded"
+ 5) Now you can load SOS. Type
+ .loadby sos clr
+
+ 6) Add the breakpoint with command such as:
+ !bpmd myapp.exe MyApp.Main
+ 7) g
+ 8) You will stop at the start of MyApp.Main. If you type "bl" you will
+ see the breakpoint listed.
+
+You can specify breakpoints by file and line number if:
+ a) You have some version of .Net Framework installed on your machine. Any OS from
+ Vista onwards should have .Net Framework installed by default.
+ b) You have PDBs for the managed modules that need breakpoints, and your symbol
+ path points to those PDBs.
+This is often easier than module and method name syntax. For example:
+ !bpmd Demo.cs:15
+
+
+To correctly specify explicitly implemented methods make sure to retrieve the
+method name from the metadata, or from the output of the "!dumpmt -md" command.
+For example:
+
+ public interface I1
+ {
+ void M1();
+ }
+ public class ExplicitItfImpl : I1
+ {
+ ...
+ void I1.M1() // this method's name is 'I1.M1'
+ { ... }
+ }
+
+ !bpmd myapp.exe ExplicitItfImpl.I1.M1
+
+
+!BPMD works equally well with generic types. Adding a breakpoint on a generic
+type sets breakpoints on all already JIT-ted generic methods and sets a pending
+breakpoint for any instantiation that will be JIT-ted in the future.
+
+Example for generics:
+ Given the following two classes:
+
+ class G3<T1, T2, T3>
+ {
+ ...
+ public void F(T1 p1, T2 p2, T3 p3)
+ { ... }
+ }
+
+ public class G1<T> {
+ // static method
+ static public void G<W>(W w)
+ { ... }
+ }
+
+ One would issue the following commands to set breapoints on G3.F() and
+ G1.G():
+
+ !bpmd myapp.exe G3`3.F
+ !bpmd myapp.exe G1`1.G
+
+And for explicitly implemented methods on generic interfaces:
+ public interface IT1<T>
+ {
+ void M1(T t);
+ }
+
+ public class ExplicitItfImpl<U> : IT1<U>
+ {
+ ...
+ void IT1<U>.M1(U u) // this method's name is 'IT1<U>.M1'
+ { ... }
+ }
+
+ !bpmd bpmd.exe ExplicitItfImpl`1.IT1<U>.M1
+
+Additional examples:
+ If IT1 and ExplicitItfImpl are types declared inside another class,
+ Outer, the bpmd command would become:
+
+ !bpmd bpmd.exe Outer+ExplicitItfImpl`1.Outer.IT1<U>.M1
+
+ (note that the fully qualified type name for ExplicitItfImpl became
+ Outer+ExplicitItfImpl, using the '+' separator, while the method name
+ is Outer.IT1<U>.M1, using a '.' as the separator)
+
+ Furthermore, if the Outer class resides in a namespace, NS, the bpmd
+ command to use becomes:
+
+ !bpmd bpmd.exe NS.Outer+ExplicitItfImpl`1.NS.Outer.IT1<U>.M1
+
+!BPMD does not accept offsets nor parameters in the method name. You can add
+an IL offset as an optional parameter seperate from the name. If there are overloaded
+methods, !bpmd will set a breakpoint for all of them.
+
+In the case of hosted environments such as SQL, the module name may be
+complex, like 'price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
+For this case, just be sure to surround the module name with single quotes,
+like:
+
+!bpmd 'price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' Price.M2
+
+\\
+
+COMMAND: dumpdomain.
+!DumpDomain [<Domain address>]
+
+When called with no parameters, !DumpDomain will list all the AppDomains in the
+process. It enumerates each Assembly loaded into those AppDomains as well.
+In addition to your application domain, and any domains it might create, there
+are two special domains: the Shared Domain and the System Domain.
+
+Any Assembly pointer in the output can be passed to !DumpAssembly. Any Module
+pointer in the output can be passed to !DumpModule. Any AppDomain pointer can
+be passed to !DumpDomain to limit output only to that AppDomain. Other
+functions provide an AppDomain pointer as well, such as !Threads where it lists
+the current AppDomain for each thread.
+\\
+
+COMMAND: eeheap.
+!EEHeap [-gc] [-loader]
+
+!EEHeap enumerates process memory consumed by internal CLR data structures. You
+can limit the output by passing "-gc" or "-loader". All information will be
+displayed otherwise.
+
+The information for the Garbage Collector lists the ranges of each Segment in
+the managed heap. This can be useful if you believe you have an object pointer.
+If the pointer falls within a segment range given by "!EEHeap -gc", then you do
+have an object pointer, and can attempt to run "!DumpObj" on it.
+
+Here is output for a simple program:
+
+ 0:000> !eeheap -gc
+ Number of GC Heaps: 1
+ generation 0 starts at 0x00a71018
+ generation 1 starts at 0x00a7100c
+ generation 2 starts at 0x00a71000
+ segment begin allocated size
+ 00a70000 00a71000 00a7e01c 0000d01c(53276)
+ Large object heap starts at 0x01a71000
+ segment begin allocated size
+ 01a70000 01a71000 01a76000 0x00005000(20480)
+ Total Size 0x1201c(73756)
+ ------------------------------
+ GC Heap Size 0x1201c(73756)
+
+So the total size of the GC Heap is only 72K. On a large web server, with
+multiple processors, you can expect to see a GC Heap of 400MB or more. The
+Garbage Collector attempts to collect and reclaim memory only when required to
+by memory pressure for better performance. You can also see the notion of
+"generations," wherein the youngest objects live in generation 0, and
+long-lived objects eventually get "promoted" to generation 2.
+
+The loader output lists various private heaps associated with AppDomains. It
+also lists heaps associated with the JIT compiler, and heaps associated with
+Modules. For example:
+
+ 0:000> !EEHeap -loader
+ Loader Heap:
+ --------------------------------------
+ System Domain: 5e0662a0
+ LowFrequencyHeap:008f0000(00002000:00001000) Size: 0x00001000 bytes.
+ HighFrequencyHeap:008f2000(00008000:00001000) Size: 0x00001000 bytes.
+ StubHeap:008fa000(00002000:00001000) Size: 0x00001000 bytes.
+ Total size: 0x3000(12288)bytes
+ --------------------------------------
+ Shared Domain: 5e066970
+ LowFrequencyHeap:00920000(00002000:00001000) 03e30000(00010000:00003000) Size: 0x00004000 bytes.
+ Wasted: 0x00001000 bytes.
+ HighFrequencyHeap:00922000(00008000:00001000) Size: 0x00001000 bytes.
+ StubHeap:0092a000(00002000:00001000) Size: 0x00001000 bytes.
+ Total size: 0x6000(24576)bytes
+ --------------------------------------
+ Domain 1: 14f000
+ LowFrequencyHeap:00900000(00002000:00001000) 03ee0000(00010000:00003000) Size: 0x00004000 bytes.
+ Wasted: 0x00001000 bytes.
+ HighFrequencyHeap:00902000(00008000:00003000) Size: 0x00003000 bytes.
+ StubHeap:0090a000(00002000:00001000) Size: 0x00001000 bytes.
+ Total size: 0x8000(32768)bytes
+ --------------------------------------
+ Jit code heap:
+ Normal JIT:03ef0000(00010000:00002000) Size: 0x00002000 bytes.
+ Total size: 0x2000(8192)bytes
+ --------------------------------------
+ Module Thunk heaps:
+ Module 5ba22410: Size: 0x00000000 bytes.
+ Module 001c1320: Size: 0x00000000 bytes.
+ Module 001c03f0: Size: 0x00000000 bytes.
+ Module 001caa38: Size: 0x00000000 bytes.
+ Total size: 0x0(0)bytes
+ --------------------------------------
+ Module Lookup Table heaps:
+ Module 5ba22410:Size: 0x00000000 bytes.
+ Module 001c1320:Size: 0x00000000 bytes.
+ Module 001c03f0:Size: 0x00000000 bytes.
+ Module 001caa38:03ec0000(00010000:00002000) Size: 0x00002000 bytes.
+ Total size: 0x2000(8192)bytes
+ --------------------------------------
+ Total LoaderHeap size: 0x15000(86016)bytes
+ =======================================
+
+By using !EEHeap to keep track of the growth of these private heaps, we are
+able to rule out or include them as a source of a memory leak.
+\\
+
+COMMAND: name2ee.
+!Name2EE <module name> <type or method name>
+!Name2EE <module name>!<type or method name>
+
+This function allows you to turn a class name into a MethodTable and EEClass.
+It turns a method name into a MethodDesc. Here is an example for a method:
+
+ 0:000> !name2ee unittest.exe MainClass.Main
+ Module: 001caa38
+ Token: 0x0600000d
+ MethodDesc: 00902f40
+ Name: MainClass.Main()
+ JITTED Code Address: 03ef00b8
+
+and for a class:
+
+ 0:000> !name2ee unittest!MainClass
+ Module: 001caa38
+ Token: 0x02000005
+ MethodTable: 009032d8
+ EEClass: 03ee1424
+ Name: MainClass
+
+The module you are "browsing" with Name2EE needs to be loaded in the process.
+To get a type name exactly right, first browse the module with ILDASM. You
+can also pass * as the <module name> to search all loaded managed modules.
+<module name> can also be the debugger's name for a module, such as
+mscorlib or image00400000.
+
+The Windows Debugger syntax of <module>!<type> is also supported. You can
+use an asterisk on the left of the !, but the type on the right side needs
+to be fully qualified.
+
+If you are looking for a way to display a static field of a class (and you
+don't have an instance of the class, so !dumpobj won't help you), note that
+once you have the EEClass, you can run !DumpClass, which will display the
+value of all static fields.
+
+There is yet one more way to specify a module name. In the case of modules
+loaded from an assembly store (such as a SQL db) rather than disk, the
+module name will look like this:
+
+price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
+
+For this kind of module, simply use price as the module name:
+
+ 0:044> !name2ee price Price
+ Module: 10f028b0 (price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null)
+ Token: 0x02000002
+ MethodTable: 11a47ae0
+ EEClass: 11a538c8
+ Name: Price
+
+Where are we getting these module names from? Run !DumpDomain to see a list of
+all loaded modules in all domains. And remember that you can browse all the
+types in a module with !DumpModule -mt <module pointer>.
+\\
+
+COMMAND: syncblk.
+!SyncBlk [-all | <syncblk number>]
+
+A SyncBlock is a holder for extra information that doesn't need to be created
+for every object. It can hold COM Interop data, HashCodes, and locking
+information for thread-safe operations.
+
+When called without arguments, !SyncBlk will print the list of SyncBlocks
+corresponding to objects that are owned by a thread. For example, a
+
+ lock(MyObject)
+ {
+ ....
+ }
+
+statement will set MyObject to be owned by the current thread. A SyncBlock will
+be created for MyObject, and the thread ownership information stored there
+(this is an oversimplification, see NOTE below). If another thread tries to
+execute the same code, they won't be able to enter the block until the first
+thread exits.
+
+This makes !SyncBlk useful for detecting managed deadlocks. Consider that the
+following code is executed by Threads A & B:
+
+ Resource r1 = new Resource();
+ Resource r2 = new Resource();
+
+ ...
+
+ lock(r1) lock(r2)
+ { {
+ lock(r2) lock(r1)
+ { {
+ ... ...
+ } }
+ } }
+
+This is a deadlock situation, as Thread A could take r1, and Thread B r2,
+leaving both threads with no option but to wait forever in the second lock
+statement. !SyncBlk will detect this with the following output:
+
+ 0:003> !syncblk
+ Index SyncBlock MonitorHeld Recursion Owning Thread Info SyncBlock Owner
+ 238 001e40ec 3 1 001e4e60 e04 3 00a7a194 Resource
+ 239 001e4124 3 1 001e5980 ab8 4 00a7a1a4 Resource
+
+It means that Thread e04 owns object 00a7a194, and Thread ab8 owns object
+00a7a1a4. Combine that information with the call stacks of the deadlock:
+
+(threads 3 and 4 have similar output)
+ 0:003> k
+ ChildEBP RetAddr
+ 0404ea04 77f5c524 SharedUserData!SystemCallStub+0x4
+ 0404ea08 77e75ee0 ntdll!NtWaitForMultipleObjects+0xc
+ 0404eaa4 5d9de9d6 KERNEL32!WaitForMultipleObjectsEx+0x12c
+ 0404eb38 5d9def80 clr!Thread::DoAppropriateAptStateWait+0x156
+ 0404ecc4 5d9dd8bb clr!Thread::DoAppropriateWaitWorker+0x360
+ 0404ed20 5da628dd clr!Thread::DoAppropriateWait+0xbb
+ 0404ede4 5da4e2e2 clr!CLREvent::Wait+0x29d
+ 0404ee70 5da4dd41 clr!AwareLock::EnterEpilog+0x132
+ 0404ef34 5da4efa3 clr!AwareLock::Enter+0x2c1
+ 0404f09c 5d767880 clr!AwareLock::Contention+0x483
+ 0404f1c4 03f00229 clr!JITutil_MonContention+0x2c0
+ 0404f1f4 5b6ef077 image00400000!Worker.Work()+0x79
+ ...
+
+By looking at the code corresponding to Worker.Work()+0x79 (run "!u 03f00229"),
+you can see that thread 3 is attempting to acquire the Resource 00a7a1a4, which
+is owned by thread 4.
+
+NOTE:
+It is not always the case that a SyncBlock will be created for every object
+that is locked by a thread. In version 2.0 of the CLR and above, a mechanism
+called a ThinLock will be used if there is not already a SyncBlock for the
+object in question. ThinLocks will not be reported by the !SyncBlk command.
+You can use "!DumpHeap -thinlock" to list objects locked in this way.
+\\
+
+COMMAND: dumpmt.
+!DumpMT [-MD] <MethodTable address>
+
+Examine a MethodTable. Each managed object has a MethodTable pointer at the
+start. If you pass the "-MD" flag, you'll also see a list of all the methods
+defined on the object.
+\\
+
+COMMAND: dumpclass.
+!DumpClass <EEClass address>
+
+The EEClass is a data structure associated with an object type. !DumpClass
+will show attributes, as well as list the fields of the type. The output is
+similar to !DumpObj. Although static field values will be displayed,
+non-static values won't because you need an instance of an object for that.
+
+You can get an EEClass to look at from !DumpMT, !DumpObj, !Name2EE, and
+!Token2EE among others.
+\\
+
+COMMAND: dumpmd.
+!DumpMD <MethodDesc address>
+
+This command lists information about a MethodDesc. You can use !IP2MD to turn
+a code address in a managed function into a MethodDesc:
+
+ 0:000> !dumpmd 902f40
+ Method Name: Mainy.Main()
+ Class: 03ee1424
+ MethodTable: 009032d8
+ mdToken: 0600000d
+ Module: 001caa78
+ IsJitted: yes
+ CodeAddr: 03ef00b8
+
+If IsJitted is "yes," you can run !U on the CodeAddr pointer to see a
+disassembly of the JITTED code. You can also call !DumpClass, !DumpMT,
+!DumpModule on the Class, MethodTable and Module fields above.
+\\
+
+COMMAND: token2ee.
+!Token2EE <module name> <token>
+
+This function allows you to turn a metadata token into a MethodTable or
+MethodDesc. Here is an example showing class tokens being resolved:
+
+ 0:000> !token2ee unittest.exe 02000003
+ Module: 001caa38
+ Token: 0x02000003
+ MethodTable: 0090375c
+ EEClass: 03ee1ae0
+ Name: Bank
+ 0:000> !token2ee image00400000 02000004
+ Module: 001caa38
+ Token: 0x02000004
+ MethodTable: 009038ec
+ EEClass: 03ee1b84
+ Name: Customer
+
+The module you are "browsing" with Token2EE needs to be loaded in the process.
+This function doesn't see much use, especially since a tool like ILDASM can
+show the mapping between metadata tokens and types/methods in a friendlier way.
+But it could be handy sometimes.
+
+You can pass "*" for <module name> to find what that token maps to in every
+loaded managed module. <module name> can also be the debugger's name for a
+module, such as mscorlib or image00400000.
+\\
+
+COMMAND: eeversion.
+!EEVersion
+
+This prints the Common Language Runtime version. It also tells you if the code
+is running in "Workstation" or "Server" mode, a distinction which affects the
+garbage collector. The most apparent difference in the debugger is that in
+"Server" mode there is one dedicated garbage collector thread per CPU.
+
+A handy supplement to this function is to also run "lm v m clr". That
+will provide more details about the CLR, including where clr.dll is
+loaded from.
+\\
+
+COMMAND: dumpmodule.
+!DumpModule [-mt] <Module address>
+
+You can get a Module address from !DumpDomain, !DumpAssembly and other
+functions. Here is sample output:
+
+ 0:000> !DumpModule 1caa50
+ Name: C:\pub\unittest.exe
+ Attributes: PEFile
+ Assembly: 001ca248
+ LoaderHeap: 001cab3c
+ TypeDefToMethodTableMap: 03ec0010
+ TypeRefToMethodTableMap: 03ec0024
+ MethodDefToDescMap: 03ec0064
+ FieldDefToDescMap: 03ec00a4
+ MemberRefToDescMap: 03ec00e8
+ FileReferencesMap: 03ec0128
+ AssemblyReferencesMap: 03ec012c
+ MetaData start address: 00402230 (1888 bytes)
+
+The Maps listed map metadata tokens to CLR data structures. Without going into
+too much detail, you can examine memory at those addresses to find the
+appropriate structures. For example, the TypeDefToMethodTableMap above can be
+examined:
+
+ 0:000> dd 3ec0010
+ 03ec0010 00000000 00000000 0090320c 0090375c
+ 03ec0020 009038ec ...
+
+This means TypeDef token 2 maps to a MethodTable with the value 0090320c. You
+can run !DumpMT to verify that. The MethodDefToDescMap takes a MethodDef token
+and maps it to a MethodDesc, which can be passed to !DumpMD.
+
+There is a new option "-mt", which will display the types defined in a module,
+and the types referenced by the module. For example:
+
+ 0:000> !dumpmodule -mt 1aa580
+ Name: C:\pub\unittest.exe
+ ...<etc>...
+ MetaData start address: 0040220c (1696 bytes)
+
+ Types defined in this module
+
+ MT TypeDef Name
+ --------------------------------------------------------------------------
+ 030d115c 0x02000002 Funny
+ 030d1228 0x02000003 Mainy
+
+ Types referenced in this module
+
+ MT TypeRef Name
+ --------------------------------------------------------------------------
+ 030b6420 0x01000001 System.ValueType
+ 030b5cb0 0x01000002 System.Object
+ 030fceb4 0x01000003 System.Exception
+ 0334e374 0x0100000c System.Console
+ 03167a50 0x0100000e System.Runtime.InteropServices.GCHandle
+ 0336a048 0x0100000f System.GC
+
+\\
+
+COMMAND: threadpool.
+!ThreadPool
+
+This command lists basic information about the ThreadPool, including the number
+of work requests in the queue, number of completion port threads, and number of
+timers.
+\\
+
+COMMAND: dumpassembly.
+!DumpAssembly <Assembly address>
+
+Example output:
+
+ 0:000> !dumpassembly 1ca248
+ Parent Domain: 0014f000
+ Name: C:\pub\unittest.exe
+ ClassLoader: 001ca060
+ Module Name
+ 001caa50 C:\pub\unittest.exe
+
+An assembly can consist of multiple modules, and those will be listed. You can
+get an Assembly address from the output of !DumpDomain.
+\\
+
+COMMAND: dumpruntimetypes.
+!DumpRuntimeTypes
+
+!DumpRuntimeTypes finds all System.RuntimeType objects in the gc heap and
+prints the type name and MethodTable they refer too. Sample output:
+
+ Address Domain MT Type Name
+ ------------------------------------------------------------------------------
+ a515f4 14a740 5baf8d28 System.TypedReference
+ a51608 14a740 5bb05764 System.Globalization.BaseInfoTable
+ a51958 14a740 5bb05b24 System.Globalization.CultureInfo
+ a51a44 14a740 5bb06298 System.Globalization.GlobalizationAssembly
+ a51de0 14a740 5bb069c8 System.Globalization.TextInfo
+ a56b98 14a740 5bb12d28 System.Security.Permissions.HostProtectionResource
+ a56bbc 14a740 5baf7248 System.Int32
+ a56bd0 14a740 5baf3fdc System.String
+ a56cfc 14a740 5baf36a4 System.ValueType
+ ...
+
+This command will print a "?" in the domain column if the type is loaded into multiple
+AppDomains. For example:
+
+ 0:000> !DumpRuntimeTypes
+ Address Domain MT Type Name
+ ------------------------------------------------------------------------------
+ 28435a0 ? 3f6a8c System.TypedReference
+ 28435b4 ? 214d6c System.ValueType
+ 28435c8 ? 216314 System.Enum
+ 28435dc ? 2147cc System.Object
+ 284365c ? 3cd57c System.IntPtr
+ 2843670 ? 3feaac System.Byte
+ 2843684 ? 23a544c System.IEquatable`1[[System.IntPtr, mscorlib]]
+ 2843784 ? 3c999c System.Int32
+ 2843798 ? 3caa04 System.IEquatable`1[[System.Int32, mscorlib]]
+
+\\
+
+COMMAND: dumpsig.
+!DumpSig <sigaddr> <moduleaddr>
+
+This command dumps the signature of a method or field given by <sigaddr>. This is
+useful when you are debugging parts of the runtime which returns a raw PCCOR_SIGNATURE
+structure and need to know what its contents are.
+
+Sample output for a method:
+ 0:000> !dumpsig 0x000007fe`ec20879d 0x000007fe`eabd1000
+ [DEFAULT] [hasThis] Void (Boolean,String,String)
+
+The first section of the output is the calling convention. This includes, but is not
+limited to, "[DEFAULT]", "[C]", "[STDCALL]", "[THISCALL]", and so on. The second
+portion of the output is either "[hasThis]" or "[explicit]" for whether the method
+is an instance method or a static method respectively. The third portion of the
+output is the return value (in this case a "void"). Finally, the method's arguments
+are printed as the final portion of the output.
+
+Sample output for a field:
+ 0:000> !dumpsig 0x000007fe`eb7fd8cd 0x000007fe`eabd1000
+ [FIELD] ValueClass System.RuntimeTypeHandle
+
+!DumpSig will also work with generics. Here is the output for the following
+function:
+ public A Test(IEnumerable<B> n)
+
+ 0:000> !dumpsig 00000000`00bc2437 000007ff00043178
+ [DEFAULT] [hasThis] __Canon (Class System.Collections.Generic.IEnumerable`1<__Canon>)
+
+\\
+
+COMMAND: dumpsigelem.
+!DumpSigElem <sigaddr> <moduleaddr>
+
+This command dumps a single element of a signature object. For most circumstances,
+you should use !DumpSig to look at individual signature objects, but if you find a
+signature that has been corrupted in some manner you can use !DumpSigElem to read out
+the valid portions of it.
+
+If we look at a valid signature object for a method we see the following:
+ 0:000> !dumpsig 0x000007fe`ec20879d 0x000007fe`eabd1000
+ [DEFAULT] [hasThis] Void (Boolean,String,String)
+
+We can look at the individual elements of this object by adding the offsets into the
+object which correspond to the return value and parameters:
+ 0:000> !dumpsigelem 0x000007fe`ec20879d+2 0x000007fe`eabd1000
+ Void
+ 0:000> !dumpsigelem 0x000007fe`ec20879d+3 0x000007fe`eabd1000
+ Boolean
+ 0:000> !dumpsigelem 0x000007fe`ec20879d+4 0x000007fe`eabd1000
+ String
+ 0:000> !dumpsigelem 0x000007fe`ec20879d+5 0x000007fe`eabd1000
+ String
+
+We can do something similar for fields. Here is the full signature of a field:
+ 0:000> !dumpsig 0x000007fe`eb7fd8cd 0x000007fe`eabd1000
+ [FIELD] ValueClass System.RuntimeTypeHandle
+
+Using !DumpSigElem we can find the type of the field by adding the offset of it (1) to
+the address of the signature:
+ 0:000> !dumpsigelem 0x000007fe`eb7fd8cd+1 0x000007fe`eabd1000
+ ValueClass System.RuntimeTypeHandle
+
+!DumpSigElem will also work with generics. Let a function be defined as follows:
+ public A Test(IEnumerable<B> n)
+
+The elements of this signature can be obtained by adding offsets into the signature
+when calling !DumpSigElem:
+
+ 0:000> !dumpsigelem 00000000`00bc2437+2 000007ff00043178
+ __Canon
+ 0:000> !dumpsigelem 00000000`00bc2437+4 000007ff00043178
+ Class System.Collections.Generic.IEnumerable`1<__Canon>
+
+The actual offsets that you should add are determined by the contents of the
+signature itself. By trial and error you should be able to find various elements
+of the signature.
+
+\\
+
+COMMAND: rcwcleanuplist.
+!RCWCleanupList [address]
+
+A RuntimeCallableWrapper is an internal CLR structure used to host COM objects
+which are exposed to managed code. This is exposed to managed code through the
+System.__ComObject class, and when objects of this type are collected, and a
+reference to the underlying COM object is no longer needed, the corresponding
+RCW is cleaned up. If you are trying to debug an issue related to one of these
+RCWs, then you can use the !RCWCleanupList function to display which COM objects
+will be released the next time a cleanup occurs.
+
+If given an address, this function will display the RCWCleanupList at that address.
+If no address is specified, it displays the default cleanup list, printing the
+wrapper, the context, and the thread of the object.
+
+Example:
+ 0:002> !rcwcleanuplist 001c04d0
+ RuntimeCallableWrappers (RCW) to be cleaned:
+ RCW CONTEXT THREAD Apartment
+ 1d54e0 192008 181180 STA
+ 1d4140 192178 0 MTA
+ 1dff50 192178 0 MTA
+ MTA Interfaces to be released: 2
+ STA Interfaces to be released: 1
+
+Note that CLR keeps track of which RCWs are bound to which managed objects through
+the SyncBlock of the object. As such, you can see more information about RCW
+objects through the !SyncBlk command. You can find more information about RCW
+cleanup through the !FinalizeQueue command.
+
+\\
+
+COMMAND: dumpil.
+!DumpIL <Managed DynamicMethod object> |
+ <DynamicMethodDesc pointer> |
+ <MethodDesc pointer> |
+ /i <IL pointer>
+
+!DumpIL prints the IL code associated with a managed method. We added this
+function specifically to debug DynamicMethod code which was constructed on
+the fly. Happily it works for non-dynamic code as well.
+
+You can use it in four ways:
+
+ 1) If you have a System.Reflection.Emit.DynamicMethod object, just pass
+ the pointer as the first argument.
+ 2) If you have a DynamicMethodDesc pointer you can use that to print the
+ IL associated with the dynamic method.
+ 3) If you have an ordinary MethodDesc, you can see the IL for that as well,
+ just pass it as the first argument.
+ 4) If you have a pointer directly to the IL, specify /i followed by the
+ the IL address. This is useful for writers of profilers that instrument
+ IL.
+
+
+Note that dynamic IL is constructed a bit differently. Rather than referring
+to metadata tokens, the IL points to objects in a managed object array. Here
+is a simple example of the output for a dynamic method:
+
+ 0:000> !dumpil b741dc
+ This is dynamic IL. Exception info is not reported at this time.
+ If a token is unresolved, run "!do <addr>" on the addr given
+ in parenthesis. You can also look at the token table yourself, by
+ running "!DumpArray 00b77388".
+
+ IL_0000: ldstr 70000002 "Inside invoked method "
+ IL_0005: call 6000003 System.Console.WriteLine(System.String)
+ IL_000a: ldc.i4.1
+ IL_000b: newarr 2000004 "System.Int32"
+ IL_0010: stloc.0
+ IL_0011: ldloc.0
+ IL_0012: ret
+
+\\
+
+COMMAND: verifyheap.
+!VerifyHeap
+
+!VerifyHeap is a diagnostic tool that checks the garbage collected heap for
+signs of corruption. It walks objects one by one in a pattern like this:
+
+ o = firstobject;
+ while(o != endobject)
+ {
+ o.ValidateAllFields();
+ o = (Object *) o + o.Size();
+ }
+
+If an error is found, !VerifyHeap will report it. I'll take a perfectly good
+object and corrupt it:
+
+ 0:000> !DumpObj a79d40
+ Name: Customer
+ MethodTable: 009038ec
+ EEClass: 03ee1b84
+ Size: 20(0x14) bytes
+ (C:\pub\unittest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 009038ec 4000008 4 CLASS instance 00a79ce4 name
+ 009038ec 4000009 8 CLASS instance 00a79d2c bank
+ 009038ec 400000a c System.Boolean instance 1 valid
+
+ 0:000> ed a79d40+4 01 (change the name field to the bogus pointer value 1)
+ 0:000> !VerifyHeap
+ object 01ee60dc: bad member 00000003 at 01EE6168
+ Last good object: 01EE60C4.
+
+If this gc heap corruption exists, there is a serious bug in your own code or
+in the CLR. In user code, an error in constructing PInvoke calls can cause
+this problem, and running with Managed Debugging Assistants is advised. If that
+possibility is eliminated, consider contacting Microsoft Product Support for
+help.
+
+\\
+
+COMMAND: verifyobj.
+!VerifyObj <object address>
+
+!VerifyObj is a diagnostic tool that checks the object that is passed as an
+argument for signs of corruption.
+
+ 0:002> !verifyobj 028000ec
+ object 0x28000ec does not have valid method table
+
+ 0:002> !verifyobj 0680017c
+ object 0x680017c: bad member 00000001 at 06800184
+
+\\
+
+COMMAND: findroots.
+!FindRoots -gen <N> | -gen any | <object address>
+
+The "-gen" form causes the debugger to break in the debuggee on the next
+collection of the specified generation. The effect is reset as soon as the
+break occurs, in other words, if you need to break on the next collection you
+would need to reissue the command.
+
+The last form of this command is meant to be used after the break caused by the
+other forms has occurred. Now the debuggee is in the right state for
+!FindRoots to be able to identify roots for objects from the current condemned
+generations.
+
+!FindRoots is a diagnostic command that is meant to answer the following
+question:
+
+"I see that GCs are happening, however my objects have still not been
+collected. Why? Who is holding onto them?"
+
+The process of answering the question would go something like this:
+
+1. Find out the generation of the object of interest using the !GCWhere
+command, say it is gen 1:
+ !GCWhere <object address>
+
+2. Instruct the runtime to stop the next time it collects that generation using
+the !FindRoots command:
+ !FindRoots -gen 1
+ g
+
+3. When the next GC starts, and has proceeded past the mark phase a CLR
+notification will cause a break in the debugger:
+ (fd0.ec4): CLR notification exception - code e0444143 (first chance)
+ CLR notification: GC - end of mark phase.
+ Condemned generation: 1.
+
+4. Now we can use the !FindRoots <object address> to find out the cross
+generational references to the object of interest. In other words, even if the
+object is not referenced by any "proper" root it may still be referenced by an
+older object (from an older generation), from a generation that has not yet been
+scheduled for collection. At this point !FindRoots will search those older
+generations too, and report those roots.
+ 0:002> !findroots 06808094
+ older generations::Root: 068012f8(AAA.Test+a)->
+ 06808094(AAA.Test+b)
+
+
+\\
+
+COMMAND: heapstat.
+!HeapStat [-inclUnrooted | -iu]
+
+This command shows the generation sizes for each heap and the total, how much free
+space there is in each generation on each heap. If the -inclUnrooted option is
+specified the report will include information about the managed objects from the
+GC heap that are not rooted anymore.
+
+Sample output:
+
+ 0:002> !heapstat
+ Heap Gen0 Gen1 Gen2 LOH
+ Heap0 177904 12 306956 8784
+ Heap1 159652 12 12 16
+ Total 337556 24 306968 8800
+
+ Free space: Percentage
+ Heap0 28 12 12 64 SOH: 0% LOH: 0%
+ Heap1 104 12 12 16 SOH: 0% LOH:100%
+ Total 132 24 24 80
+
+ 0:002> !heapstat -inclUnrooted
+ Heap Gen0 Gen1 Gen2 LOH
+ Heap0 177904 12 306956 8784
+ Heap1 159652 12 12 16
+ Total 337556 24 306968 8800
+
+ Free space: Percentage
+ Heap0 28 12 12 64 SOH: 0% LOH: 0%
+ Heap1 104 12 12 16 SOH: 0% LOH:100%
+ Total 132 24 24 80
+
+ Unrooted objects: Percentage
+ Heap0 152212 0 306196 0 SOH: 94% LOH: 0%
+ Heap1 155704 0 0 0 SOH: 97% LOH: 0%
+ Total 307916 0 306196 0
+
+The percentage column contains a breakout of free or unrooted bytes to total bytes.
+
+\\
+
+COMMAND: analyzeoom.
+!AnalyzeOOM
+
+!AnalyzeOOM displays the info of the last OOM occurred on an allocation request to
+the GC heap (in Server GC it displays OOM, if any, on each GC heap).
+
+To see the managed exception(s) use the !Threads command which will show you
+managed exception(s), if any, on each managed thread. If you do see an
+OutOfMemoryException exception you can use the !PrintException command on it.
+To get the full callstack use the "kb" command in the debugger for that thread.
+For example, to display thread 3's stack use ~3kb.
+
+OOM exceptions could be because of the following reasons:
+
+1) allocation request to GC heap
+ in which case you will see JIT_New* on the call stack because managed code called new.
+2) other runtime allocation failure
+ for example, failure to expand the finalize queue when GC.ReRegisterForFinalize is
+ called.
+3) some other code you use throws a managed OOM exception
+ for example, some .NET framework code converts a native OOM exception to managed
+ and throws it.
+
+The !AnalyzeOOM command aims to help you with investigating 1) which is the most
+difficult because it requires some internal info from GC. The only exception is
+we don't support allocating objects larger than 2GB on CLR v2.0 or prior. And this
+command will not display any managed OOM because we will throw OOM right away
+instead of even trying to allocate it on the GC heap.
+
+There are 2 legitimate scenarios where GC would return OOM to allocation requests -
+one is if the process is running out of VM space to reserve a segment; the other
+is if the system is running out physical memory (+ page file if you have one) so
+GC can not commit memory it needs. You can look at these scenarios by using performance
+counters or debugger commands. For example for the former scenario the "!address
+-summary" debugger command will show you the largest free region in the VM. For
+the latter scenario you can look at the "Memory\% Committed Bytes In Use" see
+if you are running low on commit space. One important thing to keep in mind is
+when you do this kind of memory analysis it could an aftereffect and doesn't
+completely agree with what this command tells you, in which case the command should
+be respected because it truly reflects what happened during GC.
+
+The other cases should be fairly obvious from the callstack.
+
+Sample output:
+
+0:011> !ao
+---------Heap 2 ---------
+Managed OOM occurred after GC #28 (Requested to allocate 1234 bytes)
+Reason: Didn't have enough memory to commit
+Detail: SOH: Didn't have enough memory to grow the internal GC datastructures (800000 bytes) -
+ on GC entry available commit space was 500 MB
+---------Heap 4 ---------
+Managed OOM occurred after GC #12 (Requested to allocate 100000 bytes)
+Reason: Didn't have enough memory to allocate an LOH segment
+Detail: LOH: Failed to reserve memory (16777216 bytes)
+
+\\
+
+COMMAND: gcwhere.
+!GCWhere <object address>
+
+!GCWhere displays the location in the GC heap of the argument passed in.
+
+ 0:002> !GCWhere 02800038
+ Address Gen Heap segment begin allocated size
+ 02800038 2 0 02800000 02800038 0282b740 12
+
+When the argument lies in the managed heap, but is not a valid *object* address
+the "size" is displayed as 0:
+
+ 0:002> !GCWhere 0280003c
+ Address Gen Heap segment begin allocated size
+ 0280003c 2 0 02800000 02800038 0282b740 0
+
+\\
+
+COMMAND: listnearobj.
+!ListNearObj <object address>
+
+!ListNearObj is a diagnostic tool that displays the object preceeding and
+succeeding the address passed in:
+
+The command looks for the address in the GC heap that looks like a valid
+beginning of a managed object (based on a valid method table) and the object
+following the argument address.
+
+ 0:002> !ListNearObj 028000ec
+ Before: 0x28000a4 72 (0x48 ) System.StackOverflowException
+ After: 0x2800134 72 (0x48 ) System.Threading.ThreadAbortException
+ Heap local consistency confirmed.
+
+ 0:002> !ListNearObj 028000f0
+ Before: 0x28000ec 72 (0x48 ) System.ExecutionEngineException
+ After: 0x2800134 72 (0x48 ) System.Threading.ThreadAbortException
+ Heap local consistency confirmed.
+
+The command considers the heap as "locally consistent" if:
+ prev_obj_addr + prev_obj_size = arg_addr && arg_obj + arg_size = next_obj_addr
+OR
+ prev_obj_addr + prev_obj_size = next_obj_addr
+
+When the condition is not satisfied:
+
+ 0:002> !lno 028000ec
+ Before: 0x28000a4 72 (0x48 ) System.StackOverflowException
+ After: 0x2800134 72 (0x48 ) System.Threading.ThreadAbortException
+ Heap local consistency not confirmed.
+
+\\
+
+COMMAND: dumplog.
+!DumpLog [-addr <addressOfStressLog>] [<Filename>]
+
+To aid in diagnosing hard-to-reproduce stress failures, the CLR team added an
+in-memory log capability. The idea was to avoid using locks or I/O which could
+disturb a fragile repro environment. The !DumpLog function allows you to write
+that log out to a file. If no Filename is specified, the file "Stresslog.txt"
+in the current directory is created.
+
+The optional argument addr allows one to specify a stress log other then the
+default one.
+
+ 0:000> !DumpLog
+ Attempting to dump Stress log to file 'StressLog.txt'
+ .................
+ SUCCESS: Stress log dumped
+
+To turn on the stress log, set the following registry keys under
+HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework:
+
+
+(DWORD) StressLog = 1
+(DWORD) LogFacility = 0xffffffbf (this is a bit mask, almost all logging is on.
+ This is also the default value if the key
+ isn't specified)
+(DWORD) StressLogSize = 65536 (this is the default value if the key isn't
+ specified)
+(DWORD) LogLevel = 6 (this is the default value if the key isn't
+ specified. The higher the number the more
+ detailed logs are generated. The maximum
+ value is decimal 10)
+
+StressLogSize is the size in bytes of the in-memory log allocated for each
+thread in the process. In the case above, each thread gets a 64K log. You
+could increase this to get more logging, but more memory will be required for
+this log in the process. For example, 20 threads with 524288 bytes per thread
+has a memory demand of 10 Megabytes. The stress log is circular so new entries
+will replace older ones on threads which have reached their buffer limit.
+
+The log facilities are defined as follows:
+ GC 0x00000001
+ GCINFO 0x00000002
+ STUBS 0x00000004
+ JIT 0x00000008
+ LOADER 0x00000010
+ METADATA 0x00000020
+ SYNC 0x00000040
+ EEMEM 0x00000080
+ GCALLOC 0x00000100
+ CORDB 0x00000200
+ CLASSLOADER 0x00000400
+ CORPROF 0x00000800
+ REMOTING 0x00001000
+ DBGALLOC 0x00002000
+ EH 0x00004000
+ ENC 0x00008000
+ ASSERT 0x00010000
+ VERIFIER 0x00020000
+ THREADPOOL 0x00040000
+ GCROOTS 0x00080000
+ INTEROP 0x00100000
+ MARSHALER 0x00200000
+ IJW 0x00400000
+ ZAP 0x00800000
+ STARTUP 0x01000000
+ APPDOMAIN 0x02000000
+ CODESHARING 0x04000000
+ STORE 0x08000000
+ SECURITY 0x10000000
+ LOCKS 0x20000000
+ BCL 0x40000000
+
+Here is some sample output:
+
+ 3560 9.981137099 : `SYNC` RareEnablePremptiveGC: entering.
+ Thread state = a030
+
+ 3560 9.981135033 : `GC`GCALLOC`GCROOTS` ========== ENDGC 4194 (gen = 2,
+ collect_classes = 0) ==========={
+
+ 3560 9.981125826 : `GC` Segment mem 00C61000 alloc
+ = 00D071F0 used 00D09254 committed 00D17000
+
+ 3560 9.981125726 : `GC` Generation 0 [00CED07C, 00000000
+ ] cur = 00000000
+
+ 3560 9.981125529 : `GC` Generation 1 [00CED070, 00000000
+ ] cur = 00000000
+
+ 3560 9.981125103 : `GC` Generation 2 [00C61000, 00000000
+ ] cur = 00000000
+
+ 3560 9.981124963 : `GC` GC Heap 00000000
+
+ 3560 9.980618994 : `GC`GCROOTS` GcScanHandles (Promotion Phase = 0)
+
+The first column is the OS thread ID for the thread appending to the log,
+the second column is the timestamp, the third is the facility category for the
+log entry, and the fourth contains the log message. The facility field is
+expressed as `facility1`facility2`facility3`. This facilitates the creation of
+filters for displaying only specific message categories. To make sense of this
+log, you would probably want the Shared Source CLI to find out exactly where
+the log comes from.
+\\
+
+COMMAND: findappdomain.
+!FindAppDomain <Object address>
+
+!FindAppDomain will attempt to resolve the AppDomain of an object. For example,
+using an Object Pointer from the output of !DumpStackObjects:
+
+ 0:000> !findappdomain 00a79d98
+ AppDomain: 0014f000
+ Name: unittest.exe
+ ID: 1
+
+You can find out more about the AppDomain with the !DumpDomain command. Not
+every object has enough clues about it's origin to determine the AppDomain.
+Objects with Finalizers are the easiest case, as the CLR needs to be able to
+call those when an AppDomain shuts down.
+\\
+
+COMMAND: savemodule.
+!SaveModule <Base address> <Filename>
+
+This command allows you to take a image loaded in memory and write it to a
+file. This is especially useful if you are debugging a full memory dump, and
+don't have the original DLLs or EXEs. This is most often used to save a managed
+binary to a file, so you can disassemble the code and browse types with ILDASM.
+
+The base address of an image can be found with the "LM" debugger command:
+
+ 0:000> lm
+ start end module name
+ 00400000 00408000 image00400000 (deferred)
+ 10200000 102ac000 MSVCR80D (deferred)
+ 5a000000 5a0b1000 mscoree (deferred)
+ 5a140000 5a29e000 clrjit (deferred)
+ 5b660000 5c440000 mscorlib_dll (deferred)
+ 5d1d0000 5e13c000 clr (deferred)
+ ...
+
+If I wanted to save a copy of clr.dll, I could run:
+
+ 0:000> !SaveModule 5d1d0000 c:\pub\out.tmp
+ 4 sections in file
+ section 0 - VA=1000, VASize=e82da9, FileAddr=400, FileSize=e82e00
+ section 1 - VA=e84000, VASize=24d24, FileAddr=e83200, FileSize=ec00
+ section 2 - VA=ea9000, VASize=5a8, FileAddr=e91e00, FileSize=600
+ section 3 - VA=eaa000, VASize=c183c, FileAddr=e92400, FileSize=c1a00
+
+The diagnostic output indicates that the operation was successful. If
+c:\pub\out.tmp already exists, it will be overwritten.
+\\
+
+COMMAND: gchandles.
+!GCHandles [-type handletype] [-stat] [-perdomain]
+
+!GCHandles provides statistics about GCHandles in the process.
+
+Paremeters:
+ stat - Only display the statistics and not the list of handles and
+ what they point to.
+ perdomain - Break down the statistics by the app domain in which
+ the handles reside.
+ type - A type of handle to filter it by. The handle types are:
+ Pinned
+ RefCounted
+ WeakShort
+ WeakLong
+ Strong
+ Variable
+ AsyncPinned
+ SizedRef
+
+Sometimes the source of a memory leak is a GCHandle leak. For example, code
+might keep a 50 Megabyte array alive because a strong GCHandle points to it,
+and the handle was discarded without freeing it.
+
+The most common handles are "Strong Handles," which keep the object they point
+to alive until the handle is explicitly freed. "Pinned Handles" are used to
+prevent the garbage collector from moving an object during collection. These
+should be used sparingly, and for short periods of time. If you don't follow
+that precept, the gc heap can become very fragmented.
+
+Here is sample output from a very simple program. Note that the "RefCount"
+field only applies to RefCount Handles, and this field will contain the
+reference count:
+
+ 0:000> !GCHandles
+ Handle Type Object Size RefCount Type
+ 001611c0 Strong 01d00b58 84 System.IndexOutOfRangeException
+ 001611c4 Strong 01d00b58 84 System.IndexOutOfRangeException
+ 001611c8 Strong 01d1b48c 40 System.Diagnostics.LogSwitch
+ 001611d0 Strong 01cfd2c0 36 System.Security.PermissionSet
+ 001611d4 Strong 01cf7484 56 System.Object[]
+ 001611d8 Strong 01cf1238 32 System.SharedStatics
+ 001611dc Strong 01cf11c8 84 System.Threading.ThreadAbortException
+ 001611e0 Strong 01cf1174 84 System.Threading.ThreadAbortException
+ 001611e4 Strong 01cf1120 84 System.ExecutionEngineException
+ 001611e8 Strong 01cf10cc 84 System.StackOverflowException
+ 001611ec Strong 01cf1078 84 System.OutOfMemoryException
+ 001611f0 Strong 01cf1024 84 System.Exception
+ 001611f8 Strong 01cf2068 48 System.Threading.Thread
+ 001611fc Strong 01cf1328 112 System.AppDomain
+ 001613ec Pinned 02cf3268 8176 System.Object[]
+ 001613f0 Pinned 02cf2258 4096 System.Object[]
+ 001613f4 Pinned 02cf2038 528 System.Object[]
+ 001613f8 Pinned 01cf121c 12 System.Object
+ 001613fc Pinned 02cf1010 4116 System.Object[]
+
+ Statistics:
+ MT Count TotalSize Class Name
+ 563266dc 1 12 System.Object
+ 56329708 1 32 System.SharedStatics
+ 5632bc38 1 36 System.Security.PermissionSet
+ 5635f934 1 40 System.Diagnostics.LogSwitch
+ 5632759c 1 48 System.Threading.Thread
+ 5632735c 1 84 System.ExecutionEngineException
+ 56327304 1 84 System.StackOverflowException
+ 563272ac 1 84 System.OutOfMemoryException
+ 563270c4 1 84 System.Exception
+ 56328914 1 112 System.AppDomain
+ 56335f78 2 168 System.IndexOutOfRangeException
+ 563273b4 2 168 System.Threading.ThreadAbortException
+ 563208d0 5 16972 System.Object[]
+ Total 19 objects
+
+ Handles:
+ Strong Handles: 14
+ Pinned Handles: 5
+\\
+
+COMMAND: gchandleleaks.
+!GCHandleLeaks
+
+This command is an aid in tracking down GCHandle leaks. It searches all of
+memory for any references to the Strong and Pinned GCHandles in the process,
+and reports what it found. If a handle is found, you'll see the address of the
+reference. This might be a stack address or a field within an object, for
+example. If a handle is not found in memory, you'll get notification of that
+too.
+
+The command has diagnostic output which doesn't need to be repeated here. One
+thing to keep in mind is that anytime you search all of memory for a value, you
+can get false positives because even though the value was found, it might be
+garbage in that no code knows about the address. You can also get false
+negatives because a user is free to pass that GCHandle to unmanaged code that
+might store the handle in a strange way (shifting bits, for example).
+
+For example, a GCHandle valuetype is stored on the stack with the low bit set
+if it points to a Pinned handle. So !GCHandleLeaks ignores the low bit in it's
+searches.
+
+That said, if a serious leak is going on, you'll get a ever-growing set of
+handle addresses that couldn't be found.
+\\
+
+COMMAND: vmmap.
+!VMMap
+
+!VMMap traverses the virtual address space and lists the type of protection
+applied to each region. Sample output:
+
+ 0:000> !VMMap
+ Start Stop Length AllocProtect Protect State Type
+ 00000000-0000ffff 00010000 NA Free
+ 00010000-00011fff 00002000 RdWr RdWr Commit Private
+ 00012000-0001ffff 0000e000 NA Free
+ 00020000-00020fff 00001000 RdWr RdWr Commit Private
+ 00021000-0002ffff 0000f000 NA Free
+ 00030000-00030fff 00001000 RdWr Reserve Private
+ ...
+\\
+
+COMMAND: vmstat.
+!VMStat
+
+Provides a summary view of the virtual address space, ordered by each type of
+protection applied to that memory (free, reserved, committed, private, mapped,
+image). The TOTAL column is (AVERAGE * BLK COUNT). Sample output below:
+
+ 0:000> !VMStat
+ ~~~~ ~~~~~~~ ~~~~~~~ ~~~~~~~ ~~~~~~~~~ ~~~~~
+ TYPE MINIMUM MAXIMUM AVERAGE BLK COUNT TOTAL
+ Free:
+ Small 4,096 65,536 48,393 27 1,306,611
+ Medium 139,264 528,384 337,920 4 1,351,680
+ Large 6,303,744 974,778,368 169,089,706 12 2,029,076,472
+ Summary 4,096 974,778,368 47,249,646 43 2,031,734,778
+
+ Reserve:
+ Small 4,096 65,536 43,957 41 1,802,237
+ Medium 249,856 1,019,904 521,557 6 3,129,342
+ Large 2,461,696 16,703,488 11,956,224 3 35,868,672
+ Summary 4,096 16,703,488 816,005 50 40,800,250
+
+\\
+
+COMMAND: procinfo.
+!ProcInfo [-env] [-time] [-mem]
+
+!ProcInfo lists the environment variables for the process, kernel CPU time, as
+well as memory usage statistics.
+\\
+
+COMMAND: histinit.
+!HistInit
+
+Before running any of the Hist - family commands you need to initialize the SOS
+structures from the stress log saved in the debuggee. This is achieved by the
+HistInit command.
+
+Sample output:
+
+ 0:001> !HistInit
+ Attempting to read Stress log
+ STRESS LOG:
+ facilitiesToLog = 0xffffffff
+ levelToLog = 6
+ MaxLogSizePerThread = 0x10000 (65536)
+ MaxTotalLogSize = 0x1000000 (16777216)
+ CurrentTotalLogChunk = 9
+ ThreadsWithLogs = 3
+ Clock frequency = 3.392 GHz
+ Start time 15:26:31
+ Last message time 15:26:56
+ Total elapsed time 25.077 sec
+ .....................................
+ ---------------------------- 2407 total entries -----------------------------
+
+
+ SUCCESS: GCHist structures initialized
+
+\\
+
+COMMAND: histobjfind.
+!HistObjFind <obj_address>
+
+To examine log entries related to an object whose present address is known one
+would use this command. The output of this command contains all entries that
+reference the object:
+
+ 0:003> !HistObjFind 028970d4
+ GCCount Object Message
+ ---------------------------------------------------------
+ 2296 028970d4 Promotion for root 01e411b8 (MT = 5b6c5cd8)
+ 2296 028970d4 Relocation NEWVALUE for root 00223fc4
+ 2296 028970d4 Relocation NEWVALUE for root 01e411b8
+ ...
+ 2295 028970d4 Promotion for root 01e411b8 (MT = 5b6c5cd8)
+ 2295 028970d4 Relocation NEWVALUE for root 00223fc4
+ 2295 028970d4 Relocation NEWVALUE for root 01e411b8
+ ...
+
+\\
+
+COMMAND: histroot.
+!HistRoot <root>
+
+The root value obtained from !HistObjFind can be used to track the movement of
+an object through the GCs.
+
+HistRoot provides information related to both promotions and relocations of the
+root specified as the argument.
+
+ 0:003> !HistRoot 01e411b8
+ GCCount Value MT Promoted? Notes
+ ---------------------------------------------------------
+ 2296 028970d4 5b6c5cd8 yes
+ 2295 028970d4 5b6c5cd8 yes
+ 2294 028970d4 5b6c5cd8 yes
+ 2293 028970d4 5b6c5cd8 yes
+ 2292 028970d4 5b6c5cd8 yes
+ 2291 028970d4 5b6c5cd8 yes
+ 2290 028970d4 5b6c5cd8 yes
+ 2289 028970d4 5b6c5cd8 yes
+ 2288 028970d4 5b6c5cd8 yes
+ 2287 028970d4 5b6c5cd8 yes
+ 2286 028970d4 5b6c5cd8 yes
+ 2285 028970d4 5b6c5cd8 yes
+ 322 028970e8 5b6c5cd8 yes Duplicate promote/relocs
+ ...
+
+\\
+
+COMMAND: histobj.
+!HistObj <obj_address>
+
+This command examines all stress log relocation records and displays the chain
+of GC relocations that may have led to the address passed in as an argument.
+Conceptually the output is:
+
+ GenN obj_address root1, root2, root3,
+ GenN-1 prev_obj_addr root1, root2,
+ GenN-2 prev_prev_oa root1, root4,
+ ...
+
+Sample output:
+ 0:003> !HistObj 028970d4
+ GCCount Object Roots
+ ---------------------------------------------------------
+ 2296 028970d4 00223fc4, 01e411b8,
+ 2295 028970d4 00223fc4, 01e411b8,
+ 2294 028970d4 00223fc4, 01e411b8,
+ 2293 028970d4 00223fc4, 01e411b8,
+ 2292 028970d4 00223fc4, 01e411b8,
+ 2291 028970d4 00223fc4, 01e411b8,
+ 2290 028970d4 00223fc4, 01e411b8,
+ 2289 028970d4 00223fc4, 01e411b8,
+ 2288 028970d4 00223fc4, 01e411b8,
+ 2287 028970d4 00223fc4, 01e411b8,
+ 2286 028970d4 00223fc4, 01e411b8,
+ 2285 028970d4 00223fc4, 01e411b8,
+ 322 028970d4 01e411b8,
+ 0 028970d4
+
+\\
+
+COMMAND: histclear.
+!HistClear
+
+This command releases any resources used by the Hist-family of commands.
+Generally there's no need to call this explicitly, as each HistInit will first
+cleanup the previous resources.
+
+ 0:003> !HistClear
+ Completed successfully.
+
+\\
+
+COMMAND: dumprcw.
+!DumpRCW <RCW address>
+
+This command lists information about a Runtime Callable Wrapper. You can use
+!DumpObj to obtain the RCW address corresponding to a managed object.
+
+The output contains all COM interface pointers that the RCW holds on to, which
+is useful for investigating lifetime issues of interop-heavy applications.
+\\
+
+COMMAND: dumpccw.
+!DumpCCW <CCW address or COM IP>
+
+This command lists information about a COM Callable Wrapper. You can use
+!DumpObj to obtain the CCW address corresponding to a managed object or pass
+a COM interface pointer to which the object has been marshaled.
+
+The output contains the COM reference count of the CCW, which is useful for
+investigating lifetime issues of interop-heavy applications.
+\\
+
+COMMAND: suppressjitoptimization.
+!SuppressJitOptimization [on|off]
+
+!SuppressJitOptimization helps when you want the CLR to generate more debuggable
+jitted code. This will turn off inlining, enregistering of local variables,
+optimizing across sequence point boundaries, and precise variable lifetimes.
+The resulting code should step more cleanly and make local variables more
+accesible to inspection. The cost is that code quality is lower so your
+application may execute a little slower.
+
+Once you execute !SuppressJitOptimization on, all managed modules that load
+from that point onwards will not be optimized. The setting is only checked once
+per module, at the time that module loads. Changing the setting later will only
+affect modules that are loaded later. The recommendation is to set this once at
+app startup and let the same setting apply for all managed modules.
+\\
+
+COMMAND: stoponcatch.
+!StopOnCatch
+
+This commands sets a one-time breakpoint on the first managed catch clause
+entered by managed code. This will cause the debugger to stop on the first line
+of the catch handler. This is useful step from from the point of an exception
+throw to the catch handler.
+\\
+
+
+COMMAND: savestate.
+!SaveState <file_path>
+
+!SaveState serializes SOS managed breakpoints and watch values into a script file
+that can be executed on a future windbg session to restore them. Use the windbg
+command <$ <file_path> to execute the saved script.
+\\
+
+COMMAND: watch.
+!Watch
+!Watch -add <expression>
+!Watch -remove <index>
+!Watch -save <watch_list_name>
+!Watch -rename <old_watch_list_name> <new_watch_list_name>
+!Watch [-filter <watch_list_name>] [-expand <index> [type_cast]<expression>]
+
+!Watch displays the value of managed expressions, and manages the list of
+expressions. A quick example...
+
+DEBUGGEE CODE:
+ static int Main(string[] args)
+ {
+ int p14 = -123;
+ MyStruct ms = new MyStruct();
+ ms.a = p14;
+ ms.b = true;
+ MyStruct.c = "Foo";
+ ...
+
+COMMAND WINDOW:
+0:000> !Watch -add ms
+0:000> !Watch -add p14
+0:000> !Watch
+ 1) MyStruct ms @ 0x0093D1CC
+ 2) int p14 = -123
+
+ms in the command window will be a DML link, if you click on it then you see:
+
+COMMAND WINDOW:
+0:000> !watch -expand 1 (MyStruct)ms
+ 1) MyStruct ms @ 0x0093D1CC
+ |- int a = -123
+ |- bool b = true
+ |- string c = null
+ 2) int p14 = -123
+
+
+
+Watch is capable of parsing a variety of different expressions. Expressions
+can start with the name of a local variable or parameter in any stack frame.
+To access fields of classes and structures simply add a '.' and then the field
+name. To access array elements use standard [<index>] notation. In the above
+code example these would be valid expressions:
+args[19]
+p14
+ms.a
+
+Additionally 'this' is available in the leafmost frame to access fields.
+You can not use unqualified field names. For example if there is a class:
+class C
+{
+ int m_foo;
+ public void Hello() { Console.WriteLine(m_foo); }
+}
+
+this.m_foo is a legal expression when executing inside C.Hello(),
+m_foo would not work.
+
+Expressions also allow fully qualified type names and dereferencing static
+fields from those types using the same '.' syntax. For example:
+System.Int32
+System.Type.Missing
+
+Generic types can be used, though don't forget to use the CLI type
+name which adds `<number_of_generic_params> to the type name you would see
+in VB or C#. For example:
+System.Collections.Generic.Dictionary`2<System.String,System.Type>
+
+Dereferencing fields or array indices can recurse as you might expect. A
+made up example:
+foo.bar.baz[19][12].m_field[4]
+
+Property values and method invocations can not be used in expressions as
+the !Watch command will never execute any managed code to evaluate the
+expressions.
+
+To remove entries in the list use !Watch -remove <index> where index is
+the number printed next to the expression in the viewing list. In our
+initial example !Watch -remove 2 would remove the 'p14' expression from
+the list.
+
+Saving/Renaming/Filtering
+Sometimes it can be helpful to see only those values which have changed
+since some point in the past. As an example assume that MyStruct.c changes
+values during the go:
+
+0:000> !Watch -save myOldValues
+0:000> g
+...
+0:000> !Watch -filter myOldValues
+ 3) string MyStruct.c = "Foo"
+
+When saving a list of expressions, the string that would be displayed in a
+!Watch command is also saved. It is this string that is compared to determine
+if the value has changed. Some changes might not alter the console string
+representation. In the first example changing the value of ms.a would not
+cause the 'ms' expression to be different.
+
+Renaming can be useful in scripts. It allows the name of a saved watch list to
+change after saving it. For example:
+0:000> !Watch -rename myCurrentValues myOldValues
+0:000> !Watch -save myCurrentValues
+0:000> !Watch -filter myOldValues
+ 3) string MyStruct.c = "Foo"
+
+Placing that pattern inside a script shows the changed watch values
+since the last time the script was run.
+\\
+
diff --git a/src/ToolBox/SOS/Strike/data.h b/src/ToolBox/SOS/Strike/data.h
new file mode 100644
index 0000000000..9989038653
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/data.h
@@ -0,0 +1,51 @@
+// 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.
+
+// ==++==
+//
+
+//
+// ==--==
+#ifndef __data_h__
+#define __data_h__
+
+#include "cor.h"
+#include "corhdr.h"
+#include "cor.h"
+#include "dacprivate.h"
+
+BOOL FileExist (const char *filename);
+BOOL FileExist (const WCHAR *filename);
+
+// We use global variables
+// because move returns void if it fails
+//typedef DWORD DWORD_PTR;
+//typedef ULONG ULONG_PTR;
+
+// Max length in WCHAR for a buffer to store metadata name
+const int mdNameLen = 2048;
+extern WCHAR g_mdName[mdNameLen];
+
+const int nMDIMPORT = 128;
+struct MDIMPORT
+{
+ enum MDType {InMemory, InFile, Dynamic};
+ WCHAR *name;
+ size_t base; // base of the PE module
+ size_t mdBase; // base of the metadata
+ char *metaData;
+ ULONG metaDataSize;
+ MDType type;
+ IMetaDataImport *pImport;
+
+ MDIMPORT *left;
+ MDIMPORT *right;
+};
+
+class Module;
+
+extern "C" BOOL ControlC;
+extern IMetaDataDispenserEx *pDisp;
+
+#endif // __data_h__
diff --git a/src/ToolBox/SOS/Strike/datatarget.cpp b/src/ToolBox/SOS/Strike/datatarget.cpp
new file mode 100644
index 0000000000..8f24b7d841
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/datatarget.cpp
@@ -0,0 +1,215 @@
+// 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 "sos.h"
+#include "datatarget.h"
+#include "corhdr.h"
+#include "cor.h"
+#include "dacprivate.h"
+#include "sospriv.h"
+#include "corerror.h"
+
+#define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8)
+
+DataTarget::DataTarget(void) :
+ m_ref(0)
+{
+}
+
+STDMETHODIMP
+DataTarget::QueryInterface(
+ THIS_
+ ___in REFIID InterfaceId,
+ ___out PVOID* Interface
+ )
+{
+ if (InterfaceId == IID_IUnknown ||
+ InterfaceId == IID_ICLRDataTarget)
+ {
+ *Interface = (ICLRDataTarget*)this;
+ AddRef();
+ return S_OK;
+ }
+ else if (InterfaceId == IID_ICorDebugDataTarget4)
+ {
+ *Interface = (ICorDebugDataTarget4*)this;
+ AddRef();
+ return S_OK;
+ }
+ else
+ {
+ *Interface = NULL;
+ return E_NOINTERFACE;
+ }
+}
+
+STDMETHODIMP_(ULONG)
+DataTarget::AddRef(
+ THIS
+ )
+{
+ LONG ref = InterlockedIncrement(&m_ref);
+ return ref;
+}
+
+STDMETHODIMP_(ULONG)
+DataTarget::Release(
+ THIS
+ )
+{
+ LONG ref = InterlockedDecrement(&m_ref);
+ if (ref == 0)
+ {
+ delete this;
+ }
+ return ref;
+}
+
+HRESULT STDMETHODCALLTYPE
+DataTarget::GetMachineType(
+ /* [out] */ ULONG32 *machine)
+{
+ if (g_ExtControl == NULL)
+ {
+ return E_UNEXPECTED;
+ }
+ return g_ExtControl->GetExecutingProcessorType(machine);
+}
+
+HRESULT STDMETHODCALLTYPE
+DataTarget::GetPointerSize(
+ /* [out] */ ULONG32 *size)
+{
+#if defined(SOS_TARGET_AMD64) || defined(SOS_TARGET_ARM64)
+ *size = 8;
+#elif defined(SOS_TARGET_ARM)
+ *size = 4;
+#elif
+ #error Unsupported architecture
+#endif
+
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE
+DataTarget::GetImageBase(
+ /* [string][in] */ LPCWSTR name,
+ /* [out] */ CLRDATA_ADDRESS *base)
+{
+ if (g_ExtSymbols == NULL)
+ {
+ return E_UNEXPECTED;
+ }
+ CHAR lpstr[MAX_LONGPATH];
+ int name_length = WideCharToMultiByte(CP_ACP, 0, name, -1, lpstr, MAX_LONGPATH, NULL, NULL);
+ if (name_length == 0)
+ {
+ return E_FAIL;
+ }
+ return g_ExtSymbols->GetModuleByModuleName(lpstr, 0, NULL, base);
+}
+
+HRESULT STDMETHODCALLTYPE
+DataTarget::ReadVirtual(
+ /* [in] */ CLRDATA_ADDRESS address,
+ /* [length_is][size_is][out] */ PBYTE buffer,
+ /* [in] */ ULONG32 request,
+ /* [optional][out] */ ULONG32 *done)
+{
+ if (g_ExtData == NULL)
+ {
+ return E_UNEXPECTED;
+ }
+ return g_ExtData->ReadVirtual(address, (PVOID)buffer, request, done);
+}
+
+HRESULT STDMETHODCALLTYPE
+DataTarget::WriteVirtual(
+ /* [in] */ CLRDATA_ADDRESS address,
+ /* [size_is][in] */ PBYTE buffer,
+ /* [in] */ ULONG32 request,
+ /* [optional][out] */ ULONG32 *done)
+{
+ if (g_ExtData == NULL)
+ {
+ return E_UNEXPECTED;
+ }
+ return g_ExtData->WriteVirtual(address, (PVOID)buffer, request, done);
+}
+
+HRESULT STDMETHODCALLTYPE
+DataTarget::GetTLSValue(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 index,
+ /* [out] */ CLRDATA_ADDRESS* value)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+DataTarget::SetTLSValue(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 index,
+ /* [in] */ CLRDATA_ADDRESS value)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+DataTarget::GetCurrentThreadID(
+ /* [out] */ ULONG32* threadID)
+{
+ if (g_ExtSystem == NULL)
+ {
+ return E_UNEXPECTED;
+ }
+ return g_ExtSystem->GetCurrentThreadSystemId(threadID);
+}
+
+HRESULT STDMETHODCALLTYPE
+DataTarget::GetThreadContext(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 contextFlags,
+ /* [in] */ ULONG32 contextSize,
+ /* [out, size_is(contextSize)] */ PBYTE context)
+{
+ if (g_ExtSystem == NULL)
+ {
+ return E_UNEXPECTED;
+ }
+ return g_ExtSystem->GetThreadContextById(threadID, contextFlags, contextSize, context);
+}
+
+HRESULT STDMETHODCALLTYPE
+DataTarget::SetThreadContext(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 contextSize,
+ /* [out, size_is(contextSize)] */ PBYTE context)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+DataTarget::Request(
+ /* [in] */ ULONG32 reqCode,
+ /* [in] */ ULONG32 inBufferSize,
+ /* [size_is][in] */ BYTE *inBuffer,
+ /* [in] */ ULONG32 outBufferSize,
+ /* [size_is][out] */ BYTE *outBuffer)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+DataTarget::VirtualUnwind(
+ /* [in] */ DWORD threadId,
+ /* [in] */ ULONG32 contextSize,
+ /* [in, out, size_is(contextSize)] */ PBYTE context)
+{
+ if (g_ExtServices == NULL)
+ {
+ return E_UNEXPECTED;
+ }
+ return g_ExtServices->VirtualUnwind(threadId, contextSize, context);
+}
diff --git a/src/ToolBox/SOS/Strike/datatarget.h b/src/ToolBox/SOS/Strike/datatarget.h
new file mode 100644
index 0000000000..0293bc668b
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/datatarget.h
@@ -0,0 +1,90 @@
+// 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.
+
+class DataTarget : public ICLRDataTarget, ICorDebugDataTarget4
+{
+private:
+ LONG m_ref; // Reference count.
+
+public:
+ DataTarget(void);
+ virtual ~DataTarget() {}
+
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ ___in REFIID InterfaceId,
+ ___out PVOID* Interface
+ );
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ );
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ );
+
+ //
+ // ICLRDataTarget.
+ //
+
+ virtual HRESULT STDMETHODCALLTYPE GetMachineType(
+ /* [out] */ ULONG32 *machine);
+
+ virtual HRESULT STDMETHODCALLTYPE GetPointerSize(
+ /* [out] */ ULONG32 *size);
+
+ virtual HRESULT STDMETHODCALLTYPE GetImageBase(
+ /* [string][in] */ LPCWSTR name,
+ /* [out] */ CLRDATA_ADDRESS *base);
+
+ virtual HRESULT STDMETHODCALLTYPE ReadVirtual(
+ /* [in] */ CLRDATA_ADDRESS address,
+ /* [length_is][size_is][out] */ PBYTE buffer,
+ /* [in] */ ULONG32 request,
+ /* [optional][out] */ ULONG32 *done);
+
+ virtual HRESULT STDMETHODCALLTYPE WriteVirtual(
+ /* [in] */ CLRDATA_ADDRESS address,
+ /* [size_is][in] */ PBYTE buffer,
+ /* [in] */ ULONG32 request,
+ /* [optional][out] */ ULONG32 *done);
+
+ virtual HRESULT STDMETHODCALLTYPE GetTLSValue(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 index,
+ /* [out] */ CLRDATA_ADDRESS* value);
+
+ virtual HRESULT STDMETHODCALLTYPE SetTLSValue(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 index,
+ /* [in] */ CLRDATA_ADDRESS value);
+
+ virtual HRESULT STDMETHODCALLTYPE GetCurrentThreadID(
+ /* [out] */ ULONG32* threadID);
+
+ virtual HRESULT STDMETHODCALLTYPE GetThreadContext(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 contextFlags,
+ /* [in] */ ULONG32 contextSize,
+ /* [out, size_is(contextSize)] */ PBYTE context);
+
+ virtual HRESULT STDMETHODCALLTYPE SetThreadContext(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 contextSize,
+ /* [in, size_is(contextSize)] */ PBYTE context);
+
+ virtual HRESULT STDMETHODCALLTYPE Request(
+ /* [in] */ ULONG32 reqCode,
+ /* [in] */ ULONG32 inBufferSize,
+ /* [size_is][in] */ BYTE *inBuffer,
+ /* [in] */ ULONG32 outBufferSize,
+ /* [size_is][out] */ BYTE *outBuffer);
+
+ // ICorDebugDataTarget4
+
+ virtual HRESULT STDMETHODCALLTYPE VirtualUnwind(
+ /* [in] */ DWORD threadId,
+ /* [in] */ ULONG32 contextSize,
+ /* [in, out, size_is(contextSize)] */ PBYTE context);
+}; \ No newline at end of file
diff --git a/src/ToolBox/SOS/Strike/dirs.proj b/src/ToolBox/SOS/Strike/dirs.proj
new file mode 100644
index 0000000000..1e391307aa
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/dirs.proj
@@ -0,0 +1,20 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+
+ <PropertyGroup>
+ <BuildInPhase1>true</BuildInPhase1>
+ <BuildInPhaseDefault>false</BuildInPhaseDefault>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ </PropertyGroup>
+
+ <ItemGroup Condition="'$(BuildExePhase)' == '1'">
+ <ProjectFile Condition="'$(BuildForCoreSystem)'!='true'" Include="sos.nativeproj" />
+ <ProjectFile Condition="'$(BuildArchitecture)' == 'arm'" Include="sosx86\sosx86.nativeproj" />
+ <ProjectFile Condition="'$(BuildArchitecture)' == 'i386' and '$(BuildForCoreSystem)' == 'true'" Include="sosx86\sosx86.nativeproj" />
+ <ProjectFile Condition="'$(BuildArchitecture)' == 'amd64' and '$(BuildForCoreSystem)' == 'true'" Include="sosx64\sosx64.nativeproj" />
+ <ProjectFile Condition="'$(BuildArchitecture)' == 'arm64' and '$(BuildForCoreSystem)' == 'true'" Include="sosx64\sosx64.nativeproj" />
+ </ItemGroup>
+
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/ToolBox/SOS/Strike/disasm.cpp b/src/ToolBox/SOS/Strike/disasm.cpp
new file mode 100644
index 0000000000..e141f8038f
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/disasm.cpp
@@ -0,0 +1,1142 @@
+// 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 "strike.h"
+#include "gcinfo.h"
+#include "util.h"
+#include <dbghelp.h>
+#include <limits.h>
+
+#include "sos_md.h"
+
+#ifdef SOS_TARGET_X86
+namespace X86GCDump
+{
+#include "gcdump.h"
+#undef assert
+#define assert(a)
+#define CONTRACTL
+#define DAC_ARG(x)
+#define CONTRACTL_END
+#define LIMITED_METHOD_CONTRACT
+#define NOTHROW
+#define GC_NOTRIGGER
+#define SUPPORTS_DAC
+#define LIMITED_METHOD_DAC_CONTRACT
+#include "gcdecoder.cpp"
+#undef CONTRACTL
+#undef CONTRACTL_END
+#undef LIMITED_METHOD_CONTRACT
+#undef NOTHROW
+#undef GC_NOTRIGGER
+#undef _ASSERTE
+#define _ASSERTE(a) do {} while (0)
+
+#include "gcdump.cpp"
+#include "i386/gcdumpx86.cpp"
+}
+#endif // SOS_TARGET_X86
+
+#ifdef SOS_TARGET_AMD64
+#include "gcdump.h"
+#define DAC_ARG(x)
+#define SUPPORTS_DAC
+#define LIMITED_METHOD_DAC_CONTRACT
+#undef LIMITED_METHOD_CONTRACT
+#undef PREGDISPLAY
+ #ifdef LOG
+ #undef LOG
+ #endif
+ #define LOG(x) ((void)0)
+ #ifdef LOG_PIPTR
+ #undef LOG_PIPTR
+ #endif
+ #define LOG_PIPTR(pObjRef, gcFlags, hCallBack) ((void)0)
+#include "gcdumpnonx86.cpp"
+#endif // SOS_TARGET_AMD64
+
+#include "disasm.h"
+
+#ifndef ERANGE
+#define ERANGE 34
+#endif
+
+PVOID
+GenOpenMapping(
+ PCSTR FilePath,
+ PULONG Size
+ )
+{
+#ifndef FEATURE_PAL
+ HANDLE hFile;
+ HANDLE hMappedFile;
+ PVOID MappedFile;
+
+ hFile = CreateFileA(
+ FilePath,
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL
+ );
+#if 0
+ if ( hFile == NULL || hFile == INVALID_HANDLE_VALUE ) {
+
+ if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
+
+ // We're on an OS that doesn't support Unicode
+ // file operations. Convert to ANSI and see if
+ // that helps.
+
+ CHAR FilePathA [ MAX_LONGPATH + 10 ];
+
+ if (WideCharToMultiByte (CP_ACP,
+ 0,
+ FilePath,
+ -1,
+ FilePathA,
+ sizeof (FilePathA),
+ 0,
+ 0
+ ) > 0) {
+
+ hFile = CreateFileA(FilePathA,
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL
+ );
+ }
+ }
+
+ if ( hFile == NULL || hFile == INVALID_HANDLE_VALUE ) {
+ return NULL;
+ }
+ }
+#endif
+
+ *Size = GetFileSize(hFile, NULL);
+ if (*Size == ULONG_MAX) {
+ CloseHandle( hFile );
+ return NULL;
+ }
+
+ hMappedFile = CreateFileMapping (
+ hFile,
+ NULL,
+ PAGE_READONLY,
+ 0,
+ 0,
+ NULL
+ );
+
+ if ( !hMappedFile ) {
+ CloseHandle ( hFile );
+ return NULL;
+ }
+
+ MappedFile = MapViewOfFile (
+ hMappedFile,
+ FILE_MAP_READ,
+ 0,
+ 0,
+ 0
+ );
+
+ CloseHandle (hMappedFile);
+ CloseHandle (hFile);
+
+ return MappedFile;
+#else // FEATURE_PAL
+ return NULL;
+#endif // FEATURE_PAL
+}
+
+char* PrintOneLine (__in_z char *begin, __in_z char *limit)
+{
+ if (begin == NULL || begin >= limit) {
+ return NULL;
+ }
+ char line[128];
+ size_t length;
+ char *end;
+ while (1) {
+ if (IsInterrupt())
+ return NULL;
+ length = strlen (begin);
+ end = strstr (begin, "\r\xa");
+ if (end == NULL) {
+ ExtOut ("%s", begin);
+ end = begin+length+1;
+ if (end >= limit) {
+ return NULL;
+ }
+ }
+ else {
+ end += 2;
+ length = end-begin;
+ while (length) {
+ if (IsInterrupt())
+ return NULL;
+ size_t n = length;
+ if (n > 127) {
+ n = 127;
+ }
+ strncpy_s (line,_countof(line), begin, n);
+ line[n] = '\0';
+ ExtOut ("%s", line);
+ begin += n;
+ length -= n;
+ }
+ return end;
+ }
+ }
+}
+
+void UnassemblyUnmanaged(DWORD_PTR IP, BOOL bSuppressLines)
+{
+ char filename[MAX_PATH_FNAME+1];
+ char line[256];
+ int lcount = 10;
+
+ ULONG linenum = 0;
+ ULONG64 Displacement = 0;
+ BOOL fLineAvailable = FALSE;
+ ULONG64 vIP = 0;
+
+ if (!bSuppressLines)
+ {
+ ReloadSymbolWithLineInfo();
+ fLineAvailable = SUCCEEDED (g_ExtSymbols->GetLineByOffset(TO_CDADDR(IP),
+ &linenum,
+ filename,
+ MAX_PATH_FNAME+1,
+ NULL,
+ &Displacement));
+ }
+ ULONG FileLines = 0;
+ ArrayHolder<ULONG64> Buffer = NULL;
+
+ if (fLineAvailable)
+ {
+ g_ExtSymbols->GetSourceFileLineOffsets(filename, NULL, 0, &FileLines);
+ if (FileLines == 0xFFFFFFFF || FileLines == 0)
+ fLineAvailable = FALSE;
+ }
+
+ if (fLineAvailable)
+ {
+ Buffer = new ULONG64[FileLines];
+ if (Buffer == NULL)
+ fLineAvailable = FALSE;
+ }
+
+ if (!fLineAvailable)
+ {
+ vIP = TO_CDADDR(IP);
+ // There is no line info. Just disasm the code.
+ while (lcount-- > 0)
+ {
+ if (IsInterrupt())
+ return;
+ g_ExtControl->Disassemble (vIP, 0, line, 256, NULL, &vIP);
+ ExtOut (line);
+ }
+ return;
+ }
+
+ g_ExtSymbols->GetSourceFileLineOffsets(filename, Buffer, FileLines, NULL);
+
+ int beginLine = 0;
+ int endLine = 0;
+ int lastLine;
+ linenum --;
+ for (lastLine = linenum; lastLine >= 0; lastLine --) {
+ if (IsInterrupt())
+ return;
+ if (Buffer[lastLine] != DEBUG_INVALID_OFFSET) {
+ g_ExtSymbols->GetNameByOffset(Buffer[lastLine], NULL, 0, NULL, &Displacement);
+ if (Displacement == 0) {
+ beginLine = lastLine;
+ break;
+ }
+ }
+ }
+ if (lastLine < 0) {
+ int n = lcount / 2;
+ lastLine = linenum-1;
+ beginLine = lastLine;
+ while (lastLine >= 0) {
+ if (IsInterrupt())
+ return;
+ if (Buffer[lastLine] != DEBUG_INVALID_OFFSET) {
+ beginLine = lastLine;
+ n --;
+ if (n == 0) {
+ break;
+ }
+ }
+ lastLine --;
+ }
+ }
+ while (beginLine > 0 && Buffer[beginLine-1] == DEBUG_INVALID_OFFSET) {
+ if (IsInterrupt())
+ return;
+ beginLine --;
+ }
+ int endOfFunc = 0;
+ for (lastLine = linenum+1; (ULONG)lastLine < FileLines; lastLine ++) {
+ if (IsInterrupt())
+ return;
+ if (Buffer[lastLine] != DEBUG_INVALID_OFFSET) {
+ g_ExtSymbols->GetNameByOffset(Buffer[lastLine], NULL, 0, NULL, &Displacement);
+ if (Displacement == 0) {
+ endLine = lastLine;
+ break;
+ }
+ endOfFunc = lastLine;
+ }
+ }
+ if ((ULONG)lastLine == FileLines) {
+ int n = lcount / 2;
+ lastLine = linenum+1;
+ endLine = lastLine;
+ while ((ULONG)lastLine < FileLines) {
+ if (IsInterrupt())
+ return;
+ if (Buffer[lastLine] != DEBUG_INVALID_OFFSET) {
+ endLine = lastLine;
+ n --;
+ if (n == 0) {
+ break;
+ }
+ }
+ lastLine ++;
+ }
+ }
+
+ PVOID MappedBase = NULL;
+ ULONG MappedSize = 0;
+
+ class ToUnmap
+ {
+ PVOID *m_Base;
+ public:
+ ToUnmap (PVOID *base)
+ :m_Base(base)
+ {}
+ ~ToUnmap ()
+ {
+ if (*m_Base) {
+ UnmapViewOfFile (*m_Base);
+ *m_Base = NULL;
+ }
+ }
+ };
+ ToUnmap toUnmap(&MappedBase);
+
+#define MAX_SOURCE_PATH 1024
+ char Found[MAX_SOURCE_PATH];
+ char *pFile;
+ if (g_ExtSymbols->FindSourceFile(0,
+ filename,
+ DEBUG_FIND_SOURCE_BEST_MATCH | DEBUG_FIND_SOURCE_FULL_PATH,
+ NULL,
+ Found,
+ sizeof(Found),
+ NULL) != S_OK)
+ {
+ pFile = filename;
+ }
+ else
+ {
+ MappedBase = GenOpenMapping(Found, &MappedSize);
+ pFile = Found;
+ }
+
+ lastLine = beginLine;
+ char *pFileCh = (char*)MappedBase;
+ if (MappedBase) {
+ ExtOut ("%s\n", pFile);
+ int n = beginLine;
+ while (n > 0) {
+ while (!(pFileCh[0] == '\r' && pFileCh[1] == 0xa)) {
+ if (IsInterrupt())
+ return;
+ pFileCh ++;
+ }
+ pFileCh += 2;
+ n --;
+ }
+ }
+
+ char filename1[MAX_PATH_FNAME+1];
+ for (lastLine = beginLine; lastLine < endLine; lastLine ++) {
+ if (IsInterrupt())
+ return;
+ if (MappedBase) {
+ ExtOut("%4d ", lastLine+1);
+ pFileCh = PrintOneLine(pFileCh, (char*)MappedBase+MappedSize);
+ }
+ if (Buffer[lastLine] != DEBUG_INVALID_OFFSET) {
+ if (MappedBase == 0) {
+ ExtOut (">>> %s:%d\n", pFile, lastLine+1);
+ }
+ vIP = Buffer[lastLine];
+ ULONG64 vNextLineIP;
+ int i;
+ for (i = lastLine + 1; (ULONG)i < FileLines && Buffer[i] == DEBUG_INVALID_OFFSET; i ++) {
+ if (IsInterrupt())
+ return;
+ }
+ if ((ULONG)i == FileLines) {
+ vNextLineIP = 0;
+ }
+ else
+ vNextLineIP = Buffer[i];
+ while (1) {
+ if (IsInterrupt())
+ return;
+ g_ExtControl->Disassemble(vIP, 0, line, 256, NULL, &vIP);
+ ExtOut (line);
+ if (vIP > vNextLineIP || vNextLineIP - vIP > 40) {
+ if (FAILED (g_ExtSymbols->GetLineByOffset(vIP, &linenum,
+ filename1,
+ MAX_PATH_FNAME+1,
+ NULL,
+ &Displacement))) {
+ if (lastLine != endOfFunc) {
+ break;
+ }
+ if (strstr (line, "ret") || strstr (line, "jmp")) {
+ break;
+ }
+ }
+
+ if (linenum != (ULONG)lastLine+1 || strcmp (filename, filename1)) {
+ break;
+ }
+ }
+ else if (vIP == vNextLineIP) {
+ break;
+ }
+ }
+ }
+ }
+}
+
+void DisasmAndClean (DWORD_PTR &IP, __out_ecount_opt(length) char *line, ULONG length)
+{
+ ULONG64 vIP = TO_CDADDR(IP);
+ g_ExtControl->Disassemble (vIP, 0, line, length, NULL, &vIP);
+ IP = (DWORD_PTR)vIP;
+ // remove the ending '\n'
+ char *ptr = strrchr (line, '\n');
+ if (ptr != NULL)
+ ptr[0] = '\0';
+}
+
+// If byref, move to pass the byref prefix
+BOOL IsByRef (__deref_inout_z char *& ptr)
+{
+ BOOL bByRef = FALSE;
+ const char* qindirCh = "qword ptr [";
+ const char* dindirCh = "dword ptr [";
+ const char* qindirDsCh = "qword ptr ds:[";
+ const char* dindirDsCh = "dword ptr ds:[";
+ if (ptr[0] == '[')
+ {
+ bByRef = TRUE;
+ ptr ++;
+ }
+ else if (!IsDbgTargetArm() && !strncmp (ptr, IsDbgTargetWin64() ? qindirCh : dindirCh, 11))
+ {
+ bByRef = TRUE;
+ ptr += 11;
+ }
+ // The new disassembly engine for windbg formats indirect calls
+ // slightly differently:
+ else if (!IsDbgTargetArm() && !strncmp (ptr, IsDbgTargetWin64() ? qindirDsCh : dindirDsCh, 14))
+ {
+ bByRef = TRUE;
+ ptr += 14;
+ }
+ return bByRef;
+}
+
+BOOL IsTermSep (char ch)
+{
+ return (ch == '\0' || isspace (ch) || ch == ',' || ch == '\n');
+}
+
+// Find next term. A term is seperated by space or ,
+void NextTerm (__deref_inout_z char *& ptr)
+{
+ // If we have a byref, skip to ']'
+ if (IsByRef (ptr))
+ {
+ while (ptr[0] != ']' && ptr[0] != '\0')
+ {
+ if (IsInterrupt())
+ return;
+ ptr ++;
+ }
+ if (ptr[0] == ']')
+ ptr ++;
+ }
+
+ while (!IsTermSep (ptr[0]))
+ {
+ if (IsInterrupt())
+ return;
+ ptr ++;
+ }
+
+ while (IsTermSep(ptr[0]) && (*ptr != '\0'))
+ {
+ if (IsInterrupt())
+ return;
+ ptr ++;
+ }
+}
+
+
+// Parses something like 6e24d310, 0x6e24d310, or 6e24d310h.
+// On 64-bit, also parses things like 000006fb`f9b70f50 and
+// 000006fbf9b70f50 (as well as their 0x-prefix, -h suffix variations).
+INT_PTR ParseHexNumber (__in_z char *ptr, ___out char **endptr)
+{
+ char *endptr1;
+ INT_PTR value1 = strtoul(ptr, &endptr1, 16);
+
+#ifdef _TARGET_WIN64_
+ if ('`' == endptr1[0] && isxdigit(endptr1[1]))
+ {
+ char *endptr2;
+ INT_PTR value2 = strtoul(endptr1+1, &endptr2, 16);
+
+ value1 = (value1 << 32) | value2;
+ endptr1 = endptr2;
+ }
+ // if the hex number was specified as 000006fbf9b70f50, an overflow occurred
+ else if (ULONG_MAX == value1 && errno == ERANGE)
+ {
+ if (!strncmp(ptr, "0x", 2))
+ ptr += 2;
+
+ char savedigit = ptr[8];
+ ptr[8] = '\0';
+
+ value1 = strtoul(ptr, &endptr1, 16);
+
+ ptr[8] = savedigit;
+
+ char *endptr2;
+ INT_PTR value2 = strtoul(ptr+8, &endptr2, 16);
+
+ size_t ndigits2 = endptr2 - (ptr+8);
+
+ value1 = (value1 << (ndigits2*4)) | value2;
+ endptr1 = endptr2;
+ }
+#endif // _TARGET_WIN64_
+
+ // account for the possible 'h' suffix
+ if ((*endptr1 == 'h') || (*endptr1 == 'H'))
+ {
+ ++endptr1;
+ }
+
+ *endptr = endptr1;
+ return value1;
+}
+
+
+// only handle pure value, or memory address
+INT_PTR GetValueFromExpr(__in_z char *ptr, INT_PTR &value)
+{
+ BOOL bNegative = FALSE;
+ value = 0;
+ char *myPtr = ptr;
+ BOOL bByRef = IsByRef (myPtr);
+
+ // ARM disassembly contains '#' prefixes for hex constants
+ if (*myPtr == '#')
+ ++myPtr;
+
+ if (myPtr[0] == '-')
+ {
+ myPtr ++;
+ bNegative = TRUE;
+ }
+ if (!strncmp (myPtr, "0x", 2) || isxdigit (myPtr[0]))
+ {
+ char *endptr;
+ value = ParseHexNumber(myPtr, &endptr);
+ if ((!bByRef && IsTermSep(endptr[0])) || (bByRef && endptr[0] == ']'))
+ {
+ if (bNegative)
+ value = -value;
+ ptr = endptr;
+ if (bByRef)
+ {
+ ptr += 1;
+ SafeReadMemory (TO_TADDR(value), &value, 4, NULL);
+ }
+ return ptr - myPtr;
+ }
+ }
+
+ // handle mscorlib+0xed310 (6e24d310)
+ if (!bByRef)
+ {
+ ptr = myPtr;
+ // handle 'offset ' before the expression:
+ if (strncmp(ptr, "offset ", 7) == 0)
+ {
+ ptr += 7;
+ }
+ while (ptr[0] != ' ' && ptr[0] != '+' && ptr[0] != '\0')
+ {
+ if (IsInterrupt())
+ return 0;
+ ptr ++;
+ }
+ if (ptr[0] == '+')
+ {
+ NextTerm (ptr);
+ if (ptr[0] == '(')
+ {
+ ptr ++;
+ char *endptr;
+ value = ParseHexNumber(ptr, &endptr);
+ if (endptr[0] == ')')
+ {
+ ptr ++;
+ return ptr - myPtr;
+ }
+ }
+ }
+ }
+ if (bByRef)
+ {
+ // handle dword [mscorlib+0x2bd788 (02ead788)]
+ ptr = myPtr;
+ // handle 'offset ' before the expression:
+ if (strncmp(ptr, "offset ", 7) == 0)
+ {
+ ptr += 7;
+ }
+ while (ptr[0] != '(' && ptr[0] != '\0')
+ {
+ if (IsInterrupt())
+ return 0;
+ ptr ++;
+ }
+ if (ptr[0] == '(')
+ {
+ ptr ++;
+ char *endptr;
+ value = ParseHexNumber(ptr, &endptr);
+ if (endptr[0] == ')' && endptr[1] == ']')
+ {
+ ptr = endptr + 2;
+ SafeReadMemory (TO_TADDR(value), &value, 4, NULL);
+ return ptr - myPtr;
+ }
+ }
+ }
+
+#ifdef _TARGET_WIN64_
+ // handle CLRStub@7fffc8601cc (000007fffc8601cc)
+ if (!bByRef && !strncmp(myPtr, "CLRStub[", 8))
+ {
+ ptr = myPtr;
+ while (ptr[0] != '(' && ptr[0] != '\0')
+ {
+ if (IsInterrupt())
+ return 0;
+ ptr ++;
+ }
+ if (ptr[0] == '(')
+ {
+ ptr ++;
+ char *endptr;
+ value = ParseHexNumber(ptr, &endptr);
+ if (endptr[0] == ')')
+ {
+ ptr ++;
+ return ptr - myPtr;
+ }
+ }
+ }
+#endif // _TARGET_WIN64_
+
+ return 0;
+}
+
+
+const char * HelperFuncName (size_t IP)
+{
+ static char s_szHelperName[100];
+ if (S_OK == g_sos->GetJitHelperFunctionName(IP, sizeof(s_szHelperName), &s_szHelperName[0], NULL))
+ return &s_szHelperName[0];
+ else
+ return NULL;
+}
+
+
+// Returns:
+// NULL if the EHInfo passed in does not refer to a Typed clause
+// "..." if pEHInfo->isCatchAllHandler is TRUE
+// "TypeName" if pEHInfo is a DACEHInfo*.
+// Note:
+// The return is a pointer to a global buffer, therefore this value must
+// be consumed as soon as possible after a call to this function.
+LPCWSTR EHTypedClauseTypeName(___in const DACEHInfo* pEHInfo)
+{
+ _ASSERTE(pEHInfo != NULL);
+ if ((pEHInfo->clauseType == EHTyped) && pEHInfo->isCatchAllHandler)
+ {
+ return W("...");
+ }
+
+ // is there a method table or a token to look at?
+ if (pEHInfo->clauseType == EHTyped)
+ {
+ TADDR mt;
+ if (pEHInfo->moduleAddr == 0)
+ {
+ mt = TO_TADDR(pEHInfo->mtCatch);
+ NameForMT_s(mt, g_mdName, mdNameLen);
+ } else {
+ PrettyPrintClassFromToken(TO_TADDR(pEHInfo->moduleAddr), pEHInfo->tokCatch, g_mdName, mdNameLen, FormatCSharp);
+ }
+ return g_mdName;
+ }
+
+ return NULL;
+}
+
+BOOL IsClonedFinally(DACEHInfo *pEHInfo)
+{
+ // This maybe should be determined in the VM and passed in the DACEHInfo struct.
+#if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
+ return ((pEHInfo->tryStartOffset == pEHInfo->tryEndOffset) &&
+ (pEHInfo->tryStartOffset == pEHInfo->handlerStartOffset) &&
+ (pEHInfo->clauseType == EHFinally) &&
+ pEHInfo->isDuplicateClause);
+#else
+ return FALSE;
+#endif
+}
+
+
+void SOSEHInfo::FormatForDisassembly(CLRDATA_ADDRESS offSet)
+{
+ LPCWSTR typeName = NULL;
+ // the order of printing and iterating will matter on the boundaries
+
+ // Print END tags in forward order (most nested to least nested). However, cloned
+ // finally clauses are always at the end, but they should be considered most nested,
+ // so have a separate loop to output them first.
+ for (UINT i=0; i < EHCount; i++)
+ {
+ DACEHInfo *pCur = &m_pInfos[i];
+
+ if (IsClonedFinally(pCur) &&
+ (offSet == pCur->handlerEndOffset))
+ {
+ ExtOut ("EHHandler %d: CLONED FINALLY END\n", i);
+ }
+ }
+
+ for (UINT i=0; i < EHCount; i++)
+ {
+ DACEHInfo *pCur = &m_pInfos[i];
+
+ if (pCur->isDuplicateClause)
+ {
+ // Don't print anything for duplicate clauses
+ continue;
+ }
+
+ if (offSet == pCur->tryEndOffset)
+ {
+ ExtOut ("EHHandler %d: %s CLAUSE END\n", i, EHTypeName(pCur->clauseType));
+ }
+
+ if (offSet == pCur->handlerEndOffset)
+ {
+ ExtOut ("EHHandler %d: %s HANDLER END\n", i, EHTypeName(pCur->clauseType));
+ }
+ }
+
+ // Print BEGIN tags in reverse order (least nested to most nested).
+ for (UINT i=EHCount-1; i != (UINT)-1; --i)
+ {
+ DACEHInfo *pCur = &m_pInfos[i];
+
+ // Must do this before the isDuplicatedClause check, since these are marked as duplicated clauses.
+ if (IsClonedFinally(pCur) &&
+ (offSet == pCur->handlerStartOffset))
+ {
+ ExtOut ("EHHandler %d: CLONED FINALLY BEGIN\n", i);
+ }
+
+ if (pCur->isDuplicateClause)
+ {
+ // Don't print anything for duplicate clauses
+ continue;
+ }
+
+ if (offSet == pCur->tryStartOffset)
+ {
+ ExtOut ("EHHandler %d: %s CLAUSE BEGIN", i, EHTypeName(pCur->clauseType));
+ typeName = EHTypedClauseTypeName(pCur);
+ if (typeName != NULL)
+ {
+ ExtOut(" catch(%S) ", typeName);
+ }
+ ExtOut ("\n");
+ }
+
+ if (offSet == pCur->handlerStartOffset)
+ {
+ ExtOut ("EHHandler %d: %s HANDLER BEGIN", i, EHTypeName(pCur->clauseType));
+ typeName = EHTypedClauseTypeName(pCur);
+ if (typeName != NULL)
+ {
+ ExtOut(" catch(%S) ", typeName);
+ }
+ ExtOut ("\n");
+ }
+
+ if ((pCur->clauseType == EHFilter) &&
+ (offSet == pCur->filterOffset))
+ {
+ ExtOut ("EHHandler %d: %s FILTER BEGIN\n",i, EHTypeName(pCur->clauseType));
+ }
+ }
+}
+
+
+//
+// Implementation shared by X86, ARM, and X64
+// Any cross platform code should resolve through g_targetMachine or should
+// use the IS_DBG_TARGET_XYZ macro.
+//
+
+void PrintNativeStack(DWORD_PTR ip, BOOL bSuppressLines)
+{
+ char filename[MAX_PATH_FNAME + 1];
+ char symbol[1024];
+ ULONG64 displacement;
+
+ HRESULT hr = g_ExtSymbols->GetNameByOffset(TO_CDADDR(ip), symbol, _countof(symbol), NULL, &displacement);
+ if (SUCCEEDED(hr) && symbol[0] != '\0')
+ {
+ ExtOut("%s", symbol);
+
+ if (displacement)
+ {
+ ExtOut(" + %#x", displacement);
+ }
+
+ if (!bSuppressLines)
+ {
+ ULONG line;
+ hr = g_ExtSymbols->GetLineByOffset(TO_CDADDR(ip), &line, filename, _countof(filename), NULL, NULL);
+ if (SUCCEEDED(hr))
+ {
+ ExtOut(" [%s:%d]", filename, line);
+ }
+ }
+ }
+ else
+ {
+ DMLOut(DMLIP(ip));
+ }
+}
+
+// Return TRUE if we have printed something.
+BOOL PrintCallInfo(DWORD_PTR vEBP, DWORD_PTR IP, DumpStackFlag& DSFlag, BOOL bSymbolOnly)
+{
+ char Symbol[1024];
+ char filename[MAX_PATH_FNAME+1];
+ ULONG64 Displacement;
+ BOOL bOutput = FALSE;
+
+ // degrade gracefully for debuggees that don't have a runtime loaded, or a DAC available
+ DWORD_PTR methodDesc = 0;
+ if (!g_bDacBroken)
+ {
+ methodDesc = FunctionType (IP);
+ }
+
+ if (methodDesc > 1)
+ {
+ bOutput = TRUE;
+ if (!bSymbolOnly)
+ DMLOut("%p %s ", SOS_PTR(vEBP), DMLIP(IP));
+ DMLOut("(MethodDesc %s ", DMLMethodDesc(methodDesc));
+
+ // TODO: Microsoft, more checks to make sure method is not eeimpl, etc. Add this field to MethodDesc
+
+ DacpCodeHeaderData codeHeaderData;
+ if (codeHeaderData.Request(g_sos, TO_CDADDR(IP)) == S_OK)
+ {
+ DWORD_PTR IPBegin = (DWORD_PTR) codeHeaderData.MethodStart;
+ methodDesc = (DWORD_PTR) codeHeaderData.MethodDescPtr;
+ Displacement = IP - IPBegin;
+ if (IP >= IPBegin && Displacement <= codeHeaderData.MethodSize)
+ ExtOut ("+ %#x ", Displacement);
+ }
+ if (NameForMD_s(methodDesc, g_mdName, mdNameLen))
+ {
+ ExtOut("%S)", g_mdName);
+ }
+ else
+ {
+ ExtOut("%s)", DMLIP(IP));
+ }
+ }
+ else
+ {
+ if (!DSFlag.fEEonly)
+ {
+ bOutput = TRUE;
+ const char *name;
+ if (!bSymbolOnly)
+ DMLOut("%p %s ", SOS_PTR(vEBP), DMLIP(IP));
+
+ // if AMD64 ever becomes a cross platform target this must be resolved through
+ // virtual dispatch rather than conditional compilation
+#ifdef _TARGET_AMD64_
+ // degrade gracefully for debuggees that don't have a runtime loaded, or a DAC available
+ eTargetType ett = ettUnk;
+ if (!g_bDacBroken)
+ {
+ DWORD_PTR finalMDorIP = 0;
+ ett = GetFinalTarget(IP, &finalMDorIP);
+ if (ett == ettNative || ett==ettJitHelp)
+ {
+ methodDesc = 0;
+ IP = finalMDorIP;
+ }
+ else
+ {
+ methodDesc = finalMDorIP;
+ }
+ }
+#endif // _TARGET_AMD64_
+ if (methodDesc == 0)
+ {
+ PrintNativeStack(IP, DSFlag.fSuppressSrcInfo);
+ }
+ else if (g_bDacBroken)
+ {
+ // degrade gracefully for debuggees that don't have a runtime loaded, or a DAC available
+ DMLOut(DMLIP(IP));
+ }
+ else if (IsMethodDesc (IP))
+ {
+ NameForMD_s(IP, g_mdName, mdNameLen);
+ ExtOut(" (stub for %S)", g_mdName);
+ }
+ else if (IsMethodDesc(IP+5)) {
+ NameForMD_s((DWORD_PTR)(IP+5), g_mdName, mdNameLen);
+ DMLOut("%s (MethodDesc %s %S)", DMLIP(IP), DMLMethodDesc(IP+5), g_mdName);
+ }
+ else if ((name = HelperFuncName(IP)) != NULL) {
+ ExtOut(" (JitHelp: %s)", name);
+ }
+#ifdef _TARGET_AMD64_
+ else if (ett == ettMD || ett == ettStub)
+ {
+ NameForMD_s(methodDesc, g_mdName,mdNameLen);
+ DMLOut("%s (stub for %S)", DMLIP(IP), g_mdName);
+ // fallthrough to return
+ }
+#endif // _TARGET_AMD64_
+ else
+ {
+ DMLOut(DMLIP(IP));
+ }
+ }
+ }
+ return bOutput;
+}
+
+void DumpStackWorker (DumpStackFlag &DSFlag)
+{
+ DWORD_PTR eip;
+ ULONG64 Offset;
+ g_ExtRegisters->GetInstructionOffset(&Offset);
+ eip = (DWORD_PTR)Offset;
+
+ ExtOut("Current frame: ");
+ PrintCallInfo (0, eip, DSFlag, TRUE);
+ ExtOut ("\n");
+
+ // make certain dword/qword aligned
+ DWORD_PTR ptr = DSFlag.top & (~ALIGNCONST);
+
+ ExtOut (g_targetMachine->GetDumpStackHeading());
+ while (ptr < DSFlag.end)
+ {
+ if (IsInterrupt())
+ return;
+ DWORD_PTR retAddr;
+ DWORD_PTR whereCalled;
+ move_xp(retAddr, ptr);
+ g_targetMachine->IsReturnAddress(retAddr, &whereCalled);
+ if (whereCalled)
+ {
+ BOOL bOutput = PrintCallInfo(ptr-sizeof(TADDR), retAddr, DSFlag, FALSE);
+ if (!DSFlag.fEEonly)
+ {
+ if (whereCalled != 0xFFFFFFFF)
+ {
+ ExtOut (", calling ");
+ PrintCallInfo (0, whereCalled, DSFlag, TRUE);
+ }
+ }
+ if (bOutput)
+ ExtOut ("\n");
+
+ DWORD_PTR cxrAddr;
+ CROSS_PLATFORM_CONTEXT cxr;
+ DWORD_PTR exrAddr;
+ EXCEPTION_RECORD exr;
+
+ if (g_targetMachine->GetExceptionContext(ptr,retAddr,&cxrAddr,&cxr,&exrAddr,&exr))
+ {
+ TADDR sp = g_targetMachine->GetSP(cxr);
+ TADDR ip = g_targetMachine->GetIP(cxr);
+ bOutput = PrintCallInfo(sp, ip, DSFlag, FALSE);
+ if (bOutput)
+ {
+ ExtOut(" ====> Exception ");
+ if (exrAddr)
+ ExtOut("Code %x ", exr.ExceptionCode);
+ ExtOut ("cxr@%p", SOS_PTR(cxrAddr));
+ if (exrAddr)
+ ExtOut(" exr@%p", SOS_PTR(exrAddr));
+ ExtOut("\n");
+ }
+ }
+ }
+ ptr += sizeof (DWORD_PTR);
+ }
+}
+
+#ifdef SOS_TARGET_X86
+///
+/// X86Machine implementation
+///
+LPCSTR X86Machine::s_DumpStackHeading = "ChildEBP RetAddr Caller, Callee\n";
+LPCSTR X86Machine::s_DSOHeading = "ESP/REG Object Name\n";
+LPCSTR X86Machine::s_GCRegs[7] = {"eax", "ebx", "ecx", "edx", "esi", "edi", "ebp"};
+LPCSTR X86Machine::s_SPName = "ESP";
+
+void PrintNothing (const char *fmt, ...)
+{
+ // Do nothing.
+}
+
+///
+/// Dump X86 GCInfo header and table
+///
+void X86Machine::DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const
+{
+ X86GCDump::InfoHdr header;
+ X86GCDump::GCDump gcDump(gcInfoToken.Version, encBytes, 5, true);
+ BYTE* pTable = dac_cast<PTR_BYTE>(gcInfoToken.Info);
+ if (bPrintHeader)
+ {
+ gcDump.gcPrintf = gcPrintf;
+ gcPrintf("Method info block:\n");
+ }
+ else
+ {
+ gcDump.gcPrintf = PrintNothing;
+ }
+ pTable += gcDump.DumpInfoHdr(pTable, &header, &methodSize, 0);
+ if (bPrintHeader)
+ {
+ gcPrintf("\n");
+ gcPrintf("Pointer table:\n");
+ }
+ gcDump.gcPrintf = gcPrintf;
+ gcDump.DumpGCTable(pTable, header, methodSize, 0);
+}
+#endif // SOS_TARGET_X86
+
+#ifdef SOS_TARGET_ARM
+///
+/// ARMMachine implementation
+///
+LPCSTR ARMMachine::s_DumpStackHeading = "ChildFP RetAddr Caller, Callee\n";
+LPCSTR ARMMachine::s_DSOHeading = "SP/REG Object Name\n";
+LPCSTR ARMMachine::s_GCRegs[14] = {"r0", "r1", "r2", "r3", "r4", "r5", "r6",
+ "r7", "r8", "r9", "r10", "r11", "r12", "lr"};
+LPCSTR ARMMachine::s_SPName = "sp";
+
+#endif // SOS_TARGET_ARM
+
+#ifdef SOS_TARGET_AMD64
+///
+/// AMD64Machine implementation
+///
+LPCSTR AMD64Machine::s_DumpStackHeading = "Child-SP RetAddr Caller, Callee\n";
+LPCSTR AMD64Machine::s_DSOHeading = "RSP/REG Object Name\n";
+LPCSTR AMD64Machine::s_GCRegs[15] = {"rax", "rbx", "rcx", "rdx", "rsi", "rdi", "rbp",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"};
+LPCSTR AMD64Machine::s_SPName = "RSP";
+
+///
+/// Dump AMD64 GCInfo table
+///
+void AMD64Machine::DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const
+{
+ if (bPrintHeader)
+ {
+ ExtOut("Pointer table:\n");
+ }
+
+ GCDump gcDump(gcInfoToken.Version, encBytes, 5, true);
+ gcDump.gcPrintf = gcPrintf;
+
+ gcDump.DumpGCTable(dac_cast<PTR_BYTE>(gcInfoToken.Info), methodSize, 0);
+}
+
+#endif // SOS_TARGET_AMD64
+
+#ifdef SOS_TARGET_ARM64
+///
+/// ARM64Machine implementation
+///
+LPCSTR ARM64Machine::s_DumpStackHeading = "ChildFP RetAddr Caller, Callee\n";
+LPCSTR ARM64Machine::s_DSOHeading = "SP/REG Object Name\n";
+// excluding x18, fp & lr as these will not contain object references
+LPCSTR ARM64Machine::s_GCRegs[28] = {"x0", "x1", "x2", "x3", "x4", "x5", "x6",
+ "x7", "x8", "x9", "x10", "x11", "x12", "x13",
+ "x14", "x15", "x16", "x17", "x19", "x20","x21",
+ "x22", "x23", "x24", "x25", "x26", "x27", "x28"};
+LPCSTR ARM64Machine::s_SPName = "sp";
+
+#endif // SOS_TARGET_ARM64
+
+
diff --git a/src/ToolBox/SOS/Strike/disasm.h b/src/ToolBox/SOS/Strike/disasm.h
new file mode 100644
index 0000000000..59fc168a6e
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/disasm.h
@@ -0,0 +1,453 @@
+// 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.
+
+// ==++==
+//
+
+//
+// ==--==
+#ifndef __disasm_h__
+#define __disasm_h__
+
+#include "sos_stacktrace.h"
+
+struct InfoHdr;
+class GCDump;
+
+
+struct DumpStackFlag
+{
+ BOOL fEEonly;
+ BOOL fSuppressSrcInfo;
+ DWORD_PTR top;
+ DWORD_PTR end;
+};
+
+struct GCEncodingInfo
+{
+ LPVOID pvMainFiber;
+ LPVOID pvGCTableFiber;
+
+ BYTE *table;
+ unsigned int methodSize;
+
+ char buf[1000];
+ int cch;
+
+ SIZE_T ofs;
+
+ // When decoding a cold region, set this to the size of the hot region to keep offset
+ // calculations working.
+ SIZE_T hotSizeToAdd;
+ bool fDoneDecoding;
+};
+
+// Returns:
+// NULL if the EHInfo passed in does not refer to a Typed clause
+// "..." if pEHInfo->isCatchAllHandler is TRUE
+// "TypeName" if pEHInfo is a DACEHInfo* that references type "TypeName".
+// Note:
+// The return is a pointer to a global buffer, therefore this value must
+// be consumed as soon as possible after a call to this function.
+LPCWSTR EHTypedClauseTypeName(const DACEHInfo* pEHInfo);
+
+struct SOSEHInfo
+{
+#ifndef FEATURE_CORECLR
+ __field_ecount(EHCount)
+#endif
+ DACEHInfo *m_pInfos;
+ UINT EHCount;
+ CLRDATA_ADDRESS methodStart;
+
+ SOSEHInfo() { ZeroMemory(this, sizeof(SOSEHInfo)); }
+ ~SOSEHInfo() { if (m_pInfos) { delete [] m_pInfos; } }
+
+ void FormatForDisassembly(CLRDATA_ADDRESS offSet);
+};
+
+BOOL IsClonedFinally(DACEHInfo *pEHInfo);
+
+void DumpStackWorker (DumpStackFlag &DSFlag);
+
+void UnassemblyUnmanaged (DWORD_PTR IP, BOOL bSuppressLines);
+
+HRESULT CheckEEDll ();
+
+BOOL GetCalleeSite (DWORD_PTR IP, DWORD_PTR &IPCallee);
+
+void DisasmAndClean (DWORD_PTR &IP, __out_ecount_opt(length) char *line, ULONG length);
+
+INT_PTR GetValueFromExpr(___in __in_z char *ptr, INT_PTR &value);
+
+void NextTerm (__deref_inout_z char *& ptr);
+
+BOOL IsByRef (__deref_inout_z char *& ptr);
+
+BOOL IsTermSep (char ch);
+
+const char * HelperFuncName (size_t IP);
+
+enum eTargetType { ettUnk = 0, ettNative = 1, ettJitHelp = 2, ettStub = 3, ettMD = 4 };
+
+// GetFinalTarget is based on HandleCall, but avoids printing anything to the output.
+// This is currently only called on x64
+eTargetType GetFinalTarget(DWORD_PTR callee, DWORD_PTR* finalMDorIP);
+
+#ifdef _MSC_VER
+// SOS is essentially single-threaded. ignore "construction of local static object is not thread-safe"
+#pragma warning(push)
+#pragma warning(disable:4640)
+#endif // _MSC_VER
+
+//-----------------------------------------------------------------------------------------
+//
+// Implementations for the supported target platforms
+//
+//-----------------------------------------------------------------------------------------
+
+#ifndef THUMB_CODE
+#define THUMB_CODE 1
+#endif
+#define STACKWALK_CONTROLPC_ADJUST_OFFSET 2
+
+#ifdef SOS_TARGET_X86
+
+/// X86 Machine specific code
+class X86Machine : public IMachine
+{
+public:
+ typedef X86_CONTEXT TGT_CTXT;
+
+ static IMachine* GetInstance()
+ { static X86Machine s_X86MachineInstance; return &s_X86MachineInstance; }
+
+ ULONG GetPlatform() const { return IMAGE_FILE_MACHINE_I386; }
+ ULONG GetContextSize() const { return sizeof(X86_CONTEXT); }
+ virtual void Unassembly(
+ TADDR IPBegin,
+ TADDR IPEnd,
+ TADDR IPAskedFor,
+ TADDR GCStressCodeCopy,
+ GCEncodingInfo * pGCEncodingInfo,
+ SOSEHInfo *pEHInfo,
+ BOOL bSuppressLines,
+ BOOL bDisplayOffsets) const;
+ virtual void IsReturnAddress(
+ TADDR retAddr,
+ TADDR* whereCalled) const;
+ virtual BOOL GetExceptionContext (
+ TADDR stack,
+ TADDR PC,
+ TADDR *cxrAddr,
+ CROSS_PLATFORM_CONTEXT * cxr,
+ TADDR * exrAddr,
+ PEXCEPTION_RECORD exr) const;
+
+ // retrieve stack pointer, frame pointer, and instruction pointer from the target context
+ virtual TADDR GetSP(const CROSS_PLATFORM_CONTEXT & ctx) const { return ctx.X86Context.Esp; }
+ virtual TADDR GetBP(const CROSS_PLATFORM_CONTEXT & ctx) const { return ctx.X86Context.Ebp; }
+ virtual TADDR GetIP(const CROSS_PLATFORM_CONTEXT & ctx) const { return ctx.X86Context.Eip; }
+
+ virtual void FillSimpleContext(StackTrace_SimpleContext * dest, LPVOID srcCtx) const;
+ virtual void FillTargetContext(LPVOID destCtx, LPVOID srcCtx, int idx = 0) const;
+
+ virtual LPCSTR GetDumpStackHeading() const { return s_DumpStackHeading; }
+ virtual LPCSTR GetDumpStackObjectsHeading() const { return s_DSOHeading; }
+ virtual LPCSTR GetSPName() const { return s_SPName; }
+ virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const
+ { _ASSERTE(cntRegs != NULL); *regNames = s_GCRegs; *cntRegs = _countof(s_GCRegs); }
+
+ virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
+
+private:
+ X86Machine() {}
+ ~X86Machine() {}
+ X86Machine(const X86Machine& machine); // undefined
+ X86Machine & operator=(const X86Machine&); // undefined
+
+private:
+ static LPCSTR s_DumpStackHeading;
+ static LPCSTR s_DSOHeading;
+ static LPCSTR s_GCRegs[7];
+ static LPCSTR s_SPName;
+}; // class X86Machine
+
+#endif // SOS_TARGET_X86
+
+
+#ifdef SOS_TARGET_ARM
+
+/// ARM Machine specific code
+class ARMMachine : public IMachine
+{
+public:
+ typedef ARM_CONTEXT TGT_CTXT;
+
+ static IMachine* GetInstance()
+ { return &s_ARMMachineInstance; }
+
+ ULONG GetPlatform() const { return IMAGE_FILE_MACHINE_ARMNT; }
+ ULONG GetContextSize() const { return sizeof(ARM_CONTEXT); }
+ virtual void Unassembly(
+ TADDR IPBegin,
+ TADDR IPEnd,
+ TADDR IPAskedFor,
+ TADDR GCStressCodeCopy,
+ GCEncodingInfo *pGCEncodingInfo,
+ SOSEHInfo *pEHInfo,
+ BOOL bSuppressLines,
+ BOOL bDisplayOffsets) const;
+ virtual void IsReturnAddress(
+ TADDR retAddr,
+ TADDR* whereCalled) const;
+ virtual BOOL GetExceptionContext (
+ TADDR stack,
+ TADDR PC,
+ TADDR *cxrAddr,
+ CROSS_PLATFORM_CONTEXT * cxr,
+ TADDR *exrAddr,
+ PEXCEPTION_RECORD exr) const;
+
+ // retrieve stack pointer, frame pointer, and instruction pointer from the target context
+ virtual TADDR GetSP(const CROSS_PLATFORM_CONTEXT & ctx) const { return ctx.ArmContext.Sp; }
+ // @ARMTODO: frame pointer
+ virtual TADDR GetBP(const CROSS_PLATFORM_CONTEXT & ctx) const { return 0; }
+ virtual TADDR GetIP(const CROSS_PLATFORM_CONTEXT & ctx) const { return ctx.ArmContext.Pc; }
+
+ virtual void FillSimpleContext(StackTrace_SimpleContext * dest, LPVOID srcCtx) const;
+ virtual void FillTargetContext(LPVOID destCtx, LPVOID srcCtx, int idx = 0) const;
+
+ virtual LPCSTR GetDumpStackHeading() const { return s_DumpStackHeading; }
+ virtual LPCSTR GetDumpStackObjectsHeading() const { return s_DSOHeading; }
+ virtual LPCSTR GetSPName() const { return s_SPName; }
+ virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const
+ { _ASSERTE(cntRegs != NULL); *regNames = s_GCRegs; *cntRegs = _countof(s_GCRegs); }
+
+ virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
+
+private:
+ ARMMachine() {}
+ ~ARMMachine() {}
+ ARMMachine(const ARMMachine& machine); // undefined
+ ARMMachine & operator=(const ARMMachine&); // undefined
+
+private:
+ static LPCSTR s_DumpStackHeading;
+ static LPCSTR s_DSOHeading;
+ static LPCSTR s_GCRegs[14];
+ static LPCSTR s_SPName;
+ static ARMMachine s_ARMMachineInstance;
+}; // class ARMMachine
+
+#endif // SOS_TARGET_ARM
+
+#ifdef SOS_TARGET_AMD64
+
+/// AMD64 Machine specific code
+class AMD64Machine : public IMachine
+{
+public:
+ typedef AMD64_CONTEXT TGT_CTXT;
+
+ static IMachine* GetInstance()
+ { static AMD64Machine s_AMD64MachineInstance; return &s_AMD64MachineInstance; }
+
+ ULONG GetPlatform() const { return IMAGE_FILE_MACHINE_AMD64; }
+ ULONG GetContextSize() const { return sizeof(AMD64_CONTEXT); }
+
+ virtual void Unassembly(
+ TADDR IPBegin,
+ TADDR IPEnd,
+ TADDR IPAskedFor,
+ TADDR GCStressCodeCopy,
+ GCEncodingInfo *pGCEncodingInfo,
+ SOSEHInfo *pEHInfo,
+ BOOL bSuppressLines,
+ BOOL bDisplayOffsets) const;
+
+ virtual void IsReturnAddress(
+ TADDR retAddr,
+ TADDR* whereCalled) const;
+
+ virtual BOOL GetExceptionContext (
+ TADDR stack,
+ TADDR PC,
+ TADDR *cxrAddr,
+ CROSS_PLATFORM_CONTEXT * cxr,
+ TADDR *exrAddr,
+ PEXCEPTION_RECORD exr) const;
+
+ // retrieve stack pointer, frame pointer, and instruction pointer from the target context
+ virtual TADDR GetSP(const CROSS_PLATFORM_CONTEXT & ctx) const { return ctx.Amd64Context.Rsp; }
+ virtual TADDR GetBP(const CROSS_PLATFORM_CONTEXT & ctx) const { return ctx.Amd64Context.Rbp; }
+ virtual TADDR GetIP(const CROSS_PLATFORM_CONTEXT & ctx) const { return ctx.Amd64Context.Rip; }
+
+ virtual void FillSimpleContext(StackTrace_SimpleContext * dest, LPVOID srcCtx) const;
+ virtual void FillTargetContext(LPVOID destCtx, LPVOID srcCtx, int idx = 0) const;
+
+ virtual LPCSTR GetDumpStackHeading() const { return s_DumpStackHeading; }
+ virtual LPCSTR GetDumpStackObjectsHeading() const { return s_DSOHeading; }
+ virtual LPCSTR GetSPName() const { return s_SPName; }
+ virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const
+ { _ASSERTE(cntRegs != NULL); *regNames = s_GCRegs; *cntRegs = _countof(s_GCRegs); }
+
+ virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
+
+private:
+ AMD64Machine() {}
+ ~AMD64Machine() {}
+ AMD64Machine(const AMD64Machine& machine); // undefined
+ AMD64Machine & operator=(const AMD64Machine&); // undefined
+
+private:
+ static LPCSTR s_DumpStackHeading;
+ static LPCSTR s_DSOHeading;
+ static LPCSTR s_GCRegs[15];
+ static LPCSTR s_SPName;
+}; // class AMD64Machine
+
+#endif // SOS_TARGET_AMD64
+
+#ifdef SOS_TARGET_ARM64
+
+/// ARM64 Machine specific code
+class ARM64Machine : public IMachine
+{
+public:
+ typedef ARM64_CONTEXT TGT_CTXT;
+
+ static IMachine* GetInstance()
+ { static ARM64Machine s_ARM64MachineInstance; return &s_ARM64MachineInstance; }
+
+ ULONG GetPlatform() const { return IMAGE_FILE_MACHINE_ARM64; }
+ ULONG GetContextSize() const { return sizeof(ARM64_CONTEXT); }
+ virtual void Unassembly(
+ TADDR IPBegin,
+ TADDR IPEnd,
+ TADDR IPAskedFor,
+ TADDR GCStressCodeCopy,
+ GCEncodingInfo *pGCEncodingInfo,
+ SOSEHInfo *pEHInfo,
+ BOOL bSuppressLines,
+ BOOL bDisplayOffsets) const;
+ virtual void IsReturnAddress(
+ TADDR retAddr,
+ TADDR* whereCalled) const;
+ virtual BOOL GetExceptionContext (
+ TADDR stack,
+ TADDR PC,
+ TADDR *cxrAddr,
+ CROSS_PLATFORM_CONTEXT * cxr,
+ TADDR *exrAddr,
+ PEXCEPTION_RECORD exr) const;
+
+ // retrieve stack pointer, frame pointer, and instruction pointer from the target context
+ virtual TADDR GetSP(const CROSS_PLATFORM_CONTEXT & ctx) const { return ctx.Arm64Context.Sp; }
+ virtual TADDR GetBP(const CROSS_PLATFORM_CONTEXT & ctx) const { return ctx.Arm64Context.Fp; }
+ virtual TADDR GetIP(const CROSS_PLATFORM_CONTEXT & ctx) const { return ctx.Arm64Context.Pc; }
+
+ virtual void FillSimpleContext(StackTrace_SimpleContext * dest, LPVOID srcCtx) const;
+ virtual void FillTargetContext(LPVOID destCtx, LPVOID srcCtx, int idx = 0) const;
+
+ virtual LPCSTR GetDumpStackHeading() const { return s_DumpStackHeading; }
+ virtual LPCSTR GetDumpStackObjectsHeading() const { return s_DSOHeading; }
+ virtual LPCSTR GetSPName() const { return s_SPName; }
+ virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const
+ { _ASSERTE(cntRegs != NULL); *regNames = s_GCRegs; *cntRegs = _countof(s_GCRegs);}
+
+ virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
+
+private:
+ ARM64Machine() {}
+ ~ARM64Machine() {}
+ ARM64Machine(const ARM64Machine& machine); // undefined
+ ARM64Machine & operator=(const ARM64Machine&); // undefined
+
+ static LPCSTR s_DumpStackHeading;
+ static LPCSTR s_DSOHeading;
+ static LPCSTR s_GCRegs[28];
+ static LPCSTR s_SPName;
+
+}; // class ARM64Machine
+
+#endif // SOS_TARGET_ARM64
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif // _MSC_VER
+
+
+//
+// Inline methods
+//
+
+
+#ifdef SOS_TARGET_X86
+inline void X86Machine::FillSimpleContext(StackTrace_SimpleContext * dest, LPVOID srcCtx) const
+{
+ TGT_CTXT& src = *(TGT_CTXT*) srcCtx;
+ dest->StackOffset = src.Esp;
+ dest->FrameOffset = src.Ebp;
+ dest->InstructionOffset = src.Eip;
+}
+
+inline void X86Machine::FillTargetContext(LPVOID destCtx, LPVOID srcCtx, int idx /*= 0*/) const
+{
+ TGT_CTXT* dest = (TGT_CTXT*)destCtx + idx;
+ *dest = *(TGT_CTXT*)srcCtx;
+}
+#endif // SOS_TARGET_X86
+
+
+#ifdef SOS_TARGET_ARM
+inline void ARMMachine::FillSimpleContext(StackTrace_SimpleContext * dest, LPVOID srcCtx) const
+{
+ TGT_CTXT& src = *(TGT_CTXT*) srcCtx;
+ dest->StackOffset = src.Sp;
+ // @ARMTODO: frame pointer - keep in sync with ARMMachine::GetBP
+ dest->FrameOffset = 0;
+ dest->InstructionOffset = src.Pc;
+}
+
+inline void ARMMachine::FillTargetContext(LPVOID destCtx, LPVOID srcCtx, int idx /*= 0*/) const
+{
+ TGT_CTXT* dest = (TGT_CTXT*)destCtx + idx;
+ *dest = *(TGT_CTXT*)srcCtx;
+}
+#endif // SOS_TARGET_ARM
+
+
+#ifdef SOS_TARGET_AMD64
+inline void AMD64Machine::FillSimpleContext(StackTrace_SimpleContext * dest, LPVOID srcCtx) const
+{
+ TGT_CTXT& src = *(TGT_CTXT*) srcCtx;
+ dest->StackOffset = src.Rsp;
+ dest->FrameOffset = src.Rbp;
+ dest->InstructionOffset = src.Rip;
+}
+
+inline void AMD64Machine::FillTargetContext(LPVOID destCtx, LPVOID srcCtx, int idx /*= 0*/) const
+{
+ TGT_CTXT* dest = (TGT_CTXT*)destCtx + idx;
+ *dest = *(TGT_CTXT*)srcCtx;
+}
+#endif // SOS_TARGET_AMD64
+
+#ifdef SOS_TARGET_ARM64
+inline void ARM64Machine::FillSimpleContext(StackTrace_SimpleContext * dest, LPVOID srcCtx) const
+{
+ TGT_CTXT& src = *(TGT_CTXT*) srcCtx;
+ dest->StackOffset = src.Sp;
+ dest->FrameOffset = src.Fp;
+ dest->InstructionOffset = src.Pc;
+}
+
+inline void ARM64Machine::FillTargetContext(LPVOID destCtx, LPVOID srcCtx, int idx /*= 0*/) const
+{
+ TGT_CTXT* dest = (TGT_CTXT*)destCtx + idx;
+ *dest = *(TGT_CTXT*)srcCtx;
+}
+#endif // SOS_TARGET_ARM64
+
+#endif // __disasm_h__
diff --git a/src/ToolBox/SOS/Strike/disasmARM.cpp b/src/ToolBox/SOS/Strike/disasmARM.cpp
new file mode 100644
index 0000000000..82173558fd
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/disasmARM.cpp
@@ -0,0 +1,626 @@
+// 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.
+
+// ==++==
+//
+
+//
+// ==--==
+
+#ifndef _TARGET_ARM_
+#define _TARGET_ARM_
+#endif
+
+
+#include "strike.h"
+#include "util.h"
+#include <dbghelp.h>
+
+
+#include "disasm.h"
+
+#include "../../../inc/corhdr.h"
+#include "../../../inc/cor.h"
+#include "../../../inc/dacprivate.h"
+
+#ifndef FEATURE_PAL
+namespace ARMGCDump
+{
+#undef _TARGET_X86_
+#undef LIMITED_METHOD_CONTRACT
+#define LIMITED_METHOD_DAC_CONTRACT
+#define SUPPORTS_DAC
+#define LF_GCROOTS
+#define LL_INFO1000
+#define LOG(x)
+#define LOG_PIPTR(pObjRef, gcFlags, hCallBack)
+#define DAC_ARG(x)
+#include "gcdumpnonx86.cpp"
+}
+#endif // !FEATURE_PAL
+
+#if defined(_TARGET_WIN64_)
+#error This file does not support SOS targeting ARM from a 64-bit debugger
+#endif
+
+#if !defined(SOS_TARGET_ARM)
+#error This file should be used to support SOS targeting ARM debuggees
+#endif
+
+#ifdef SOS_TARGET_ARM
+ARMMachine ARMMachine::s_ARMMachineInstance;
+
+// Decodes the target label of the immediate form of bl and blx instructions. The PC given is that of the
+// start of the instruction.
+static TADDR DecodeCallTarget(TADDR PC, WORD rgInstr[2])
+{
+ // Displacement is spread across several bitfields in the two words of the instruction. Using the same
+ // bitfield names as the ARM Architecture Reference Manual.
+ DWORD S = (rgInstr[0] & 0x0400) >> 10;
+ DWORD imm10 = rgInstr[0] & 0x03ff;
+ DWORD J1 = (rgInstr[1] & 0x2000) >> 13;
+ DWORD J2 = (rgInstr[1] & 0x0800) >> 11;
+ DWORD imm11 = rgInstr[1] & 0x07ff;
+
+ // For reasons that escape me the I1 and I2 fields are computed by XOR'ing J1 and J2 with S.
+ DWORD I1 = (~J1 ^ S) & 0x1;
+ DWORD I2 = (~J2 ^ S) & 0x1;
+
+ // The final displacement is put together as: SignExtend(S:I1:I2:imm10:imm11:0)
+ DWORD highByte = S ? 0xff000000 : 0x00000000;
+ DWORD disp = highByte | (I1 << 23) | (I2 << 22) | (imm10 << 12) | (imm11 << 1);
+
+ // The displacement is relative to the PC but the PC for a given instruction reads as the PC for the
+ // beginning of the instruction plus 4.
+ return PC + 4 + disp;
+}
+
+// Validate that a potential call target points to readable memory. If so, and the code appears to be one of
+// our standard jump thunks we'll deference through that and return the real target. Returns 0 if any checks
+// fail.
+static TADDR GetRealCallTarget(TADDR PC)
+{
+ WORD instr[2];
+
+ // Read the minimum (a WORD) first in case we're calling to a single WORD method at the end of a page
+ // (e.g. BLX <reg>).
+ if (g_ExtData->ReadVirtual(TO_CDADDR(PC), &instr[0], sizeof(WORD), NULL) != S_OK)
+ return 0;
+
+ // All the jump thunks we handle start with the literal form of LDR (i.e. LDR <reg>, [PC +/- <imm>]). It's
+ // always the two word form since we're either loading R12 or PC. We never use the decrement version of
+ // the instruction (U == 0).
+ // If it's not an instruction of that form we can return immediately.
+ if (instr[0] != 0xf8df)
+ return PC;
+
+ // The first instruction is defintely a LDR of the form we expect so it's OK to read the second half of
+ // the encoding.
+ if (g_ExtData->ReadVirtual(TO_CDADDR(PC + 2), &instr[1], sizeof(WORD), NULL) != S_OK)
+ return 0;
+
+ // Determine which register we're loading. There are three cases:
+ // 1) PC: we're jumping, perform final calculation of the jump target
+ // 2) R12: we're possibly setting up a special argument to the jump target. Ignore this instruction and
+ // check for a LDR PC in the next instruction
+ // 3) Any other register: we don't recognize this instruction sequence, just return the PC we have
+ WORD reg = (instr[1] & 0xf000) >> 12;
+ if (reg == 12)
+ {
+ // Possibly a LDR R12, [...]; LDR PC, [...] thunk. Overwrite the current instruction with the next and
+ // then fall through into the common LDR PC, [...] handling below. If we fail to read the next word
+ // we're not really looking at valid code. But we need to be more careful reading the second word of
+ // the potential instruction since there are valid sequences that would terminate with a single word
+ // at the end of page.
+ if (g_ExtData->ReadVirtual(TO_CDADDR(PC + 4), &instr[0], sizeof(WORD), NULL) != S_OK)
+ return 0;
+
+ // Following instruction is not a LDR <literal>. Return this PC as the real target.
+ if (instr[0] != 0xf8df)
+ return PC;
+
+ // Read second half of the LDR instruction.
+ if (g_ExtData->ReadVirtual(TO_CDADDR(PC + 6), &instr[1], sizeof(WORD), NULL) != S_OK)
+ return 0;
+
+ // Determine the target register. If it's not the PC then return this PC as the real target.
+ reg = (instr[1] & 0xf000) >> 12;
+ if (reg != 12)
+ return PC;
+
+ // Fall through to process this LDR PC, [...] instruction. Update the input PC because it figures into
+ // the calculation below.
+ PC += 4;
+ }
+ else if (reg == 15)
+ {
+ // First instruction was a LDR PC, [...] Just fall through to common handling below.
+ }
+ else
+ {
+ // Any other target register is unrecognized. Just return what we have as the final target.
+ return PC;
+ }
+
+ // Decode the LDR PC, [PC + <imm>] to find the jump target.
+ // The displacement is in the low order 12 bits of the second instruction word.
+ DWORD disp = instr[1] & 0x0fff;
+
+ // The PC used for the effective address calculation is the PC from the start of the instruction rounded
+ // down to 4-byte alignment then incremented by 4.
+ TADDR targetAddress = (PC & ~3) + 4 + disp;
+
+ // Read the target address from this routine.
+ TADDR target;
+ if (g_ExtData->ReadVirtual(TO_CDADDR(targetAddress), &target, sizeof(target), NULL) != S_OK)
+ return 0;
+
+ // Clear the low-bit in the target used to indicate a Thumb mode destination. If this is not set we can't
+ // be looking at one of our jump thunks (in fact ARM mode code is illegal under CoreARM so this would
+ // indicate an issue).
+ _ASSERTE((target & 1) == 1);
+ target &= ~1;
+
+ // Recursively call ourselves on this target in case we have any double jump thunks.
+ return GetRealCallTarget(target);
+}
+
+// Determine (heuristically, basically a best effort guess) whether an address on the stack represents a
+// return address. This is achieved by looking at the memory prior to the potential return address and
+// disassembling it to see whether it looks like a potential call. If possible the target of the callsite is
+// also returned.
+//
+// Result is returned in whereCalled:
+// 0 : retAddr doesn't look like a return address
+// 0xffffffff : retAddr looks like a return address but we couldn't tell where the call site was targeted
+// <other> : retAddr looks like a return address, *whereCalled set to target address
+void ARMMachine::IsReturnAddress(TADDR retAddr, TADDR* whereCalled) const
+{
+ *whereCalled = 0;
+
+ // If retAddr doesn't have the low-order bit set (indicating a return to Thumb code) then it can't be a
+ // legal return address.
+ if ((retAddr & 1) == 0)
+ return;
+ retAddr &= ~1;
+
+ // Potential calling instructions may have been one or two WORDs in length.
+ WORD rgPrevious[2];
+ move_xp(rgPrevious, retAddr - sizeof(rgPrevious));
+
+ // Check two-word variants first.
+ if (((rgPrevious[0] & 0xf800) == 0xf000) &&
+ ((rgPrevious[1] & 0xd000) == 0xd000))
+ {
+ // BL <label>
+
+ // Decode and validate PC-relative call target. Dereference through any jump thunks and return the
+ // call target.
+ TADDR target = GetRealCallTarget(DecodeCallTarget(retAddr - 4, rgPrevious));
+ if (target)
+ {
+ *whereCalled = target;
+ return;
+ }
+ }
+ else if (((rgPrevious[0] & 0xf800) == 0xf000) &&
+ ((rgPrevious[1] & 0xd001) == 0xc000))
+ {
+ // BLX <label>
+
+ // Decode and validate PC-relative call target. Dereference through any jump thunks and return the
+ // call target.
+ TADDR target = GetRealCallTarget(DecodeCallTarget(retAddr - 4, rgPrevious));
+ if (target)
+ {
+ *whereCalled = target;
+ return;
+ }
+ }
+ else if (((rgPrevious[0] & 0xfff0) == 0xf8d0) &&
+ ((rgPrevious[1] & 0xf000) == 0xf000))
+ {
+ // LDR PC, [<reg> + #<imm>]
+ *whereCalled = 0xffffffff;
+ return;
+ }
+ else if (((rgPrevious[0] & 0xff7f) == 0xf85f) &&
+ ((rgPrevious[1] & 0xf000) == 0xf000))
+ {
+ // LDR PC, [PC + #<imm>]
+ *whereCalled = 0xffffffff;
+ return;
+ }
+ else if (((rgPrevious[0] & 0xfff0) == 0xf850) &&
+ ((rgPrevious[1] & 0xffc0) == 0xf000))
+ {
+ // LDR PC, [<reg> + <reg>, LSL #<imm>]
+ *whereCalled = 0xffffffff;
+ return;
+ }
+
+ // Fall through any failures to decode as a two-word instruction to the one word cases below...
+
+ // BLX <register>
+ if ((rgPrevious[1] & 0xff87) == 0x4780)
+ {
+ *whereCalled = 0xffffffff;
+ return;
+ }
+}
+
+
+// Return 0 for non-managed call. Otherwise return MD address.
+static TADDR MDForCall (TADDR callee)
+{
+ // call managed code?
+ JITTypes jitType;
+ TADDR methodDesc;
+ TADDR PC = callee;
+ TADDR gcinfoAddr;
+
+ PC = GetRealCallTarget(callee);
+ if (!PC)
+ return 0;
+
+ IP2MethodDesc (PC, methodDesc, jitType, gcinfoAddr);
+ return methodDesc;
+}
+
+// Determine if a value is MT/MD/Obj
+static void HandleValue(TADDR value)
+{
+#ifndef FEATURE_PAL
+ // remove the thumb bit (if set)
+ value = value & ~1;
+#else
+ // set the thumb bit (if not set)
+ value = value | 1;
+#endif //!FEATURE_PAL
+
+ // A MethodTable?
+ if (IsMethodTable(value))
+ {
+ NameForMT_s (value, g_mdName,mdNameLen);
+ ExtOut (" (MT: %S)", g_mdName);
+ return;
+ }
+
+ // A Managed Object?
+ TADDR dwMTAddr;
+ move_xp (dwMTAddr, value);
+ if (IsStringObject(value))
+ {
+ ExtOut (" (\"");
+ StringObjectContent (value, TRUE);
+ ExtOut ("\")");
+ return;
+ }
+ else if (IsMethodTable(dwMTAddr))
+ {
+ NameForMT_s (dwMTAddr, g_mdName,mdNameLen);
+ ExtOut (" (Object: %S)", g_mdName);
+ return;
+ }
+
+ // A MethodDesc?
+ if (IsMethodDesc(value))
+ {
+ NameForMD_s (value, g_mdName,mdNameLen);
+ ExtOut (" (MD: %S)", g_mdName);
+ return;
+ }
+
+ // A JitHelper?
+ const char* name = HelperFuncName(value);
+ if (name) {
+ ExtOut (" (JitHelp: %s)", name);
+ return;
+ }
+
+ // A call to managed code?
+ TADDR methodDesc = MDForCall(value);
+ if (methodDesc)
+ {
+ NameForMD_s (methodDesc, g_mdName,mdNameLen);
+ ExtOut (" (code for MD: %S)", g_mdName);
+ return;
+ }
+
+ // Random symbol.
+ char Symbol[1024];
+ if (SUCCEEDED(g_ExtSymbols->GetNameByOffset(TO_CDADDR(value), Symbol, 1024,
+ NULL, NULL)))
+ {
+ if (Symbol[0] != '\0')
+ {
+ ExtOut (" (%s)", Symbol);
+ return;
+ }
+ }
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Unassembly a managed code. Translating managed object, *
+* call. *
+* *
+\**********************************************************************/
+void ARMMachine::Unassembly (
+ TADDR PCBegin,
+ TADDR PCEnd,
+ TADDR PCAskedFor,
+ TADDR GCStressCodeCopy,
+ GCEncodingInfo *pGCEncodingInfo,
+ SOSEHInfo *pEHInfo,
+ BOOL bSuppressLines,
+ BOOL bDisplayOffsets) const
+{
+ ULONG_PTR PC = PCBegin;
+ char line[1024];
+ char *ptr;
+ char *valueptr;
+ bool fLastWasMovW = false;
+ INT_PTR lowbits = 0;
+ ULONG curLine = -1;
+ WCHAR filename[MAX_LONGPATH];
+ ULONG linenum;
+
+ while (PC < PCEnd)
+ {
+ if (IsInterrupt())
+ return;
+
+ // Print out line numbers if needed
+ if (!bSuppressLines
+ && SUCCEEDED(GetLineByOffset(TO_CDADDR(PC), &linenum, filename, MAX_LONGPATH)))
+ {
+ if (linenum != curLine)
+ {
+ curLine = linenum;
+ ExtOut("\n%S @ %d:\n", filename, linenum);
+ }
+ }
+
+#ifndef FEATURE_PAL
+ //
+ // Print out any GC information corresponding to the current instruction offset.
+ //
+ if (pGCEncodingInfo)
+ {
+ SIZE_T curOffset = (PC - PCBegin) + pGCEncodingInfo->hotSizeToAdd;
+ while ( !pGCEncodingInfo->fDoneDecoding
+ && pGCEncodingInfo->ofs <= curOffset)
+ {
+ ExtOut(pGCEncodingInfo->buf);
+ ExtOut("\n");
+ SwitchToFiber(pGCEncodingInfo->pvGCTableFiber);
+ }
+ }
+#endif //!FEATURE_PAL
+ //
+ // Print out any EH info corresponding to the current offset
+ //
+ if (pEHInfo)
+ {
+ pEHInfo->FormatForDisassembly(PC - PCBegin);
+ }
+
+ if ((PC & ~1) == (PCAskedFor & ~1))
+ {
+ ExtOut (">>> ");
+ }
+
+ //
+ // Print offsets, in addition to actual address.
+ //
+ if (bDisplayOffsets)
+ {
+ ExtOut("%04x ", PC - PCBegin);
+ }
+
+ ULONG_PTR prevPC = PC;
+ DisasmAndClean (PC, line, _countof(line));
+
+ // look at the disassembled bytes
+ ptr = line;
+ NextTerm (ptr);
+
+ //
+ // If there is gcstress info for this method, and this is a 'hlt'
+ // instruction, then gcstress probably put the 'hlt' there. Look
+ // up the original instruction and print it instead.
+ //
+
+
+ if ( GCStressCodeCopy
+ && ( !strncmp (ptr, "de00 ", 5)
+ || !strncmp (ptr, "de01 ", 5)
+ || !strncmp (ptr, "de02 ", 5)
+ || !strncmp (ptr, "f7f0a001", 8)
+ || !strncmp (ptr, "f7f0a002", 8)
+ || !strncmp (ptr, "f7f0a003", 8)
+ ))
+ {
+ ULONG_PTR InstrAddr = prevPC;
+
+ //
+ // Compute address into saved copy of the code, and
+ // disassemble the original instruction
+ //
+
+ ULONG_PTR OrigInstrAddr = GCStressCodeCopy + (InstrAddr - PCBegin);
+ ULONG_PTR OrigPC = OrigInstrAddr;
+
+ DisasmAndClean(OrigPC, line, _countof(line));
+
+ //
+ // Increment the real PC based on the size of the unmodifed
+ // instruction
+ //
+
+ PC = InstrAddr + (OrigPC - OrigInstrAddr);
+
+ //
+ // Print out real code address in place of the copy address
+ //
+
+ ExtOut("%08x ", (ULONG)InstrAddr);
+
+ ptr = line;
+ NextTerm (ptr);
+
+ //
+ // Print out everything after the code address, and skip the
+ // instruction bytes
+ //
+
+ ExtOut(ptr);
+
+ //
+ // Add an indicator that this address has not executed yet
+ //
+
+ ExtOut(" (gcstress)");
+ }
+ else
+ {
+ ExtOut (line);
+ }
+
+ // Now advance to the opcode
+ NextTerm (ptr);
+
+ if (!strncmp (ptr, "movw ", 5) || !strncmp (ptr, "mov ", 4))
+ {
+ // Possibly the loading the low-order 16-bits of a 32-bit constant. Cache the value in case the
+ // next instruction is a movt with the high-order bits.
+ if ((valueptr = strchr(ptr, '#')) != NULL)
+ {
+ GetValueFromExpr(valueptr, lowbits);
+ fLastWasMovW = true;
+ }
+ }
+ else
+ {
+ if (!strncmp (ptr, "movt ", 5) && fLastWasMovW)
+ {
+ // A movt following a movw (if we were being really careful we'd check that the destination
+ // register was the same in both cases). Assemble the two 16-bit immediate values from both
+ // instructions and see if the resultant constant is interesting.
+ if ((valueptr = strchr(ptr, '#')) != NULL)
+ {
+ INT_PTR highbits;
+ GetValueFromExpr(valueptr, highbits);
+ HandleValue((highbits << 16) | lowbits);
+ }
+ }
+ else if ((valueptr = strchr(ptr, '=')) != NULL)
+ {
+ // Some instruction fetched a PC-relative constant which the disassembler nicely decoded for
+ // us using the ARM convention =<constant>. Retrieve this value and see if it's interesting.
+ INT_PTR value;
+ GetValueFromExpr(valueptr, value);
+ HandleValue(value);
+ }
+
+ fLastWasMovW = false;
+ }
+
+ ExtOut ("\n");
+ }
+}
+
+#if 0 // @ARMTODO: Figure out how to extract this information under CoreARM
+static void ExpFuncStateInit (TADDR *PCRetAddr)
+{
+ ULONG64 offset;
+ if (FAILED(g_ExtSymbols->GetOffsetByName("ntdll!KiUserExceptionDispatcher", &offset))) {
+ return;
+ }
+ char line[256];
+ int i = 0;
+ while (i < 3) {
+ g_ExtControl->Disassemble (offset, 0, line, 256, NULL, &offset);
+ if (strstr (line, "call")) {
+ PCRetAddr[i++] = (TADDR)offset;
+ }
+ }
+}
+#endif // 0
+
+
+// @ARMTODO: Figure out how to extract this information under CoreARM
+BOOL ARMMachine::GetExceptionContext (TADDR stack, TADDR PC, TADDR *cxrAddr, CROSS_PLATFORM_CONTEXT * cxr,
+ TADDR * exrAddr, PEXCEPTION_RECORD exr) const
+{
+ return FALSE;
+#if 0 // @ARMTODO: Figure out how to extract this information under CoreARM
+ static TADDR PCRetAddr[3] = {0,0,0};
+
+ if (PCRetAddr[0] == 0) {
+ ExpFuncStateInit (PCRetAddr);
+ }
+ *cxrAddr = 0;
+ *exrAddr = 0;
+ if (PC == PCRetAddr[0]) {
+ *exrAddr = stack + sizeof(TADDR);
+ *cxrAddr = stack + 2*sizeof(TADDR);
+ }
+ else if (PC == PCRetAddr[1]) {
+ *cxrAddr = stack + sizeof(TADDR);
+ }
+ else if (PC == PCRetAddr[2]) {
+ *exrAddr = stack + sizeof(TADDR);
+ *cxrAddr = stack + 2*sizeof(TADDR);
+ }
+ else
+ return FALSE;
+
+ if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(*cxrAddr), &stack, sizeof(stack), NULL)))
+ return FALSE;
+ *cxrAddr = stack;
+
+ if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(stack), cxr, sizeof(DT_CONTEXT), NULL))) {
+ return FALSE;
+ }
+
+ if (*exrAddr) {
+ if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(*exrAddr), &stack, sizeof(stack), NULL)))
+ {
+ *exrAddr = 0;
+ return TRUE;
+ }
+ *exrAddr = stack;
+ size_t erSize = offsetof (EXCEPTION_RECORD, ExceptionInformation);
+ if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(stack), exr, erSize, NULL))) {
+ *exrAddr = 0;
+ return TRUE;
+ }
+ }
+ return TRUE;
+#endif // 0
+}
+
+
+///
+/// Dump ARM GCInfo table
+///
+void ARMMachine::DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const
+{
+#ifndef FEATURE_PAL
+ if (bPrintHeader)
+ {
+ ExtOut("Pointer table:\n");
+ }
+
+ ARMGCDump::GCDump gcDump(gcInfoToken.Version, encBytes, 5, true);
+ gcDump.gcPrintf = gcPrintf;
+
+ gcDump.DumpGCTable(dac_cast<PTR_BYTE>(gcInfoToken.Info), methodSize, 0);
+#endif // !FEATURE_PAL
+}
+
+#endif // SOS_TARGET_ARM
diff --git a/src/ToolBox/SOS/Strike/disasmARM64.cpp b/src/ToolBox/SOS/Strike/disasmARM64.cpp
new file mode 100644
index 0000000000..6a19fc9377
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/disasmARM64.cpp
@@ -0,0 +1,392 @@
+// 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.
+
+// ==++==
+//
+
+//
+// ==--==
+
+#ifndef _TARGET_ARM64_
+#define _TARGET_ARM64_
+#endif
+
+#ifdef _TARGET_AMD64_
+#undef _TARGET_AMD64_
+#endif
+
+#include "strike.h"
+#include "util.h"
+#include <dbghelp.h>
+
+
+#include "disasm.h"
+
+#include "../../../inc/corhdr.h"
+#include "../../../inc/cor.h"
+#include "../../../inc/dacprivate.h"
+
+namespace ARM64GCDump
+{
+#undef _TARGET_X86_
+#undef LIMITED_METHOD_CONTRACT
+#define LIMITED_METHOD_DAC_CONTRACT
+#define SUPPORTS_DAC
+#define LF_GCROOTS
+#define LL_INFO1000
+#define LOG(x)
+#define LOG_PIPTR(pObjRef, gcFlags, hCallBack)
+#define DAC_ARG(x)
+#include "gcdumpnonx86.cpp"
+}
+
+#ifdef FEATURE_PAL
+void SwitchToFiber(void*)
+{
+ // TODO: Fix for linux
+ assert(false);
+}
+#endif
+
+#if !defined(_TARGET_WIN64_)
+#error This file only supports SOS targeting ARM64 from a 64-bit debugger
+#endif
+
+#if !defined(SOS_TARGET_ARM64)
+#error This file should be used to support SOS targeting ARM64 debuggees
+#endif
+
+
+void ARM64Machine::IsReturnAddress(TADDR retAddr, TADDR* whereCalled) const
+{
+ *whereCalled = 0;
+
+ DWORD previousInstr;
+ move_xp(previousInstr, retAddr - sizeof(previousInstr));
+
+ // ARM64TODO: needs to be implemented for jump stubs for ngen case
+
+ if ((previousInstr & 0xfffffc1f) == 0xd63f0000)
+ {
+ // BLR <reg>
+ *whereCalled = 0xffffffff;
+ }
+ else if ((previousInstr & 0xfc000000) == 0x94000000)
+ {
+ // BL <label>
+ DWORD imm26 = previousInstr & 0x03ffffff;
+ // offset = SignExtend(imm26:'00', 64);
+ INT64 offset = ((INT64)imm26 << 38) >> 36;
+ *whereCalled = retAddr - 4 + offset;
+ }
+}
+
+// Determine if a value is MT/MD/Obj
+static void HandleValue(TADDR value)
+{
+ // A MethodTable?
+ if (IsMethodTable(value))
+ {
+ NameForMT_s (value, g_mdName,mdNameLen);
+ ExtOut (" (MT: %S)", g_mdName);
+ return;
+ }
+
+ // A Managed Object?
+ TADDR dwMTAddr;
+ move_xp (dwMTAddr, value);
+ if (IsStringObject(value))
+ {
+ ExtOut (" (\"");
+ StringObjectContent (value, TRUE);
+ ExtOut ("\")");
+ return;
+ }
+ else if (IsMethodTable(dwMTAddr))
+ {
+ NameForMT_s (dwMTAddr, g_mdName,mdNameLen);
+ ExtOut (" (Object: %S)", g_mdName);
+ return;
+ }
+
+ // A MethodDesc?
+ if (IsMethodDesc(value))
+ {
+ NameForMD_s (value, g_mdName,mdNameLen);
+ ExtOut (" (MD: %S)", g_mdName);
+ return;
+ }
+
+ // A JitHelper?
+ const char* name = HelperFuncName(value);
+ if (name) {
+ ExtOut (" (JitHelp: %s)", name);
+ return;
+ }
+
+ // A call to managed code?
+ // ARM64TODO: not (yet) implemented. perhaps we don't need it at all.
+
+ // Random symbol.
+ char Symbol[1024];
+ if (SUCCEEDED(g_ExtSymbols->GetNameByOffset(TO_CDADDR(value), Symbol, 1024,
+ NULL, NULL)))
+ {
+ if (Symbol[0] != '\0')
+ {
+ ExtOut (" (%s)", Symbol);
+ return;
+ }
+ }
+
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Unassembly a managed code. Translating managed object, *
+* call. *
+* *
+\**********************************************************************/
+void ARM64Machine::Unassembly (
+ TADDR PCBegin,
+ TADDR PCEnd,
+ TADDR PCAskedFor,
+ TADDR GCStressCodeCopy,
+ GCEncodingInfo *pGCEncodingInfo,
+ SOSEHInfo *pEHInfo,
+ BOOL bSuppressLines,
+ BOOL bDisplayOffsets) const
+{
+ TADDR PC = PCBegin;
+ char line[1024];
+ ULONG lineNum;
+ ULONG curLine = -1;
+ WCHAR fileName[MAX_LONGPATH];
+ char *ptr;
+ INT_PTR accumulatedConstant = 0;
+ BOOL loBitsSet = FALSE;
+ BOOL hiBitsSet = FALSE;
+ char *szConstant = NULL;
+
+
+ while(PC < PCEnd)
+ {
+ ULONG_PTR currentPC = PC;
+ DisasmAndClean (PC, line, _countof(line));
+
+ // This is the closing of the previous run.
+ // Check the next instruction. if it's not a the last movk, handle the accumulated value
+ // else simply print a new line.
+ if (loBitsSet && hiBitsSet)
+ {
+ ptr = line;
+ // Advance to the instruction encoding
+ NextTerm(ptr);
+ // Advance to the opcode
+ NextTerm(ptr);
+ // if it's not movk, handle the accumulated value
+ // otherwise simply print the new line. The constant in this expression will be
+ // accumulated below.
+ if (strncmp(ptr, "movk ", 5))
+ {
+ HandleValue(accumulatedConstant);
+ accumulatedConstant = 0;
+ }
+ ExtOut ("\n");
+ }
+ else if (currentPC != PCBegin)
+ {
+ ExtOut ("\n");
+ }
+
+ // This is the new instruction
+
+ if (IsInterrupt())
+ return;
+ //
+ // Print out line numbers if needed
+ //
+ if (!bSuppressLines &&
+ SUCCEEDED(GetLineByOffset(TO_CDADDR(currentPC), &lineNum, fileName, MAX_LONGPATH)))
+ {
+ if (lineNum != curLine)
+ {
+ curLine = lineNum;
+ ExtOut("\n%S @ %d:\n", fileName, lineNum);
+ }
+ }
+
+ //
+ // Print out any GC information corresponding to the current instruction offset.
+ //
+ if (pGCEncodingInfo)
+ {
+ SIZE_T curOffset = (currentPC - PCBegin) + pGCEncodingInfo->hotSizeToAdd;
+ while ( !pGCEncodingInfo->fDoneDecoding
+ && pGCEncodingInfo->ofs <= curOffset)
+ {
+ ExtOut(pGCEncodingInfo->buf);
+ ExtOut("\n");
+ SwitchToFiber(pGCEncodingInfo->pvGCTableFiber);
+ }
+ }
+
+ //
+ // Print out any EH info corresponding to the current offset
+ //
+ if (pEHInfo)
+ {
+ pEHInfo->FormatForDisassembly(currentPC - PCBegin);
+ }
+
+ if (currentPC == PCAskedFor)
+ {
+ ExtOut (">>> ");
+ }
+
+ //
+ // Print offsets, in addition to actual address.
+ //
+ if (bDisplayOffsets)
+ {
+ ExtOut("%04x ", currentPC - PCBegin);
+ }
+
+ // look at the disassembled bytes
+ ptr = line;
+ NextTerm (ptr);
+
+ //
+ // If there is gcstress info for this method, and this is a 'hlt'
+ // instruction, then gcstress probably put the 'hlt' there. Look
+ // up the original instruction and print it instead.
+ //
+
+
+ if ( GCStressCodeCopy
+ && ( !strncmp (ptr, "badc0de0", 8)
+ || !strncmp (ptr, "badc0de1", 8)
+ || !strncmp (ptr, "badc0de2", 8)
+ ))
+ {
+ ULONG_PTR InstrAddr = currentPC;
+
+ //
+ // Compute address into saved copy of the code, and
+ // disassemble the original instruction
+ //
+
+ ULONG_PTR OrigInstrAddr = GCStressCodeCopy + (InstrAddr - PCBegin);
+ ULONG_PTR OrigPC = OrigInstrAddr;
+
+ DisasmAndClean(OrigPC, line, _countof(line));
+
+ //
+ // Increment the real PC based on the size of the unmodifed
+ // instruction
+ //
+
+ PC = InstrAddr + (OrigPC - OrigInstrAddr);
+
+ //
+ // Print out real code address in place of the copy address
+ //
+
+ ExtOut("%08x`%08x ", (ULONG)(InstrAddr >> 32), (ULONG)InstrAddr);
+
+ ptr = line;
+ NextTerm (ptr);
+
+ //
+ // Print out everything after the code address, and skip the
+ // instruction bytes
+ //
+
+ ExtOut(ptr);
+
+ //
+ // Add an indicator that this address has not executed yet
+ //
+
+ ExtOut(" (gcstress)");
+ }
+ else
+ {
+ ExtOut (line);
+ }
+
+ // Now advance to the opcode
+ NextTerm (ptr);
+
+ if (!strncmp(ptr, "mov ", 4))
+ {
+ if ((szConstant = strchr(ptr, '#')) != NULL)
+ {
+ GetValueFromExpr(szConstant, accumulatedConstant);
+ loBitsSet = TRUE;
+ }
+ }
+ else if (!strncmp(ptr, "movk ", 5))
+ {
+ char *szShiftAmount = NULL;
+ INT_PTR shiftAmount = 0;
+ INT_PTR constant = 0;
+ if (((szShiftAmount = strrchr(ptr, '#')) != NULL) &&
+ ((szConstant = strchr(ptr, '#')) != NULL) &&
+ (szShiftAmount != szConstant) &&
+ (accumulatedConstant > 0)) // Misses when movk is succeeding mov reg, #0x0, which I don't think makes any sense
+ {
+ GetValueFromExpr(szShiftAmount, shiftAmount);
+ GetValueFromExpr(szConstant, constant);
+ accumulatedConstant += (constant<<shiftAmount);
+ hiBitsSet = TRUE;
+ }
+ }
+ else
+ {
+ accumulatedConstant = 0;
+ loBitsSet = hiBitsSet = FALSE;
+ if ((szConstant = strchr(ptr, '=')) != NULL)
+ {
+ // Some instruction fetched a PC-relative constant which the disassembler nicely decoded for
+ // us using the ARM convention =<constant>. Retrieve this value and see if it's interesting.
+ INT_PTR value;
+ GetValueFromExpr(szConstant, value);
+ HandleValue(value);
+ }
+
+
+ // ARM64TODO: we could possibly handle adr(p)/ldr pair too.
+ }
+
+ }
+ ExtOut ("\n");
+}
+
+
+// @ARMTODO: Figure out how to extract this information under CoreARM
+BOOL ARM64Machine::GetExceptionContext (TADDR stack, TADDR PC, TADDR *cxrAddr, CROSS_PLATFORM_CONTEXT * cxr,
+ TADDR * exrAddr, PEXCEPTION_RECORD exr) const
+{
+ _ASSERTE("ARM64:NYI");
+ return FALSE;
+}
+
+///
+/// Dump ARM GCInfo table
+///
+void ARM64Machine::DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const
+{
+ if (bPrintHeader)
+ {
+ ExtOut("Pointer table:\n");
+ }
+
+ ARM64GCDump::GCDump gcDump(gcInfoToken.Version, encBytes, 5, true);
+ gcDump.gcPrintf = gcPrintf;
+
+ gcDump.DumpGCTable(dac_cast<PTR_BYTE>(gcInfoToken.Info), methodSize, 0);
+}
+
diff --git a/src/ToolBox/SOS/Strike/disasmX86.cpp b/src/ToolBox/SOS/Strike/disasmX86.cpp
new file mode 100644
index 0000000000..36a08d20a3
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/disasmX86.cpp
@@ -0,0 +1,1707 @@
+// 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 "strike.h"
+#include "util.h"
+#include "disasm.h"
+#include <dbghelp.h>
+
+#include "../../../inc/corhdr.h"
+#include "../../../inc/cor.h"
+#include "../../../inc/dacprivate.h"
+
+
+#if defined(SOS_TARGET_X86) && defined(SOS_TARGET_AMD64)
+#error This file does not support SOS targeting both X86 and AMD64 debuggees
+#endif
+
+#if !defined(SOS_TARGET_X86) && !defined(SOS_TARGET_AMD64)
+#error This file should be used to support SOS targeting either X86 or AMD64 debuggees
+#endif
+
+
+// These must be in the same order as they are used in the instruction
+// encodings/same as the CONTEXT field order.
+enum RegIndex
+{
+ EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI,
+
+#ifdef _TARGET_AMD64_
+ R8, R9, R10, R11, R12, R13, R14, R15,
+#endif // _TARGET_AMD64_
+
+ EIP, NONE
+};
+
+const int NumReg = NONE;
+struct Register
+{
+ TADDR value;
+ BOOL bValid;
+ TADDR stack;
+ BOOL bOnStack;
+};
+
+// Find the index for a register name
+inline RegIndex FindReg (___in __in_z char *ptr, __out_opt int *plen = NULL, __out_opt int *psize = NULL)
+{
+ struct RegName
+ {
+ RegIndex index;
+ PCSTR pszName;
+ int cchName;
+ int size;
+ };
+
+ static RegName rgRegNames[] = {
+
+#define REG(index, reg, size) { index, #reg, sizeof(#reg)-1, size }
+#define REG8(index, reg) REG(index, reg, 1)
+#define REG16(index, reg) REG(index, reg, 2)
+#define REG32(index, reg) REG(index, reg, 4)
+#define REG64(index, reg) REG(index, reg, 8)
+
+ REG8(EAX, al),
+ REG8(EAX, ah),
+ REG8(EBX, bl),
+ REG8(EBX, bh),
+ REG8(ECX, cl),
+ REG8(ECX, ch),
+ REG8(EDX, dl),
+ REG8(EDX, dh),
+
+ REG16(EAX, ax),
+ REG16(EBX, bx),
+ REG16(ECX, cx),
+ REG16(EDX, dx),
+ REG16(ESI, si),
+ REG16(EDI, di),
+ REG16(EBP, bp),
+ REG16(ESP, sp),
+
+ REG32(EAX, eax),
+ REG32(EBX, ebx),
+ REG32(ECX, ecx),
+ REG32(EDX, edx),
+ REG32(ESI, esi),
+ REG32(EDI, edi),
+ REG32(EBP, ebp),
+ REG32(ESP, esp),
+
+#ifdef _TARGET_AMD64_
+
+ REG8(R8, r8b),
+ REG8(R9, r9b),
+ REG8(R10, r10b),
+ REG8(R11, r11b),
+ REG8(R12, r12b),
+ REG8(R13, r13b),
+ REG8(R14, r14b),
+ REG8(R15, r15b),
+
+ REG16(R8, r8w),
+ REG16(R9, r9w),
+ REG16(R10, r10w),
+ REG16(R11, r11w),
+ REG16(R12, r12w),
+ REG16(R13, r13w),
+ REG16(R14, r14w),
+ REG16(R15, r15w),
+
+ REG32(R8, r8d),
+ REG32(R9, r9d),
+ REG32(R10, r10d),
+ REG32(R11, r11d),
+ REG32(R12, r12d),
+ REG32(R13, r13d),
+ REG32(R14, r14d),
+ REG32(R15, r15d),
+
+ REG64(EAX, rax),
+ REG64(EBX, rbx),
+ REG64(ECX, rcx),
+ REG64(EDX, rdx),
+ REG64(ESI, rsi),
+ REG64(EDI, rdi),
+ REG64(EBP, rbp),
+ REG64(ESP, rsp),
+ REG64(R8, r8),
+ REG64(R9, r9),
+ REG64(R10, r10),
+ REG64(R11, r11),
+ REG64(R12, r12),
+ REG64(R13, r13),
+ REG64(R14, r14),
+ REG64(R15, r15),
+
+#endif // _TARGET_AMD64_
+
+#undef REG
+#undef REG8
+#undef REG16
+#undef REG32
+#undef REG64
+
+ };
+
+ for (size_t i = 0; i < sizeof(rgRegNames)/sizeof(rgRegNames[0]); i++)
+ {
+ if (!strncmp(ptr, rgRegNames[i].pszName, rgRegNames[i].cchName))
+ {
+ if (psize)
+ *psize = rgRegNames[i].size;
+
+ if (plen)
+ *plen = rgRegNames[i].cchName;
+
+ return rgRegNames[i].index;
+ }
+ }
+
+ return NONE;
+}
+
+// Find the value of an expression.
+inline BOOL FindSrc (__in_z char *ptr, ___in Register *reg, INT_PTR &value, BOOL &bDigit)
+{
+ if (GetValueFromExpr (ptr, value))
+ {
+ bDigit = TRUE;
+ return TRUE;
+ }
+
+ BOOL bValid = FALSE;
+ BOOL bByRef = IsByRef (ptr);
+ bDigit = FALSE;
+
+ int regnamelen;
+ RegIndex index = FindReg (ptr, &regnamelen);
+ if (index != NONE)
+ {
+ if (reg[index].bValid)
+ {
+ value = reg[index].value;
+ ptr += regnamelen;
+ // TODO: consider ecx+edi*4+0x4
+ if ((IsTermSep (ptr[0]) && !bByRef)
+ || (ptr[0] == ']' && bByRef))
+ {
+ bValid = TRUE;
+ if (bByRef)
+ SafeReadMemory (TO_TADDR(value), &value, sizeof(void*), NULL);
+ }
+ }
+ }
+ return bValid;
+}
+
+enum ADDRESSMODE {REG, DATA, INDIRECT, NODATA, BAD};
+
+struct RegState
+{
+ RegIndex reg;
+ BOOL bFullReg;
+ char scale;
+ int namelen;
+};
+
+struct InstData
+{
+ ADDRESSMODE mode;
+ RegState reg[2];
+ INT_PTR value;
+};
+
+void FindMainReg (___in __in_z char *ptr, RegState &reg)
+{
+ int size = 0;
+
+ reg.reg = FindReg(ptr, &reg.namelen, &size);
+
+ reg.bFullReg = (reg.reg!=NONE && sizeof(void*)==size) ? TRUE : FALSE;
+}
+
+static void DecodeAddressIndirect (___in __in_z char *term, InstData& arg)
+{
+ arg.mode = BAD;
+ arg.value = 0;
+ arg.reg[0].scale = 0;
+ arg.reg[1].scale = 0;
+
+ if (!IsByRef (term))
+ {
+ return;
+ }
+
+ // first part must be a reg
+ arg.reg[0].scale = 1;
+ if (term[0] == '+')
+ term ++;
+ else if (term[0] == '-')
+ {
+ term ++;
+ arg.reg[0].scale = -1;
+ }
+ if (isdigit(term[0]))
+ {
+ arg.reg[0].scale *= term[0]-'0';
+ term ++;
+ }
+
+ FindMainReg (term, arg.reg[0]);
+ if (arg.reg[0].reg == NONE)
+ return;
+ term += arg.reg[0].namelen;
+
+ if (term[0] == ']')
+ {
+ // It is [reg]
+ arg.mode = INDIRECT;
+ arg.value = 0;
+ return;
+ }
+
+ char sign = (char)((term[0] == '+')?1:-1);
+ term ++;
+ FindMainReg (term, arg.reg[1]);
+ if (arg.reg[1].reg != NONE)
+ {
+ // It is either [reg+reg*c] or [reg+reg*c+c]
+
+ term += arg.reg[1].namelen;
+
+ if (term[0] == '*')
+ {
+ term ++;
+ arg.reg[1].scale = sign*(term[0]-'0');
+ term ++;
+ }
+ else
+ arg.reg[1].scale = sign;
+
+ if (term[0] == ']')
+ {
+ // It is [reg+reg*c]
+ arg.mode = INDIRECT;
+ arg.value = 0;
+ return;
+ }
+ sign = (char)((term[0] == '+')?1:-1);
+ term ++;
+ }
+
+ char *endptr;
+ arg.value = strtoul(term, &endptr, 16);
+ if (endptr[0] == ']')
+ {
+ // It is [reg+reg*c+c]
+ arg.value *= sign;
+ arg.mode = INDIRECT;
+ }
+}
+
+void DecodeAddressTerm (___in __in_z char *term, InstData& arg)
+{
+ arg.mode = BAD;
+ arg.reg[0].scale = 0;
+ arg.reg[1].scale = 0;
+ arg.value = 0;
+ INT_PTR value;
+
+ if (GetValueFromExpr (term, value))
+ {
+ arg.value = value;
+ arg.mode = DATA;
+ }
+ else
+ {
+ FindMainReg (term, arg.reg[0]);
+ if (arg.reg[0].reg != NONE)
+ {
+ arg.mode = REG;
+ }
+ else
+ {
+ DecodeAddressIndirect (term, arg);
+ }
+ }
+}
+
+// Return 0 for non-managed call. Otherwise return MD address.
+TADDR MDForCall (TADDR callee)
+{
+ // call managed code?
+ JITTypes jitType;
+ TADDR methodDesc;
+ TADDR IP = callee;
+ TADDR gcinfoAddr;
+
+ if (!GetCalleeSite (callee, IP))
+ return 0;
+
+ IP2MethodDesc (IP, methodDesc, jitType, gcinfoAddr);
+ if (methodDesc)
+ {
+ return methodDesc;
+ }
+
+ // jmp stub
+ char line[256];
+ DisasmAndClean (IP, line, 256);
+ char *ptr = line;
+ NextTerm (ptr);
+ NextTerm (ptr);
+ if (!strncmp (ptr, "jmp ", 4))
+ {
+ // jump thunk
+ NextTerm (ptr);
+ INT_PTR value;
+ methodDesc = 0;
+ if (GetValueFromExpr (ptr, value))
+ {
+ IP2MethodDesc (value, methodDesc, jitType, gcinfoAddr);
+ }
+ return methodDesc;
+ }
+ return 0;
+}
+
+// Handle a call instruction.
+void HandleCall(TADDR callee, Register *reg)
+{
+ // call managed code?
+ TADDR methodDesc = MDForCall (callee);
+ if (methodDesc)
+ {
+ DacpMethodDescData MethodDescData;
+ if (MethodDescData.Request(g_sos, TO_CDADDR(methodDesc)) == S_OK)
+ {
+ NameForMD_s(methodDesc, g_mdName,mdNameLen);
+ ExtOut(" (%S, mdToken: %p)", g_mdName, SOS_PTR(MethodDescData.MDToken));
+ return;
+ }
+ }
+
+#ifdef _TARGET_AMD64_
+ // A jump thunk?
+
+ CONTEXT ctx = {0};
+
+ ctx.ContextFlags = (CONTEXT_AMD64 | CONTEXT_CONTROL | CONTEXT_INTEGER);
+
+ for (unsigned ireg = 0; ireg < 16; ireg++)
+ {
+ if (reg[ireg].bValid)
+ {
+ *(&ctx.Rax + ireg) = reg[ireg].value;
+ }
+ }
+
+ ctx.Rip = callee;
+
+ CLRDATA_ADDRESS ip = 0, md = 0;
+ if (S_OK == g_sos->GetJumpThunkTarget(&ctx, &ip, &md))
+ {
+ if (md)
+ {
+ DacpMethodDescData MethodDescData;
+ if (MethodDescData.Request(g_sos, md) == S_OK)
+ {
+ NameForMD_s(md, g_mdName,mdNameLen);
+ ExtOut(" (%S, mdToken: %p)", g_mdName, SOS_PTR(MethodDescData.MDToken));
+ return;
+ }
+ }
+
+ if (ip != callee)
+ {
+ return HandleCall(ip, reg);
+ }
+ }
+#endif // _TARGET_AMD64_
+
+ // A JitHelper?
+ const char* name = HelperFuncName(callee);
+ if (name) {
+ ExtOut (" (JitHelp: %s)", name);
+ return;
+ }
+
+ // call unmanaged code?
+ char Symbol[1024];
+ if (SUCCEEDED(g_ExtSymbols->GetNameByOffset(TO_CDADDR(callee), Symbol, 1024,
+ NULL, NULL)))
+ {
+ if (Symbol[0] != '\0')
+ {
+ ExtOut (" (%s)", Symbol);
+ return;
+ }
+ }
+}
+
+// Determine if a value is MT/MD/Obj
+void HandleValue(TADDR value)
+{
+ // A MethodTable?
+ if (IsMethodTable(value))
+ {
+ NameForMT_s (value, g_mdName,mdNameLen);
+ ExtOut (" (MT: %S)", g_mdName);
+ return;
+ }
+
+ // A Managed Object?
+ TADDR dwMTAddr;
+ move_xp (dwMTAddr, value);
+ if (IsStringObject(value))
+ {
+ ExtOut (" (\"");
+ StringObjectContent (value, TRUE);
+ ExtOut ("\")");
+ return;
+ }
+ else if (IsMethodTable(dwMTAddr))
+ {
+ NameForMT_s (dwMTAddr, g_mdName,mdNameLen);
+ ExtOut (" (Object: %S)", g_mdName);
+ return;
+ }
+
+ // A MethodDesc?
+ if (IsMethodDesc(value))
+ {
+ NameForMD_s (value, g_mdName,mdNameLen);
+ ExtOut (" (MD: %S)", g_mdName);
+ return;
+ }
+
+ // A JitHelper?
+ const char* name = HelperFuncName(value);
+ if (name) {
+ ExtOut (" (JitHelp: %s)", name);
+ return;
+ }
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Unassembly a managed code. Translating managed object, *
+* call. *
+* *
+\**********************************************************************/
+void
+#ifdef _TARGET_X86_
+ X86Machine::Unassembly
+#elif defined(_TARGET_AMD64_)
+ AMD64Machine::Unassembly
+#endif
+ (TADDR IPBegin,
+ TADDR IPEnd,
+ TADDR IPAskedFor,
+ TADDR GCStressCodeCopy,
+ GCEncodingInfo *pGCEncodingInfo,
+ SOSEHInfo *pEHInfo,
+ BOOL bSuppressLines,
+ BOOL bDisplayOffsets) const
+{
+ ULONG_PTR IP = IPBegin;
+ char line[1024];
+ Register reg [NumReg];
+ ZeroMemory (reg, sizeof(reg));
+ RegIndex dest;
+ INT_PTR value;
+ BOOL bDigit;
+ char *ptr;
+
+ ULONG curLine = -1;
+ WCHAR filename[MAX_LONGPATH];
+ ULONG linenum;
+
+ while (IP < IPEnd)
+ {
+ if (IsInterrupt())
+ return;
+
+ // Print out line numbers if needed
+ if (!bSuppressLines
+ && SUCCEEDED(GetLineByOffset(TO_CDADDR(IP), &linenum, filename, MAX_LONGPATH)))
+ {
+ if (linenum != curLine)
+ {
+ curLine = linenum;
+ ExtOut("\n%S @ %d:\n", filename, linenum);
+ }
+ }
+
+ //
+ // Print out any GC information corresponding to the current instruction offset.
+ //
+
+#ifndef FEATURE_PAL
+ if (pGCEncodingInfo)
+ {
+ SIZE_T curOffset = (IP - IPBegin) + pGCEncodingInfo->hotSizeToAdd;
+ while ( !pGCEncodingInfo->fDoneDecoding
+ && pGCEncodingInfo->ofs <= curOffset)
+ {
+ ExtOut(pGCEncodingInfo->buf);
+ ExtOut("\n");
+ SwitchToFiber(pGCEncodingInfo->pvGCTableFiber);
+ }
+ }
+#endif // FEATURE_PAL
+
+ ULONG_PTR InstrAddr = IP;
+
+ //
+ // Print out any EH info corresponding to the current offset
+ //
+ if (pEHInfo)
+ {
+ pEHInfo->FormatForDisassembly(IP - IPBegin);
+ }
+
+ if (IP == IPAskedFor)
+ {
+ ExtOut (">>> ");
+ }
+
+ //
+ // Print offsets, in addition to actual address.
+ //
+ if (bDisplayOffsets)
+ {
+ ExtOut("%04x ", IP - IPBegin);
+ }
+
+ DisasmAndClean (IP, line, _countof(line));
+
+ // look at key word
+ ptr = line;
+ NextTerm (ptr);
+ NextTerm (ptr);
+
+ //
+ // If there is gcstress info for this method, and this is a 'hlt'
+ // instruction, then gcstress probably put the 'hlt' there. Look
+ // up the original instruction and print it instead.
+ //
+
+ SSIZE_T cbIPOffset = 0;
+
+ if ( GCStressCodeCopy
+ && ( !strncmp (ptr, "hlt", 3)
+ || !strncmp (ptr, "cli", 3)
+ || !strncmp (ptr, "sti", 3)))
+ {
+ //
+ // Compute address into saved copy of the code, and
+ // disassemble the original instruction
+ //
+
+ ULONG_PTR OrigInstrAddr = GCStressCodeCopy + (InstrAddr - IPBegin);
+ ULONG_PTR OrigIP = OrigInstrAddr;
+
+ DisasmAndClean(OrigIP, line, _countof(line));
+
+ //
+ // Increment the real IP based on the size of the unmodifed
+ // instruction
+ //
+
+ IP = InstrAddr + (OrigIP - OrigInstrAddr);
+
+ cbIPOffset = IP - OrigIP;
+
+ //
+ // Print out real code address in place of the copy address
+ //
+
+#ifdef _WIN64
+ ExtOut("%08x`%08x ", (ULONG)(InstrAddr >> 32), (ULONG)InstrAddr);
+#else
+ ExtOut("%08x ", (ULONG)InstrAddr);
+#endif
+
+ ptr = line;
+ NextTerm (ptr);
+
+ //
+ // Print out everything after the code address, and skip the
+ // instruction bytes
+ //
+
+ ExtOut(ptr);
+
+ NextTerm (ptr);
+
+ //
+ // Add an indicator that this address has not executed yet
+ //
+
+ ExtOut(" (gcstress)");
+ }
+ else
+ {
+ ExtOut (line);
+ }
+
+ if (!strncmp (ptr, "mov ", 4))
+ {
+ NextTerm (ptr);
+
+ dest = FindReg(ptr);
+ if (dest != NONE)
+ {
+ NextTerm (ptr);
+
+ if (FindSrc (ptr, reg, value, bDigit))
+ {
+ reg[dest].bValid = TRUE;
+ reg[dest].value = value;
+ // Is it a managed obj
+ if (bDigit)
+ HandleValue (reg[dest].value);
+ }
+ else
+ {
+ reg[dest].bValid = FALSE;
+ }
+ }
+ }
+ else if (!strncmp (ptr, "call ", 5))
+ {
+ NextTerm (ptr);
+ if (FindSrc (ptr, reg, value, bDigit))
+ {
+ if (bDigit)
+ value += cbIPOffset;
+
+ HandleCall (value, reg);
+ }
+
+ // trash EAX, ECX, EDX
+ reg[EAX].bValid = FALSE;
+ reg[ECX].bValid = FALSE;
+ reg[EDX].bValid = FALSE;
+
+#ifdef _TARGET_AMD64_
+ reg[R8].bValid = FALSE;
+ reg[R9].bValid = FALSE;
+ reg[R10].bValid = FALSE;
+ reg[R11].bValid = FALSE;
+#endif // _TARGET_AMD64_
+ }
+ else if (!strncmp (ptr, "lea ", 4))
+ {
+ NextTerm (ptr);
+ dest = FindReg(ptr);
+ if (dest != NONE)
+ {
+ NextTerm (ptr);
+ if (FindSrc (ptr, reg, value, bDigit))
+ {
+ reg[dest].bValid = TRUE;
+ reg[dest].value = value;
+ }
+ else
+ {
+ reg[dest].bValid = FALSE;
+ }
+ }
+ }
+ else if (!strncmp (ptr, "push ", 5))
+ {
+ // do not do anything
+ NextTerm (ptr);
+ if (FindSrc (ptr, reg, value, bDigit))
+ {
+ if (bDigit)
+ {
+ HandleValue (value);
+ }
+ }
+ }
+ else
+ {
+ // assume this instruction will trash dest reg
+ NextTerm (ptr);
+ dest = FindReg(ptr);
+ if (dest != NONE)
+ reg[dest].bValid = FALSE;
+ }
+ ExtOut ("\n");
+ }
+
+ //
+ // Print out any "end" EH info (where the end address is the byte immediately following the last instruction)
+ //
+ if (pEHInfo)
+ {
+ pEHInfo->FormatForDisassembly(IP - IPBegin);
+ }
+}
+
+// Find the real callee site. Handle JMP instruction.
+// Return TRUE if we get the address, FALSE if not.
+BOOL GetCalleeSite (TADDR IP, TADDR &IPCallee)
+{
+ while (TRUE) {
+ unsigned char inst[2];
+ if (g_ExtData->ReadVirtual(TO_CDADDR(IP), inst, sizeof(inst), NULL) != S_OK)
+ {
+ return FALSE;
+ }
+ if (inst[0] == 0xEB) {
+ IP += 2+(char)inst[1];
+ }
+ else if (inst[0] == 0xE9) {
+ int displace;
+ if (g_ExtData->ReadVirtual(TO_CDADDR(IP+1), &displace, sizeof(displace), NULL) != S_OK)
+ {
+ return FALSE;
+ }
+ else
+ {
+ IP += 5+displace;
+ }
+ }
+ else if (inst[0] == 0xFF && (inst[1] & 070) == 040) {
+ if (inst[1] == 0x25) {
+ DWORD displace;
+ if (g_ExtData->ReadVirtual(TO_CDADDR(IP+2), &displace, sizeof(displace), NULL) != S_OK)
+ {
+ return FALSE;
+ }
+ if (g_ExtData->ReadVirtual(TO_CDADDR(displace), &displace, sizeof(displace), NULL) != S_OK)
+ {
+ return FALSE;
+ }
+ else
+ {
+ IP = displace;
+ }
+ }
+ else
+ // Target for jmp is determined from register values.
+ return FALSE;
+ }
+ else
+ {
+ IPCallee = IP;
+ return TRUE;
+ }
+ }
+}
+
+// GetFinalTarget is based on HandleCall, but avoids printing anything to the output.
+// This is currently only called on x64
+eTargetType GetFinalTarget(TADDR callee, TADDR* finalMDorIP)
+{
+ // call managed code?
+ TADDR methodDesc = MDForCall (callee);
+ if (methodDesc)
+ {
+ DacpMethodDescData MethodDescData;
+ if (MethodDescData.Request(g_sos, TO_CDADDR(methodDesc)) == S_OK)
+ {
+ *finalMDorIP = methodDesc;
+ return ettMD;
+ }
+ }
+
+#ifdef _TARGET_AMD64_
+ // A jump thunk?
+
+ CONTEXT ctx = {0};
+ ctx.ContextFlags = (CONTEXT_AMD64 | CONTEXT_CONTROL | CONTEXT_INTEGER);
+ ctx.Rip = callee;
+
+ CLRDATA_ADDRESS ip = 0, md = 0;
+ if (S_OK == g_sos->GetJumpThunkTarget(&ctx, &ip, &md))
+ {
+ if (md)
+ {
+ DacpMethodDescData MethodDescData;
+ if (MethodDescData.Request(g_sos, md) == S_OK)
+ {
+ *finalMDorIP = md;
+ return ettStub;
+ }
+ }
+
+ if (ip != callee)
+ {
+ return GetFinalTarget(ip, finalMDorIP);
+ }
+ }
+#endif // _TARGET_AMD64_
+
+ // A JitHelper?
+ const char* name = HelperFuncName(callee);
+ if (name) {
+ *finalMDorIP = callee;
+ return ettJitHelp;
+ }
+
+ // call unmanaged code?
+ *finalMDorIP = callee;
+ return ettNative;
+}
+
+#ifndef FEATURE_PAL
+
+void ExpFuncStateInit (TADDR *IPRetAddr)
+{
+ ULONG64 offset;
+ if (FAILED(g_ExtSymbols->GetOffsetByName("ntdll!KiUserExceptionDispatcher", &offset))) {
+ return;
+ }
+
+ // test if we have a minidump for which the image is not cached anymore. this avoids
+ // the having the while loop below spin forever (or a very long time)...
+ // (Watson backend hit this a few times, and they had to institute a timeout policy
+ // to work around this)
+ SIZE_T instrs;
+ if (FAILED(g_ExtData->ReadVirtual(offset, &instrs, sizeof(instrs), NULL)) || instrs == 0) {
+ return;
+ }
+
+ char line[256];
+ int i = 0;
+ int cnt = 0;
+#ifdef SOS_TARGET_X86
+ // On x86 and x64 the last 3 "call" instructions in ntdll!KiUserExceptionDispatcher
+ // are making calls to OS APIs that take as argument the context record (and some
+ // of them the exception record as well)
+ const int cCallInstrs = 3;
+#elif defined(SOS_TARGET_AMD64)
+ // On x64 the first "call" instruction should be considered, as well
+ const int cCallInstrs = 4;
+#endif
+
+ while (i < cCallInstrs) {
+ g_ExtControl->Disassemble (offset, 0, line, 256, NULL, &offset);
+ if (strstr (line, "call")) {
+ IPRetAddr[i++] = (TADDR)offset;
+ }
+ // if we didn't find at least one "call" in the first 500 instructions give up...
+ if (++cnt >= 500 && IPRetAddr[0] == 0)
+ break;
+ }
+}
+
+#endif // FEATURE_PAL
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to fill in a cross platform context *
+* struct by looking on the stack for return addresses into *
+* KiUserExceptionDispatcher *
+* *
+\**********************************************************************/
+BOOL
+#ifdef SOS_TARGET_X86
+ X86Machine::GetExceptionContext
+#elif defined(SOS_TARGET_AMD64)
+ AMD64Machine::GetExceptionContext
+#endif
+ (TADDR stack,
+ TADDR IP,
+ TADDR * cxrAddr,
+ CROSS_PLATFORM_CONTEXT * pcxr,
+ TADDR * exrAddr,
+ PEXCEPTION_RECORD exr) const
+{
+#ifndef FEATURE_PAL
+#ifdef SOS_TARGET_X86
+ X86_CONTEXT * cxr = &pcxr->X86Context;
+ size_t contextSize = offsetof(CONTEXT, ExtendedRegisters);
+#elif defined(SOS_TARGET_AMD64)
+ AMD64_CONTEXT * cxr = &pcxr->Amd64Context;
+ size_t contextSize = offsetof(CONTEXT, FltSave);
+#endif
+
+ static TADDR IPRetAddr[4] = {0, 0, 0, 0};
+
+ if (IPRetAddr[0] == 0) {
+ ExpFuncStateInit (IPRetAddr);
+ }
+ *cxrAddr = 0;
+ *exrAddr = 0;
+
+#ifdef SOS_TARGET_X86
+
+ if (IP == IPRetAddr[0]) {
+ *exrAddr = stack + sizeof(TADDR);
+ *cxrAddr = stack + 2*sizeof(TADDR);
+ }
+ else if (IP == IPRetAddr[1]) {
+ *cxrAddr = stack + sizeof(TADDR);
+ }
+ else if (IP == IPRetAddr[2]) {
+ *exrAddr = stack + sizeof(TADDR);
+ *cxrAddr = stack + 2*sizeof(TADDR);
+ }
+ else
+ return FALSE;
+
+ if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(*cxrAddr), &stack, sizeof(stack), NULL)))
+ return FALSE;
+ *cxrAddr = stack;
+
+ //if ((pContext->ContextFlags & CONTEXT_EXTENDED_REGISTERS) == CONTEXT_EXTENDED_REGISTERS)
+ // contextSize += sizeof(pContext->ExtendedRegisters);
+ if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(stack), cxr, (ULONG)contextSize, NULL))) {
+ return FALSE;
+ }
+
+ if (*exrAddr) {
+ if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(*exrAddr), &stack, sizeof(stack), NULL)))
+ {
+ *exrAddr = 0;
+ return TRUE;
+ }
+ *exrAddr = stack;
+ size_t erSize = offsetof (EXCEPTION_RECORD, ExceptionInformation);
+ if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(stack), exr, (ULONG)erSize, NULL))) {
+ *exrAddr = 0;
+ return TRUE;
+ }
+ }
+
+#elif defined(SOS_TARGET_AMD64)
+
+ if (IP == IPRetAddr[0] || IP == IPRetAddr[1] || IP == IPRetAddr[3]) {
+ *exrAddr = stack + sizeof(TADDR) + 0x4F0;
+ *cxrAddr = stack + sizeof(TADDR);
+ } else if (IP == IPRetAddr[2]) {
+ *cxrAddr = stack + sizeof(TADDR);
+ }
+ else {
+ return FALSE;
+ }
+
+ if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(*cxrAddr), cxr, (ULONG)contextSize, NULL))) {
+ return FALSE;
+ }
+
+ if (*exrAddr) {
+ size_t erSize = offsetof (EXCEPTION_RECORD, ExceptionInformation);
+ if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(*exrAddr), exr, (ULONG)erSize, NULL))) {
+ *exrAddr = 0;
+ return TRUE;
+ }
+ }
+
+#endif
+ return TRUE;
+#else
+ return FALSE;
+#endif // FEATURE_PAL
+}
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to determine if a DWORD on the stack is *
+* a return address. *
+* It does this by checking several bytes before the DWORD to see if *
+* there is a call instruction. *
+* *
+\**********************************************************************/
+
+void
+#ifdef _TARGET_X86_
+ X86Machine::IsReturnAddress
+#elif defined(_TARGET_AMD64_)
+ AMD64Machine::IsReturnAddress
+#endif
+ (TADDR retAddr, TADDR* whereCalled) const
+{
+ *whereCalled = 0;
+
+ unsigned char spotend[6];
+ move_xp (spotend, retAddr-6);
+ unsigned char *spot = spotend+6;
+ TADDR addr;
+
+ // Note this is possible to be spoofed, but pretty unlikely
+ // call XXXXXXXX
+ if (spot[-5] == 0xE8) {
+ DWORD offs = 0;
+ move_xp (offs, retAddr-4);
+ *whereCalled = retAddr + (ULONG64)(LONG)(offs);
+ //*whereCalled = *((int*) (retAddr-4)) + retAddr;
+ // on WOW64 the range valid for code is almost the whole 4GB adddress space
+ if (g_ExtData->ReadVirtual(TO_CDADDR(*whereCalled), &addr, sizeof(addr), NULL) == S_OK)
+ {
+ TADDR callee;
+ if (GetCalleeSite(*whereCalled, callee)) {
+ *whereCalled = callee;
+ }
+ return;
+ }
+ else
+ *whereCalled = 0;
+ }
+
+ // call [XXXXXXXX]
+ if (spot[-6] == 0xFF && (spot[-5] == 025)) {
+ DWORD offs = 0;
+ move_xp (offs, retAddr-4);
+#ifdef _TARGET_AMD64_
+ // on x64 this 32-bit is an RIP offset
+ addr = retAddr + (ULONG64)(LONG)(offs);
+#elif defined (_TARGET_X86_)
+ addr = offs;
+#endif
+ if (g_ExtData->ReadVirtual(TO_CDADDR(addr), whereCalled, sizeof(*whereCalled), NULL) == S_OK) {
+ move_xp (*whereCalled, addr);
+ //*whereCalled = **((unsigned**) (retAddr-4));
+ // on WOW64 the range valid for code is almost the whole 4GB adddress space
+ if (g_ExtData->ReadVirtual(TO_CDADDR(*whereCalled), &addr, sizeof(addr), NULL) == S_OK)
+ {
+ TADDR callee;
+ if (GetCalleeSite(*whereCalled,callee)) {
+ *whereCalled = callee;
+ }
+ return;
+ }
+ else
+ *whereCalled = 0;
+ }
+ else
+ *whereCalled = 0;
+ }
+
+ // call [REG+XX]
+ if (spot[-3] == 0xFF && (spot[-2] & ~7) == 0120 && (spot[-2] & 7) != 4)
+ {
+ *whereCalled = 0xFFFFFFFF;
+ return;
+ }
+ if (spot[-4] == 0xFF && spot[-3] == 0124)
+ {
+ *whereCalled = 0xFFFFFFFF;
+ return;
+ }
+
+ // call [REG+XXXX]
+ if (spot[-6] == 0xFF && (spot[-5] & ~7) == 0220 && (spot[-5] & 7) != 4)
+ {
+ *whereCalled = 0xFFFFFFFF;
+ return;
+ }
+ if (spot[-7] == 0xFF && spot[-6] == 0224)
+ {
+ *whereCalled = 0xFFFFFFFF;
+ return;
+ }
+
+ // call [REG]
+ if (spot[-2] == 0xFF && (spot[-1] & ~7) == 0020 && (spot[-1] & 7) != 4 && (spot[-1] & 7) != 5)
+ {
+ *whereCalled = 0xFFFFFFFF;
+ return;
+ }
+
+ // call REG
+ if (spot[-2] == 0xFF && (spot[-1] & ~7) == 0320 && (spot[-1] & 7) != 4)
+ {
+ *whereCalled = 0xFFFFFFFF;
+ return;
+ }
+
+ // There are other cases, but I don't believe they are used.
+ return;
+}
+
+
+#ifdef _X86_
+
+///
+/// This is dead code, not called from anywhere, not linked in the final product.
+///
+static BOOL DecodeLine (___in __in_z char *line, ___in __in_z const char *const inst, InstData& arg1, InstData& arg2)
+{
+ char *ptr = line;
+ if (inst[0] == '*' || !strncmp (ptr, inst, strlen (inst)))
+ {
+ arg1.mode = BAD;
+ arg2.mode = BAD;
+ NextTerm (ptr);
+ if (*ptr == '\0')
+ {
+ arg1.mode = NODATA;
+ return TRUE;
+ }
+
+ DecodeAddressTerm (ptr, arg1);
+ NextTerm (ptr);
+ if (*ptr == '\0')
+ {
+ return TRUE;
+ }
+ DecodeAddressTerm (ptr, arg2);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+void PrintReg (Register *reg)
+{
+ ExtOut ("[EBX=%08x ESI=%08x EDI=%08x EBP=%08x ESP=%08x]\n",
+ reg[EBX].value, reg[ESI].value, reg[EDI].value, reg[EBP].value,
+ reg[ESP].value);
+}
+
+
+struct CallInfo
+{
+ DWORD_PTR stackPos;
+ DWORD_PTR retAddr;
+ DWORD_PTR whereCalled;
+};
+
+// Search for a Return address on stack.
+BOOL GetNextRetAddr (DWORD_PTR stackBegin, DWORD_PTR stackEnd,
+ CallInfo &callInfo)
+{
+ for (callInfo.stackPos = stackBegin;
+ callInfo.stackPos <= stackEnd;
+ callInfo.stackPos += 4)
+ {
+ if (!SafeReadMemory (callInfo.stackPos, &callInfo.retAddr, 4, NULL))
+ continue;
+
+ g_targetMachine->IsReturnAddress(callInfo.retAddr, &callInfo.whereCalled);
+ if (callInfo.whereCalled)
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+struct FrameInfo
+{
+ DWORD_PTR IPStart;
+ DWORD_PTR Prolog;
+ DWORD_PTR FrameBase; // The value of ESP at the entry.
+ DWORD_PTR StackEnd;
+ DWORD_PTR argCount;
+ BOOL bEBPFrame;
+};
+
+// if a EBP frame, return TRUE if EBP has been setup
+void GetFrameBaseHelper (DWORD_PTR IPBegin, DWORD_PTR IPEnd,
+ INT_PTR &StackChange)
+{
+ char line[256];
+ char *ptr;
+ InstData arg1;
+ InstData arg2;
+ DWORD_PTR IP = IPBegin;
+ StackChange = 0;
+ while (IP < IPEnd)
+ {
+ DisasmAndClean (IP, line, 256);
+ ptr = line;
+ NextTerm (ptr);
+ NextTerm (ptr);
+ if (DecodeLine (ptr, "push ", arg1, arg2))
+ {
+ StackChange += 4;
+ }
+ else if (DecodeLine (ptr, "pop ", arg1, arg2))
+ {
+ StackChange -= 4;
+ }
+ else if (DecodeLine (ptr, "sub ", arg1, arg2))
+ {
+ if (arg1.mode == REG && arg1.reg[0].reg == ESP)
+ {
+ if (arg2.mode == DATA)
+ StackChange -= arg2.value;
+ }
+ }
+ else if (DecodeLine (ptr, "add ", arg1, arg2))
+ {
+ if (arg1.mode == REG && arg1.reg[0].reg == ESP)
+ {
+ if (arg2.mode == DATA)
+ StackChange += arg2.value;
+ }
+ }
+ else if (!strncmp (ptr, "ret", 3)) {
+ return;
+ }
+ }
+}
+
+enum IPSTATE {IPPROLOG1 /*Before EBP set*/, IPPROLOG2 /*After EBP set*/, IPCODE, IPEPILOG, IPEND};
+
+IPSTATE GetIpState (DWORD_PTR IP, FrameInfo* pFrame)
+{
+ char line[256];
+ char *ptr;
+
+ if (IP >= pFrame->IPStart && IP < pFrame->IPStart + pFrame->Prolog)
+ {
+ if (pFrame->bEBPFrame) {
+ DWORD_PTR pIP = pFrame->IPStart;
+ while (pIP < IP) {
+ DisasmAndClean (IP,line, 256);
+ ptr = line;
+ NextTerm (ptr);
+ NextTerm (ptr);
+ if (!strncmp (ptr, "mov ", 4)) {
+ NextTerm (ptr);
+ if (!strncmp (ptr, "ebp", 3)) {
+ NextTerm (ptr);
+ if (!strncmp (ptr, "esp", 3)) {
+ return IPPROLOG2;
+ }
+ }
+ }
+ else if (!strncmp (ptr, "call ", 5)) {
+ NextTerm (ptr);
+ if (strstr (ptr, "__EH_prolog")) {
+ return IPPROLOG2;
+ }
+ }
+ }
+ pIP = IP;
+ while (pIP < pFrame->IPStart + pFrame->Prolog) {
+ DisasmAndClean (IP,line, 256);
+ ptr = line;
+ NextTerm (ptr);
+ NextTerm (ptr);
+ if (!strncmp (ptr, "mov ", 4)) {
+ NextTerm (ptr);
+ if (!strncmp (ptr, "ebp", 3)) {
+ NextTerm (ptr);
+ if (!strncmp (ptr, "esp", 3)) {
+ return IPPROLOG1;
+ }
+ }
+ }
+ else if (!strncmp (ptr, "call ", 5)) {
+ NextTerm (ptr);
+ if (strstr (ptr, "__EH_prolog")) {
+ return IPPROLOG1;
+ }
+ }
+ }
+
+ ExtOut ("Fail to find where EBP is saved\n");
+ return IPPROLOG2;
+ }
+ else
+ {
+ return IPPROLOG1;
+ }
+ }
+
+ int nline = 0;
+ while (1) {
+ DisasmAndClean (IP,line, 256);
+ nline ++;
+ ptr = line;
+ NextTerm (ptr);
+ NextTerm (ptr);
+ if (!strncmp (ptr, "ret", 3)) {
+ return (nline==1)?IPEND:IPEPILOG;
+ }
+ else if (!strncmp (ptr, "leave", 5)) {
+ return IPEPILOG;
+ }
+ else if (!strncmp (ptr, "call", 4)) {
+ return IPCODE;
+ }
+ else if (ptr[0] == 'j') {
+ return IPCODE;
+ }
+ }
+}
+
+// FrameBase is the ESP value at the entry of a function.
+BOOL GetFrameBase (Register callee[], FrameInfo* pFrame)
+{
+ //char line[256];
+ //char *ptr;
+ INT_PTR dwpushed = 0;
+ //DWORD_PTR IP;
+
+ IPSTATE IpState = GetIpState (callee[EIP].value, pFrame);
+
+ if (pFrame->bEBPFrame)
+ {
+ if (IpState == IPEND || IpState == IPPROLOG1) {
+ pFrame->FrameBase = callee[ESP].value;
+ }
+ else
+ {
+ pFrame->FrameBase = callee[EBP].value+4;
+ }
+ return TRUE;
+ }
+ else
+ {
+ if (IpState == IPEND) {
+ pFrame->FrameBase = callee[ESP].value;
+ return TRUE;
+ }
+
+ DWORD_PTR IPBegin, IPEnd;
+ if (IpState == IPEPILOG) {
+ IPBegin = callee[EIP].value;
+ IPEnd = ~0ul;
+ }
+ else if (IpState == IPPROLOG1) {
+ IPBegin = pFrame->IPStart;
+ IPEnd = callee[EIP].value;
+ }
+ else
+ {
+ IPBegin = pFrame->IPStart;
+ IPEnd = IPBegin + pFrame->Prolog;
+ }
+ GetFrameBaseHelper (IPBegin, IPEnd, dwpushed);
+
+ if (IpState == IPEPILOG) {
+ ExtOut ("stack %d\n", dwpushed);
+ pFrame->FrameBase = callee[ESP].value - dwpushed;
+ return TRUE;
+ }
+
+ CallInfo callInfo;
+ if (GetNextRetAddr (callee[ESP].value + dwpushed,
+ pFrame->StackEnd, callInfo))
+ {
+ pFrame->FrameBase = callInfo.stackPos;
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+}
+
+// caller[ESP]: the ESP value when we return to caller.
+void RestoreCallerRegister (Register callee[], Register caller[],
+ FrameInfo *pFrame)
+{
+ if (pFrame->bEBPFrame)
+ {
+ if (callee[ESP].value < pFrame->FrameBase)
+ {
+ SafeReadMemory (pFrame->FrameBase-4, &caller[EBP].value, 4, NULL);
+ }
+ else
+ caller[EBP].value = callee[EBP].value;
+ }
+ else
+ caller[EBP].value = callee[EBP].value;
+
+ caller[EBP].bValid = TRUE;
+ caller[ESP].value = pFrame->FrameBase + 4 + pFrame->argCount;
+ callee[EBP].value = pFrame->FrameBase - sizeof(void*);
+ SafeReadMemory (pFrame->FrameBase, &caller[EIP].value, 4, NULL);
+}
+
+BOOL GetFrameInfoHelper (Register callee[], Register caller[],
+ FrameInfo *pFrame)
+{
+ if (GetFrameBase (callee, pFrame))
+ {
+ RestoreCallerRegister (callee, caller, pFrame);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+// Return TRUE if Frame Info is OK, otherwise FALSE.
+BOOL GetUnmanagedFrameInfo (Register callee[], Register caller[],
+ DumpStackFlag &DSFlag, PFPO_DATA data)
+{
+ FrameInfo Frame;
+ ULONG64 base;
+ g_ExtSymbols->GetModuleByOffset (callee[EIP].value, 0, NULL, &base);
+ Frame.IPStart = data->ulOffStart + (ULONG_PTR)base;
+ Frame.Prolog = data->cbProlog;
+ // Why do we have to do this to make it work?
+ if (Frame.Prolog == 1) {
+ Frame.Prolog = 0;
+ }
+ Frame.bEBPFrame = (data->cbFrame == FRAME_NONFPO);
+ Frame.StackEnd = DSFlag.end;
+ Frame.argCount = data->cdwParams*4;
+
+ return GetFrameInfoHelper (callee, caller, &Frame);
+}
+
+// offsetEBP: offset of stack position where EBP is saved.
+// If EBP is not saved, *offsetEBP = -1 (~0ul);
+BOOL IPReachable (DWORD_PTR IPBegin, DWORD_PTR IP, DWORD *offsetEBP)
+{
+ *offsetEBP = ~0ul;
+ return FALSE;
+}
+
+BOOL HandleEEStub (Register callee[], Register caller[],
+ DumpStackFlag &DSFlag)
+{
+ // EEStub can only be called by IP directory. Let's look for possible caller.
+ CallInfo callInfo;
+ DWORD_PTR stackPos = callee[ESP].value;
+ while (stackPos < DSFlag.end) {
+ if (GetNextRetAddr (stackPos,
+ DSFlag.end, callInfo))
+ {
+ if (callInfo.whereCalled != ~0ul) {
+ DWORD offsetEBP;
+ if (IPReachable (callInfo.whereCalled, callee[EIP].value, &offsetEBP)) {
+ caller[EIP].value = callInfo.retAddr;
+ // TODO: We may have saved EBP.
+ if (offsetEBP == ~0ul) {
+ caller[EBP].value = callee[EBP].value;
+ }
+ else
+ {
+ TADDR offs = TO_TADDR(callInfo.stackPos)-sizeof(PVOID)-offsetEBP;
+ SafeReadMemory (offs, &caller[EBP].value, sizeof(PVOID), NULL);
+ }
+ caller[ESP].value = callInfo.stackPos+sizeof(PVOID);
+ return TRUE;
+ }
+ }
+ stackPos = callInfo.stackPos+sizeof(PVOID);
+ }
+ else
+ return FALSE;
+ }
+
+ return FALSE;
+}
+
+
+BOOL HandleByEpilog (Register callee[], Register caller[],
+ DumpStackFlag &DSFlag)
+{
+ return FALSE;
+}
+
+#ifndef FEATURE_PAL
+void RestoreFrameUnmanaged (Register *reg, DWORD_PTR CurIP)
+{
+ char line[256];
+ char *ptr;
+ DWORD_PTR IP = CurIP;
+ INT_PTR value;
+ BOOL bDigit;
+ BOOL bGoodESP = true;
+ RegIndex dest;
+
+ ULONG64 base;
+ g_ExtSymbols->GetModuleByOffset (TO_CDADDR(CurIP), 0, NULL, &base);
+ ULONG64 handle;
+ g_ExtSystem->GetCurrentProcessHandle(&handle);
+ PFPO_DATA data =
+ (PFPO_DATA)SymFunctionTableAccess((HANDLE)handle, CurIP);
+ DWORD_PTR IPBegin = data->ulOffStart + (ULONG_PTR)base;
+
+ if (CurIP - IPBegin <= data->cbProlog)
+ {
+ // We are inside a prolog.
+ // See where we save the callee saved register.
+ // Also how many DWORD's we pushd
+ IP = IPBegin;
+ reg[ESP].stack = 0;
+ reg[ESP].bOnStack = FALSE;
+ reg[EBP].stack = 0;
+ reg[EBP].bOnStack = FALSE;
+ reg[ESI].stack = 0;
+ reg[ESI].bOnStack = FALSE;
+ reg[EDI].stack = 0;
+ reg[EDI].bOnStack = FALSE;
+ reg[EBX].stack = 0;
+ reg[EBX].bOnStack = FALSE;
+
+ while (IP < CurIP)
+ {
+ DisasmAndClean (IP, line, 256);
+ ptr = line;
+ NextTerm (ptr);
+ NextTerm (ptr);
+ if (!strncmp (ptr, "push ", 5))
+ {
+ reg[ESP].stack += 4;
+ NextTerm (ptr);
+ dest = FindReg(ptr);
+ if (dest == EBP || dest == EBX || dest == ESI || dest == EDI)
+ {
+ reg[dest].bOnStack = TRUE;
+ reg[dest].stack = reg[ESP].stack;
+ }
+ }
+ else if (!strncmp (ptr, "sub ", 4))
+ {
+ NextTerm (ptr);
+ dest = FindReg(ptr);
+ if (dest == ESP)
+ {
+ NextTerm (ptr);
+ char *endptr;
+ reg[ESP].stack += strtoul(ptr, &endptr, 16);;
+ }
+ }
+ }
+
+ DWORD_PTR baseESP = reg[ESP].value + reg[ESP].stack;
+ if (reg[EBP].bOnStack)
+ {
+ move_xp (reg[EBP].value, baseESP-reg[EBP].stack);
+ }
+ if (reg[EBX].bOnStack)
+ {
+ move_xp (reg[EBX].value, baseESP-reg[EBX].stack);
+ }
+ if (reg[ESI].bOnStack)
+ {
+ move_xp (reg[ESI].value, baseESP-reg[ESI].stack);
+ }
+ if (reg[EDI].bOnStack)
+ {
+ move_xp (reg[EDI].value, baseESP-reg[EDI].stack);
+ }
+ move_xp (reg[EIP].value, baseESP);
+ reg[ESP].value = baseESP + 4;
+ return;
+ }
+
+ if (data->cbFrame == FRAME_NONFPO)
+ {
+ // EBP Frame
+ }
+
+ // Look for epilog
+ while (1)
+ {
+ DisasmAndClean (IP, line, 256);
+ ptr = line;
+ NextTerm (ptr);
+ NextTerm (ptr);
+ if (!strncmp (ptr, "mov ", 4))
+ {
+ NextTerm (ptr);
+ dest = FindReg(ptr);
+ if (dest == ESP)
+ {
+ NextTerm (ptr);
+ if (FindReg(ptr) == EBP)
+ {
+ // We have a EBP frame
+ bGoodESP = true;
+ reg[ESP].value = reg[EBP].value;
+ }
+ }
+ }
+ else if (!strncmp (ptr, "ret", 3))
+ {
+ NextTerm (ptr);
+ // check the value on stack is a return address.
+ DWORD_PTR retAddr;
+ DWORD_PTR whereCalled;
+ move_xp (retAddr, reg[ESP].value);
+ int ESPAdjustCount = 0;
+ while (1)
+ {
+ g_targetMachine->IsReturnAddress(retAddr, &whereCalled);
+ if (whereCalled)
+ break;
+ ESPAdjustCount ++;
+ reg[ESP].value += 4;
+ move_xp (retAddr, reg[ESP].value);
+ }
+ reg[EIP].value = retAddr;
+ if (ESPAdjustCount)
+ {
+ ESPAdjustCount *= 4;
+ }
+ if (reg[EBX].bOnStack)
+ {
+ reg[EBX].stack += ESPAdjustCount;
+ move_xp (reg[EBX].value, reg[EBX].stack);
+ }
+ if (reg[ESI].bOnStack)
+ {
+ reg[ESI].stack += ESPAdjustCount;
+ move_xp (reg[ESI].value, reg[EBX].stack);
+ }
+ if (reg[EDI].bOnStack)
+ {
+ reg[EDI].stack += ESPAdjustCount;
+ move_xp (reg[EDI].value, reg[EBX].stack);
+ }
+
+ reg[ESP].value += 4;
+ if (ptr[0] != '\0')
+ {
+ FindSrc (ptr, reg, value, bDigit);
+ reg[ESP].value += value;
+ }
+ break;
+ }
+ else if (!strncmp (ptr, "pop ", 4))
+ {
+ NextTerm (ptr);
+ dest = FindReg(ptr);
+ if (dest == EBP || dest == EBX || dest == ESI || dest == EDI)
+ {
+ reg[dest].stack = reg[ESP].value;
+ reg[dest].bOnStack = TRUE;
+ }
+ reg[ESP].value += 4;
+ }
+ else if (!strncmp (ptr, "add ", 4))
+ {
+ NextTerm (ptr);
+ dest = FindReg(ptr);
+ if (dest == ESP)
+ {
+ NextTerm (ptr);
+ FindSrc (ptr, reg, value, bDigit);
+ reg[ESP].value += value;
+ }
+ }
+ else if (!strncmp (ptr, "call ", 5))
+ {
+ // assume we do not have a good value on ESP.
+ // We could go into the call and find out number of pushed args.
+ bGoodESP = FALSE;
+ }
+ }
+
+ // Look for prolog
+}
+#endif // !FEATURE_PAL
+
+#elif defined(_AMD64_)
+
+
+#endif // !_X86_
diff --git a/src/ToolBox/SOS/Strike/dllsext.cpp b/src/ToolBox/SOS/Strike/dllsext.cpp
new file mode 100644
index 0000000000..757a04c91f
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/dllsext.cpp
@@ -0,0 +1,278 @@
+// 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 "strike.h"
+#include "data.h"
+#include "util.h"
+#include "platformspecific.h"
+
+typedef struct _PRIVATE_LDR_DATA_TABLE_ENTRY {
+ LIST_ENTRY InLoadOrderLinks;
+ LIST_ENTRY InMemoryOrderLinks;
+ LIST_ENTRY InInitializationOrderLinks;
+ PVOID DllBase;
+ PVOID EntryPoint;
+ ULONG SizeOfImage;
+ UNICODE_STRING FullDllName;
+ UNICODE_STRING BaseDllName;
+ ULONG Flags;
+ USHORT LoadCount;
+ USHORT TlsIndex;
+ union _LDR_DATA_TABLE_ENTRY_UNION1 { //DevDiv LKG RC Changes: Added union name to avoid warning C4408
+ LIST_ENTRY HashLinks;
+ struct _LDR_DATA_TABLE_ENTRY_STRUCT1 { //DevDiv LKG RC Changes: Added struct name to avoid warning C4201
+ PVOID SectionPointer;
+ ULONG CheckSum;
+ };
+ };
+ union _LDR_DATA_TABLE_ENTRY_UNION2 { //DevDiv LKG RC Changes: Added union name to avoid warning C4408
+ struct _LDR_DATA_TABLE_ENTRY_STRUCT2 { //DevDiv LKG RC Changes: Added struct name to avoid warning C4201
+ ULONG TimeDateStamp;
+ };
+ struct _LDR_DATA_TABLE_ENTRY_STRUCT3 { //DevDiv LKG RC Changes: Added struct name to avoid warning C4201
+ PVOID LoadedImports;
+ };
+ };
+ struct _ACTIVATION_CONTEXT * EntryPointActivationContext;
+
+ PVOID PatchInformation;
+
+} PRIVATE_LDR_DATA_TABLE_ENTRY, *PRIVATE_PLDR_DATA_TABLE_ENTRY;
+
+
+#ifndef FEATURE_PAL
+static void DllsNameFromPeb(
+ ULONG_PTR addrContaining,
+ __out_ecount (MAX_LONGPATH) WCHAR *dllName
+ )
+{
+ ULONG64 ProcessPeb;
+ g_ExtSystem->GetCurrentProcessPeb (&ProcessPeb);
+
+ ULONG64 pLdrEntry;
+ ULONG64 PebLdrAddress;
+ ULONG64 Next;
+ ULONG64 OrderModuleListStart;
+
+ //
+ // Capture PebLdrData
+ //
+
+ static ULONG Offset_Ldr = -1;
+ if (Offset_Ldr == -1)
+ {
+ ULONG TypeId;
+ ULONG64 NtDllBase;
+ if (SUCCEEDED(g_ExtSymbols->GetModuleByModuleName ("ntdll",0,NULL,
+ &NtDllBase))
+ && SUCCEEDED(g_ExtSymbols->GetTypeId (NtDllBase, "PEB", &TypeId)))
+ {
+ if (FAILED (g_ExtSymbols->GetFieldOffset(NtDllBase, TypeId,
+ "Ldr", &Offset_Ldr)))
+ Offset_Ldr = -1;
+ }
+ }
+ // We can not get it from PDB. Use the fixed one.
+ if (Offset_Ldr == -1)
+ Offset_Ldr = offsetof (DT_PEB, Ldr);
+
+ DT_PEB peb = {0};
+ if (FAILED(g_ExtData->ReadVirtual(ProcessPeb+Offset_Ldr, &peb.Ldr,
+ sizeof(peb.Ldr), NULL)))
+ {
+ ExtOut ( " Unable to read PEB_LDR_DATA address at %p\n", SOS_PTR(ProcessPeb+Offset_Ldr));
+ return;
+ }
+
+ PebLdrAddress = (ULONG64)peb.Ldr;
+
+ //
+ // Walk through the loaded module table and display all ldr data
+ //
+
+ static ULONG Offset_ModuleList = -1;
+ if (Offset_ModuleList == -1)
+ {
+ ULONG TypeId;
+ ULONG64 NtDllBase;
+ if (SUCCEEDED(g_ExtSymbols->GetModuleByModuleName ("ntdll",0,NULL,
+ &NtDllBase))
+ && SUCCEEDED(g_ExtSymbols->GetTypeId (NtDllBase, "PEB_LDR_DATA",
+ &TypeId)))
+ {
+ if (FAILED (g_ExtSymbols->GetFieldOffset(NtDllBase, TypeId,
+ "InMemoryOrderModuleList",
+ &Offset_ModuleList)))
+ Offset_ModuleList = -1;
+ }
+ }
+ // We can not get it from PDB. Use the fixed one.
+ if (Offset_ModuleList == -1)
+ Offset_ModuleList = offsetof (DT_PEB_LDR_DATA, InMemoryOrderModuleList);
+
+ OrderModuleListStart = PebLdrAddress + Offset_ModuleList;
+ DT_PEB_LDR_DATA Ldr = {0};
+ if (FAILED(g_ExtData->ReadVirtual(OrderModuleListStart,
+ &Ldr.InMemoryOrderModuleList,
+ sizeof(Ldr.InMemoryOrderModuleList),
+ NULL)))
+ {
+ ExtOut ( " Unable to read InMemoryOrderModuleList address at %p\n", SOS_PTR(OrderModuleListStart));
+ return;
+ }
+ Next = (ULONG64)Ldr.InMemoryOrderModuleList.Flink;
+
+ static ULONG Offset_OrderLinks = -1;
+ static ULONG Offset_FullDllName = -1;
+ static ULONG Offset_DllBase = -1;
+ static ULONG Offset_SizeOfImage = -1;
+ if (Offset_OrderLinks == -1)
+ {
+ ULONG TypeId;
+ ULONG64 NtDllBase;
+ if (SUCCEEDED(g_ExtSymbols->GetModuleByModuleName ("ntdll",0,NULL,
+ &NtDllBase))
+ && SUCCEEDED(g_ExtSymbols->GetTypeId (NtDllBase, "LDR_DATA_TABLE_ENTRY",
+ &TypeId)))
+ {
+ if (FAILED (g_ExtSymbols->GetFieldOffset(NtDllBase, TypeId,
+ "InMemoryOrderLinks",
+ &Offset_OrderLinks)))
+ Offset_OrderLinks = -1;
+ if (FAILED (g_ExtSymbols->GetFieldOffset(NtDllBase, TypeId,
+ "FullDllName",
+ &Offset_FullDllName)))
+ Offset_FullDllName = -1;
+ if (FAILED (g_ExtSymbols->GetFieldOffset(NtDllBase, TypeId,
+ "DllBase",
+ &Offset_DllBase)))
+ Offset_DllBase = -1;
+ if (FAILED (g_ExtSymbols->GetFieldOffset(NtDllBase, TypeId,
+ "SizeOfImage",
+ &Offset_SizeOfImage)))
+ Offset_SizeOfImage = -1;
+ }
+ }
+
+ // We can not get it from PDB. Use the fixed one.
+ if (Offset_OrderLinks == -1 || Offset_OrderLinks == 0)
+ {
+ Offset_OrderLinks = offsetof (PRIVATE_LDR_DATA_TABLE_ENTRY,
+ InMemoryOrderLinks);
+ Offset_FullDllName = offsetof (PRIVATE_LDR_DATA_TABLE_ENTRY,
+ FullDllName);
+ Offset_DllBase = offsetof (PRIVATE_LDR_DATA_TABLE_ENTRY,
+ DllBase);
+ Offset_SizeOfImage = offsetof (PRIVATE_LDR_DATA_TABLE_ENTRY,
+ SizeOfImage);
+ }
+
+ _UNICODE_STRING FullDllName;
+ __try {
+ while (Next != OrderModuleListStart) {
+ if (IsInterrupt())
+ return;
+
+ pLdrEntry = Next - Offset_OrderLinks;
+
+ //
+ // Capture LdrEntry
+ //
+ if (FAILED(g_ExtData->ReadVirtual(pLdrEntry + Offset_FullDllName,
+ &FullDllName,
+ sizeof(FullDllName),
+ NULL)))
+ {
+ ExtOut ( " Unable to read FullDllName address at %p\n",
+ pLdrEntry + Offset_FullDllName);
+ return;
+ }
+ ZeroMemory( dllName, MAX_LONGPATH * sizeof (WCHAR) );
+ if (FAILED(g_ExtData->ReadVirtual((ULONG64)FullDllName.Buffer,
+ dllName,
+ MAX_LONGPATH < FullDllName.Length ? MAX_LONGPATH : FullDllName.Length,
+ NULL)))
+ {
+#if 0
+ ExtOut ( " Unable to read FullDllName.Buffer address at %p\n",
+ SOS_PTR(FullDllName.Buffer));
+#endif
+ ZeroMemory( dllName, MAX_LONGPATH * sizeof (WCHAR) );
+ }
+
+ //
+ // Dump the ldr entry data
+ // (dump all the entries if no containing address specified)
+ //
+ PRIVATE_LDR_DATA_TABLE_ENTRY LdrEntry = {0};
+ if (SUCCEEDED(g_ExtData->ReadVirtual(pLdrEntry + Offset_DllBase,
+ &LdrEntry.DllBase,
+ sizeof(LdrEntry.DllBase),
+ NULL))
+ &&
+ SUCCEEDED(g_ExtData->ReadVirtual(pLdrEntry + Offset_SizeOfImage,
+ &LdrEntry.SizeOfImage,
+ sizeof(LdrEntry.SizeOfImage),
+ NULL))
+ )
+ {
+ if (((ULONG_PTR)LdrEntry.DllBase <= addrContaining) &&
+ (addrContaining <= (ULONG_PTR)LdrEntry.DllBase + (ULONG_PTR)LdrEntry.SizeOfImage))
+ break;
+ }
+
+ ZeroMemory( dllName, MAX_LONGPATH * sizeof (WCHAR) );
+ if (FAILED(g_ExtData->ReadVirtual(pLdrEntry + Offset_OrderLinks,
+ &LdrEntry.InMemoryOrderLinks,
+ sizeof(LdrEntry.InMemoryOrderLinks),
+ NULL)))
+ break;
+
+ Next = (ULONG64)LdrEntry.InMemoryOrderLinks.Flink;
+ }
+ } __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ ExtOut ("exception during reading PEB\n");
+ return;
+ }
+}
+#endif
+
+HRESULT
+DllsName(
+ ULONG_PTR addrContaining,
+ __out_ecount (MAX_LONGPATH) WCHAR *dllName
+ )
+{
+ dllName[0] = L'\0';
+
+ ULONG Index;
+ ULONG64 base;
+ HRESULT hr = g_ExtSymbols->GetModuleByOffset(addrContaining, 0, &Index, &base);
+ if (FAILED(hr))
+ return hr;
+
+ CHAR name[MAX_LONGPATH+1];
+ ULONG length;
+
+ hr = g_ExtSymbols->GetModuleNames(Index,base,name,MAX_LONGPATH,&length,NULL,0,NULL,NULL,0,NULL);
+
+ if (SUCCEEDED(hr))
+ {
+ MultiByteToWideChar (CP_ACP,0,name,-1,dllName,MAX_LONGPATH);
+ }
+
+#ifndef FEATURE_PAL
+ if (_wcsrchr (dllName, '\\') == NULL) {
+ DllsNameFromPeb (addrContaining,dllName);
+ }
+#endif
+
+ return hr;
+}
diff --git a/src/ToolBox/SOS/Strike/eeheap.cpp b/src/ToolBox/SOS/Strike/eeheap.cpp
new file mode 100644
index 0000000000..ac41e2deb6
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/eeheap.cpp
@@ -0,0 +1,1913 @@
+// 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 <assert.h>
+#include "sos.h"
+#include "safemath.h"
+
+
+// This is the increment for the segment lookup data
+const int nSegLookupStgIncrement = 100;
+
+#define CCH_STRING_PREFIX_SUMMARY 64
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to update GC heap statistics. *
+* *
+\**********************************************************************/
+void HeapStat::Add(DWORD_PTR aData, DWORD aSize)
+{
+ if (head == 0)
+ {
+ head = new Node();
+ if (head == NULL)
+ {
+ ReportOOM();
+ ControlC = TRUE;
+ return;
+ }
+
+ if (bHasStrings)
+ {
+ size_t capacity_pNew = _wcslen((WCHAR*)aData) + 1;
+ WCHAR *pNew = new WCHAR[capacity_pNew];
+ if (pNew == NULL)
+ {
+ ReportOOM();
+ ControlC = TRUE;
+ return;
+ }
+ wcscpy_s(pNew, capacity_pNew, (WCHAR*)aData);
+ aData = (DWORD_PTR)pNew;
+ }
+
+ head->data = aData;
+ }
+ Node *walk = head;
+ int cmp = 0;
+
+ for (;;)
+ {
+ if (IsInterrupt())
+ return;
+
+ cmp = CompareData(aData, walk->data);
+
+ if (cmp == 0)
+ break;
+
+ if (cmp < 0)
+ {
+ if (walk->left == NULL)
+ break;
+ walk = walk->left;
+ }
+ else
+ {
+ if (walk->right == NULL)
+ break;
+ walk = walk->right;
+ }
+ }
+
+ if (cmp == 0)
+ {
+ walk->count ++;
+ walk->totalSize += aSize;
+ }
+ else
+ {
+ Node *node = new Node();
+ if (node == NULL)
+ {
+ ReportOOM();
+ ControlC = TRUE;
+ return;
+ }
+
+ if (bHasStrings)
+ {
+ size_t capacity_pNew = _wcslen((WCHAR*)aData) + 1;
+ WCHAR *pNew = new WCHAR[capacity_pNew];
+ if (pNew == NULL)
+ {
+ ReportOOM();
+ ControlC = TRUE;
+ return;
+ }
+ wcscpy_s(pNew, capacity_pNew, (WCHAR*)aData);
+ aData = (DWORD_PTR)pNew;
+ }
+
+ node->data = aData;
+ node->totalSize = aSize;
+ node->count ++;
+
+ if (cmp < 0)
+ {
+ walk->left = node;
+ }
+ else
+ {
+ walk->right = node;
+ }
+ }
+}
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function compares two nodes in the tree. *
+* *
+\**********************************************************************/
+int HeapStat::CompareData(DWORD_PTR d1, DWORD_PTR d2)
+{
+ if (bHasStrings)
+ return _wcscmp((WCHAR*)d1, (WCHAR*)d2);
+
+ if (d1 > d2)
+ return 1;
+
+ if (d1 < d2)
+ return -1;
+
+ return 0;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to sort all entries in the heap stat. *
+* *
+\**********************************************************************/
+void HeapStat::Sort ()
+{
+ Node *root = head;
+ head = NULL;
+ ReverseLeftMost (root);
+
+ Node *sortRoot = NULL;
+ while (head)
+ {
+ Node *tmp = head;
+ head = head->left;
+ if (tmp->right)
+ ReverseLeftMost (tmp->right);
+ // add tmp
+ tmp->right = NULL;
+ tmp->left = NULL;
+ SortAdd (sortRoot, tmp);
+ }
+ head = sortRoot;
+
+ Linearize();
+
+ //reverse the order
+ root = head;
+ head = NULL;
+ sortRoot = NULL;
+ while (root)
+ {
+ Node *tmp = root->right;
+ root->left = NULL;
+ root->right = NULL;
+ LinearAdd (sortRoot, root);
+ root = tmp;
+ }
+ head = sortRoot;
+}
+
+void HeapStat::Linearize()
+{
+ // Change binary tree to a linear tree
+ Node *root = head;
+ head = NULL;
+ ReverseLeftMost (root);
+ Node *sortRoot = NULL;
+ while (head)
+ {
+ Node *tmp = head;
+ head = head->left;
+ if (tmp->right)
+ ReverseLeftMost (tmp->right);
+ // add tmp
+ tmp->right = NULL;
+ tmp->left = NULL;
+ LinearAdd (sortRoot, tmp);
+ }
+ head = sortRoot;
+ fLinear = TRUE;
+}
+
+void HeapStat::ReverseLeftMost (Node *root)
+{
+ while (root)
+ {
+ Node *tmp = root->left;
+ root->left = head;
+ head = root;
+ root = tmp;
+ }
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to help to sort heap stat. *
+* *
+\**********************************************************************/
+void HeapStat::SortAdd (Node *&root, Node *entry)
+{
+ if (root == NULL)
+ {
+ root = entry;
+ }
+ else
+ {
+ Node *parent = root;
+ Node *ptr = root;
+ while (ptr)
+ {
+ parent = ptr;
+ if (ptr->totalSize < entry->totalSize)
+ ptr = ptr->right;
+ else
+ ptr = ptr->left;
+ }
+ if (parent->totalSize < entry->totalSize)
+ parent->right = entry;
+ else
+ parent->left = entry;
+ }
+}
+
+void HeapStat::LinearAdd(Node *&root, Node *entry)
+{
+ if (root == NULL)
+ {
+ root = entry;
+ }
+ else
+ {
+ entry->right = root;
+ root = entry;
+ }
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to print GC heap statistics. *
+* *
+\**********************************************************************/
+void HeapStat::Print(const char* label /* = NULL */)
+{
+ if (label == NULL)
+ {
+ label = "Statistics:\n";
+ }
+ ExtOut(label);
+ if (bHasStrings)
+ ExtOut("%8s %12s %s\n", "Count", "TotalSize", "String Value");
+ else
+ ExtOut("%" POINTERSIZE "s %8s %12s %s\n","MT", "Count", "TotalSize", "Class Name");
+
+ Node *root = head;
+ int ncount = 0;
+ while (root)
+ {
+ if (IsInterrupt())
+ return;
+
+ ncount += root->count;
+
+ if (bHasStrings)
+ {
+ ExtOut("%8d %12I64u \"%S\"\n", root->count, (unsigned __int64)root->totalSize, root->data);
+ }
+ else
+ {
+ DMLOut("%s %8d %12I64u ", DMLDumpHeapMT(root->data), root->count, (unsigned __int64)root->totalSize);
+ if (IsMTForFreeObj(root->data))
+ {
+ ExtOut("%9s\n", "Free");
+ }
+ else
+ {
+ wcscpy_s(g_mdName, mdNameLen, W("UNKNOWN"));
+ NameForMT_s((DWORD_PTR) root->data, g_mdName, mdNameLen);
+ ExtOut("%S\n", g_mdName);
+ }
+ }
+ root = root->right;
+
+ }
+ ExtOut ("Total %d objects\n", ncount);
+}
+
+void HeapStat::Delete()
+{
+ if (head == NULL)
+ return;
+
+ // Ensure the data structure is already linearized.
+ if (!fLinear)
+ Linearize();
+
+ while (head)
+ {
+ // The list is linearized on such that the left node is always null.
+ Node *tmp = head;
+ head = head->right;
+
+ if (bHasStrings)
+ delete[] ((WCHAR*)tmp->data);
+ delete tmp;
+ }
+
+ // return to default state
+ bHasStrings = FALSE;
+ fLinear = FALSE;
+}
+
+// -----------------------------------------------------------------------
+//
+// MethodTableCache implementation
+//
+// Used during heap traversals for quick object size computation
+//
+MethodTableInfo* MethodTableCache::Lookup (DWORD_PTR aData)
+{
+ Node** addHere = &head;
+ if (head != 0) {
+ Node *walk = head;
+ int cmp = 0;
+
+ for (;;)
+ {
+ cmp = CompareData(aData, walk->data);
+
+ if (cmp == 0)
+ return &walk->info;
+
+ if (cmp < 0)
+ {
+ if (walk->left == NULL)
+ {
+ addHere = &walk->left;
+ break;
+ }
+ walk = walk->left;
+ }
+ else
+ {
+ if (walk->right == NULL)
+ {
+ addHere = &walk->right;
+ break;
+ }
+ walk = walk->right;
+ }
+ }
+ }
+ Node* newNode = new Node(aData);
+ if (newNode == NULL)
+ {
+ ReportOOM();
+ return NULL;
+ }
+ *addHere = newNode;
+ return &newNode->info;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function compares two nodes in the tree. *
+* *
+\**********************************************************************/
+int MethodTableCache::CompareData(DWORD_PTR d1, DWORD_PTR d2)
+{
+ if (d1 > d2)
+ return 1;
+
+ if (d1 < d2)
+ return -1;
+
+ return 0;
+}
+
+void MethodTableCache::ReverseLeftMost (Node *root)
+{
+ if (root)
+ {
+ if (root->left) ReverseLeftMost(root->left);
+ if (root->right) ReverseLeftMost(root->right);
+ delete root;
+ }
+}
+
+void MethodTableCache::Clear()
+{
+ Node *root = head;
+ head = NULL;
+ ReverseLeftMost (root);
+}
+
+MethodTableCache g_special_mtCache;
+
+size_t Align (size_t nbytes)
+{
+ return (nbytes + ALIGNCONST) & ~ALIGNCONST;
+}
+
+size_t AlignLarge(size_t nbytes)
+{
+ return (nbytes + ALIGNCONSTLARGE) & ~ALIGNCONSTLARGE;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Print the gc heap info. *
+* *
+\**********************************************************************/
+void GCPrintGenerationInfo(const DacpGcHeapDetails &heap)
+{
+ UINT n;
+ for (n = 0; n <= GetMaxGeneration(); n ++)
+ {
+ if (IsInterrupt())
+ return;
+ ExtOut("generation %d starts at 0x%p\n",
+ n, SOS_PTR(heap.generation_table[n].allocation_start));
+ }
+
+ // We also need to look at the gen0 alloc context.
+ ExtOut("ephemeral segment allocation context: ");
+ if (heap.generation_table[0].allocContextPtr)
+ {
+ ExtOut("(0x%p, 0x%p)\n",
+ SOS_PTR(heap.generation_table[0].allocContextPtr),
+ SOS_PTR(heap.generation_table[0].allocContextLimit + Align(min_obj_size)));
+ }
+ else
+ {
+ ExtOut("none\n");
+ }
+}
+
+
+void GCPrintSegmentInfo(const DacpGcHeapDetails &heap, DWORD_PTR &total_size)
+{
+ DWORD_PTR dwAddrSeg;
+ DacpHeapSegmentData segment;
+
+ dwAddrSeg = (DWORD_PTR)heap.generation_table[GetMaxGeneration()].start_segment;
+ total_size = 0;
+ // the loop below will terminate, because we retrieved at most nMaxHeapSegmentCount segments
+ while (dwAddrSeg != (DWORD_PTR)heap.generation_table[0].start_segment)
+ {
+ if (IsInterrupt())
+ return;
+ if (segment.Request(g_sos, dwAddrSeg, heap) != S_OK)
+ {
+ ExtOut("Error requesting heap segment %p\n", SOS_PTR(dwAddrSeg));
+ return;
+ }
+ ExtOut("%p %p %p 0x%" POINTERSIZE_TYPE "x(%" POINTERSIZE_TYPE "d)\n", SOS_PTR(dwAddrSeg),
+ SOS_PTR(segment.mem), SOS_PTR(segment.allocated),
+ (ULONG_PTR)(segment.allocated - segment.mem),
+ (ULONG_PTR)(segment.allocated - segment.mem));
+ total_size += (DWORD_PTR) (segment.allocated - segment.mem);
+ dwAddrSeg = (DWORD_PTR)segment.next;
+ }
+
+ if (segment.Request(g_sos, dwAddrSeg, heap) != S_OK)
+ {
+ ExtOut("Error requesting heap segment %p\n", SOS_PTR(dwAddrSeg));
+ return;
+ }
+
+ DWORD_PTR end = (DWORD_PTR)heap.alloc_allocated;
+ ExtOut("%p %p %p 0x%" POINTERSIZE_TYPE "x(%" POINTERSIZE_TYPE "d)\n", SOS_PTR(dwAddrSeg),
+ SOS_PTR(segment.mem), SOS_PTR(end),
+ (ULONG_PTR)(end - (DWORD_PTR)segment.mem),
+ (ULONG_PTR)(end - (DWORD_PTR)segment.mem));
+
+ total_size += end - (DWORD_PTR)segment.mem;
+
+}
+
+
+void GCPrintLargeHeapSegmentInfo(const DacpGcHeapDetails &heap, DWORD_PTR &total_size)
+{
+ DWORD_PTR dwAddrSeg;
+ DacpHeapSegmentData segment;
+ dwAddrSeg = (DWORD_PTR)heap.generation_table[GetMaxGeneration()+1].start_segment;
+
+ // total_size = 0;
+ // the loop below will terminate, because we retrieved at most nMaxHeapSegmentCount segments
+ while (dwAddrSeg != NULL)
+ {
+ if (IsInterrupt())
+ return;
+ if (segment.Request(g_sos, dwAddrSeg, heap) != S_OK)
+ {
+ ExtOut("Error requesting heap segment %p\n", SOS_PTR(dwAddrSeg));
+ return;
+ }
+ ExtOut("%p %p %p 0x%" POINTERSIZE_TYPE "x(%" POINTERSIZE_TYPE "d)\n", SOS_PTR(dwAddrSeg),
+ SOS_PTR(segment.mem), SOS_PTR(segment.allocated),
+ (ULONG_PTR)(segment.allocated - segment.mem),
+ segment.allocated - segment.mem);
+ total_size += (DWORD_PTR) (segment.allocated - segment.mem);
+ dwAddrSeg = (DWORD_PTR)segment.next;
+ }
+}
+
+void GCHeapInfo(const DacpGcHeapDetails &heap, DWORD_PTR &total_size)
+{
+ GCPrintGenerationInfo(heap);
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n", "segment", "begin", "allocated", "size");
+ GCPrintSegmentInfo(heap, total_size);
+ ExtOut("Large object heap starts at 0x%p\n",
+ SOS_PTR(heap.generation_table[GetMaxGeneration()+1].allocation_start));
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n", "segment", "begin", "allocated", "size");
+ GCPrintLargeHeapSegmentInfo(heap,total_size);
+}
+
+BOOL GCObjInGeneration(TADDR taddrObj, const DacpGcHeapDetails &heap,
+ const TADDR_SEGINFO& /*seg*/, int& gen, TADDR_RANGE& allocCtx)
+{
+ gen = -1;
+ for (UINT n = 0; n <= GetMaxGeneration(); n ++)
+ {
+ if (taddrObj >= TO_TADDR(heap.generation_table[n].allocation_start))
+ {
+ gen = n;
+ break;
+ }
+ }
+
+ // We also need to look at the gen0 alloc context.
+ if (heap.generation_table[0].allocContextPtr
+ && taddrObj >= TO_TADDR(heap.generation_table[0].allocContextPtr)
+ && taddrObj < TO_TADDR(heap.generation_table[0].allocContextLimit) + Align(min_obj_size))
+ {
+ gen = 0;
+ allocCtx.start = (TADDR)heap.generation_table[0].allocContextPtr;
+ allocCtx.end = (TADDR)heap.generation_table[0].allocContextLimit;
+ }
+ else
+ {
+ allocCtx.start = allocCtx.end = 0;
+ }
+ return (gen != -1);
+}
+
+
+BOOL GCObjInSegment(TADDR taddrObj, const DacpGcHeapDetails &heap,
+ TADDR_SEGINFO& rngSeg, int& gen, TADDR_RANGE& allocCtx)
+{
+ TADDR taddrSeg;
+ DacpHeapSegmentData dacpSeg;
+
+ taddrSeg = (TADDR)heap.generation_table[GetMaxGeneration()].start_segment;
+ // the loop below will terminate, because we retrieved at most nMaxHeapSegmentCount segments
+ while (taddrSeg != (TADDR)heap.generation_table[0].start_segment)
+ {
+ if (IsInterrupt())
+ return FALSE;
+ if (dacpSeg.Request(g_sos, taddrSeg, heap) != S_OK)
+ {
+ ExtOut("Error requesting heap segment %p\n", SOS_PTR(taddrSeg));
+ return FALSE;
+ }
+ if (taddrObj >= TO_TADDR(dacpSeg.mem) && taddrObj < TO_TADDR(dacpSeg.allocated))
+ {
+ rngSeg.segAddr = (TADDR)dacpSeg.segmentAddr;
+ rngSeg.start = (TADDR)dacpSeg.mem;
+ rngSeg.end = (TADDR)dacpSeg.allocated;
+ gen = 2;
+ allocCtx.start = allocCtx.end = 0;
+ return TRUE;
+ }
+ taddrSeg = (TADDR)dacpSeg.next;
+ }
+
+ // the ephemeral segment
+ if (dacpSeg.Request(g_sos, taddrSeg, heap) != S_OK)
+ {
+ ExtOut("Error requesting heap segment %p\n", SOS_PTR(taddrSeg));
+ return FALSE;
+ }
+
+ if (taddrObj >= TO_TADDR(dacpSeg.mem) && taddrObj < TO_TADDR(heap.alloc_allocated))
+ {
+ if (GCObjInGeneration(taddrObj, heap, rngSeg, gen, allocCtx))
+ {
+ rngSeg.segAddr = (TADDR)dacpSeg.segmentAddr;
+ rngSeg.start = (TADDR)dacpSeg.mem;
+ rngSeg.end = (TADDR)heap.alloc_allocated;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+BOOL GCObjInLargeSegment(TADDR taddrObj, const DacpGcHeapDetails &heap, TADDR_SEGINFO& rngSeg)
+{
+ TADDR taddrSeg;
+ DacpHeapSegmentData dacpSeg;
+ taddrSeg = (TADDR)heap.generation_table[GetMaxGeneration()+1].start_segment;
+
+ // the loop below will terminate, because we retrieved at most nMaxHeapSegmentCount segments
+ while (taddrSeg != NULL)
+ {
+ if (IsInterrupt())
+ return FALSE;
+ if (dacpSeg.Request(g_sos, taddrSeg, heap) != S_OK)
+ {
+ ExtOut("Error requesting heap segment %p\n", SOS_PTR(taddrSeg));
+ return FALSE;
+ }
+ if (taddrObj >= TO_TADDR(dacpSeg.mem) && taddrObj && taddrObj < TO_TADDR(dacpSeg.allocated))
+ {
+ rngSeg.segAddr = (TADDR)dacpSeg.segmentAddr;
+ rngSeg.start = (TADDR)dacpSeg.mem;
+ rngSeg.end = (TADDR)dacpSeg.allocated;
+ return TRUE;
+ }
+ taddrSeg = (TADDR)dacpSeg.next;
+ }
+ return FALSE;
+}
+
+BOOL GCObjInHeap(TADDR taddrObj, const DacpGcHeapDetails &heap,
+ TADDR_SEGINFO& rngSeg, int& gen, TADDR_RANGE& allocCtx, BOOL &bLarge)
+{
+ if (GCObjInSegment(taddrObj, heap, rngSeg, gen, allocCtx))
+ {
+ bLarge = FALSE;
+ return TRUE;
+ }
+ if (GCObjInLargeSegment(taddrObj, heap, rngSeg))
+ {
+ bLarge = TRUE;
+ gen = GetMaxGeneration()+1;
+ allocCtx.start = allocCtx.end = 0;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+#ifndef FEATURE_PAL
+// this function updates genUsage to reflect statistics from the range defined by [start, end)
+void GCGenUsageStats(TADDR start, TADDR end, const std::unordered_set<TADDR> &liveObjs,
+ const DacpGcHeapDetails &heap, BOOL bLarge, const AllocInfo *pAllocInfo, GenUsageStat *genUsage)
+{
+ // if this is an empty segment or generation return
+ if (start >= end)
+ {
+ return;
+ }
+
+ // otherwise it should start with a valid object
+ _ASSERTE(sos::IsObject(start));
+
+ // update the "allocd" field
+ genUsage->allocd += end - start;
+
+ size_t objSize = 0;
+ for (TADDR taddrObj = start; taddrObj < end; taddrObj += objSize)
+ {
+ TADDR taddrMT;
+
+ move_xp(taddrMT, taddrObj);
+ taddrMT &= ~3;
+
+ // skip allocation contexts
+ if (!bLarge)
+ {
+ // Is this the beginning of an allocation context?
+ int i;
+ for (i = 0; i < pAllocInfo->num; i ++)
+ {
+ if (taddrObj == (TADDR)pAllocInfo->array[i].alloc_ptr)
+ {
+ ExtDbgOut("Skipping allocation context: [%#p-%#p)\n",
+ SOS_PTR(pAllocInfo->array[i].alloc_ptr), SOS_PTR(pAllocInfo->array[i].alloc_limit));
+ taddrObj =
+ (TADDR)pAllocInfo->array[i].alloc_limit + Align(min_obj_size);
+ break;
+ }
+ }
+ if (i < pAllocInfo->num)
+ {
+ // we already adjusted taddrObj, so reset objSize
+ objSize = 0;
+ continue;
+ }
+
+ // We also need to look at the gen0 alloc context.
+ if (taddrObj == (DWORD_PTR) heap.generation_table[0].allocContextPtr)
+ {
+ taddrObj = (DWORD_PTR) heap.generation_table[0].allocContextLimit + Align(min_obj_size);
+ // we already adjusted taddrObj, so reset objSize
+ objSize = 0;
+ continue;
+ }
+
+ // Are we at the end of gen 0?
+ if (taddrObj == end - Align(min_obj_size))
+ {
+ objSize = 0;
+ break;
+ }
+ }
+
+ BOOL bContainsPointers;
+ BOOL bMTOk = GetSizeEfficient(taddrObj, taddrMT, bLarge, objSize, bContainsPointers);
+ if (!bMTOk)
+ {
+ ExtErr("bad object: %#p - bad MT %#p\n", SOS_PTR(taddrObj), SOS_PTR(taddrMT));
+ // set objSize to size_t to look for the next valid MT
+ objSize = sizeof(TADDR);
+ continue;
+ }
+
+ // at this point we should have a valid objSize, and there whould be no
+ // integer overflow when moving on to next object in heap
+ _ASSERTE(objSize > 0 && taddrObj < taddrObj + objSize);
+ if (objSize == 0 || taddrObj > taddrObj + objSize)
+ {
+ break;
+ }
+
+ if (IsMTForFreeObj(taddrMT))
+ {
+ genUsage->freed += objSize;
+ }
+ else if (!(liveObjs.empty()) && liveObjs.find(taddrObj) == liveObjs.end())
+ {
+ genUsage->unrooted += objSize;
+ }
+ }
+}
+#endif // !FEATURE_PAL
+
+BOOL GCHeapUsageStats(const DacpGcHeapDetails& heap, BOOL bIncUnreachable, HeapUsageStat *hpUsage)
+{
+ memset(hpUsage, 0, sizeof(*hpUsage));
+
+ AllocInfo allocInfo;
+ allocInfo.Init();
+
+ // 1. Start with small object segments
+ TADDR taddrSeg;
+ DacpHeapSegmentData dacpSeg;
+
+ taddrSeg = (TADDR)heap.generation_table[GetMaxGeneration()].start_segment;
+
+#ifndef FEATURE_PAL
+ // this will create the bitmap of rooted objects only if bIncUnreachable is true
+ GCRootImpl gcroot;
+ std::unordered_set<TADDR> emptyLiveObjs;
+ const std::unordered_set<TADDR> &liveObjs = (bIncUnreachable ? gcroot.GetLiveObjects() : emptyLiveObjs);
+
+ // 1a. enumerate all non-ephemeral segments
+ while (taddrSeg != (TADDR)heap.generation_table[0].start_segment)
+ {
+ if (IsInterrupt())
+ return FALSE;
+
+ if (dacpSeg.Request(g_sos, taddrSeg, heap) != S_OK)
+ {
+ ExtErr("Error requesting heap segment %p\n", SOS_PTR(taddrSeg));
+ return FALSE;
+ }
+ GCGenUsageStats((TADDR)dacpSeg.mem, (TADDR)dacpSeg.allocated, liveObjs, heap, FALSE, &allocInfo, &hpUsage->genUsage[2]);
+ taddrSeg = (TADDR)dacpSeg.next;
+ }
+#endif
+
+ // 1b. now handle the ephemeral segment
+ if (dacpSeg.Request(g_sos, taddrSeg, heap) != S_OK)
+ {
+ ExtErr("Error requesting heap segment %p\n", SOS_PTR(taddrSeg));
+ return FALSE;
+ }
+
+ TADDR endGen = TO_TADDR(heap.alloc_allocated);
+ for (UINT n = 0; n <= GetMaxGeneration(); n ++)
+ {
+ TADDR startGen;
+ // gen 2 starts at the beginning of the segment
+ if (n == GetMaxGeneration())
+ {
+ startGen = TO_TADDR(dacpSeg.mem);
+ }
+ else
+ {
+ startGen = TO_TADDR(heap.generation_table[n].allocation_start);
+ }
+
+#ifndef FEATURE_PAL
+ GCGenUsageStats(startGen, endGen, liveObjs, heap, FALSE, &allocInfo, &hpUsage->genUsage[n]);
+#endif
+ endGen = startGen;
+ }
+
+ // 2. Now process LOH
+ taddrSeg = (TADDR) heap.generation_table[GetMaxGeneration()+1].start_segment;
+ while (taddrSeg != NULL)
+ {
+ if (IsInterrupt())
+ return FALSE;
+
+ if (dacpSeg.Request(g_sos, taddrSeg, heap) != S_OK)
+ {
+ ExtErr("Error requesting heap segment %p\n", SOS_PTR(taddrSeg));
+ return FALSE;
+ }
+
+#ifndef FEATURE_PAL
+ GCGenUsageStats((TADDR) dacpSeg.mem, (TADDR) dacpSeg.allocated, liveObjs, heap, TRUE, NULL, &hpUsage->genUsage[3]);
+#endif
+ taddrSeg = (TADDR)dacpSeg.next;
+ }
+
+ return TRUE;
+}
+
+DWORD GetNumComponents(TADDR obj)
+{
+ // The number of components is always the second pointer in the object.
+ DWORD Value = NULL;
+ HRESULT hr = MOVE(Value, obj + sizeof(size_t));
+
+ // If we fail to read out the number of components, let's assume 0 so we don't try to
+ // read further data from the object.
+ if (FAILED(hr))
+ return 0;
+
+ // The component size on a String does not contain the trailing NULL character,
+ // so we must add that ourselves.
+ if(IsStringObject(obj))
+ return Value+1;
+
+ return Value;
+}
+
+BOOL GetSizeEfficient(DWORD_PTR dwAddrCurrObj,
+ DWORD_PTR dwAddrMethTable, BOOL bLarge, size_t& s, BOOL& bContainsPointers)
+{
+ // Remove lower bits in case we are in mark phase
+ dwAddrMethTable = dwAddrMethTable & ~3;
+ MethodTableInfo* info = g_special_mtCache.Lookup(dwAddrMethTable);
+ if (!info->IsInitialized()) // An uninitialized entry
+ {
+ // this is the first time we see this method table, so we need to get the information
+ // from the target
+ DacpMethodTableData dmtd;
+ // see code:ClrDataAccess::RequestMethodTableData for details
+ if (dmtd.Request(g_sos,dwAddrMethTable) != S_OK)
+ return FALSE;
+
+ info->BaseSize = dmtd.BaseSize;
+ info->ComponentSize = dmtd.ComponentSize;
+ info->bContainsPointers = dmtd.bContainsPointers;
+ }
+
+ bContainsPointers = info->bContainsPointers;
+ s = info->BaseSize;
+
+ if (info->ComponentSize)
+ {
+ // this is an array, so the size has to include the size of the components. We read the number
+ // of components from the target and multiply by the component size to get the size.
+ s += info->ComponentSize*GetNumComponents(dwAddrCurrObj);
+ }
+
+ // On x64 we do an optimization to save 4 bytes in almost every string we create
+ // IMPORTANT: This cannot be done in ObjectSize, which is a wrapper to this function,
+ // because we must Align only after these changes are made
+#ifdef _TARGET_WIN64_
+ // Pad to min object size if necessary
+ if (s < min_obj_size)
+ s = min_obj_size;
+#endif // _TARGET_WIN64_
+
+ s = (bLarge ? AlignLarge(s) : Align (s));
+ return TRUE;
+}
+
+// This function expects stat to be valid, and ready to get statistics.
+void GatherOneHeapFinalization(DacpGcHeapDetails& heapDetails, HeapStat *stat, BOOL bAllReady, BOOL bShort)
+{
+ DWORD_PTR dwAddr=0;
+ UINT m;
+
+ if (!bShort)
+ {
+ for (m = 0; m <= GetMaxGeneration(); m ++)
+ {
+ if (IsInterrupt())
+ return;
+
+ ExtOut("generation %d has %d finalizable objects ", m,
+ (SegQueueLimit(heapDetails,gen_segment(m)) - SegQueue(heapDetails,gen_segment(m))) / sizeof(size_t));
+
+ ExtOut ("(%p->%p)\n",
+ SOS_PTR(SegQueue(heapDetails,gen_segment(m))),
+ SOS_PTR(SegQueueLimit(heapDetails,gen_segment(m))));
+ }
+ }
+#ifndef FEATURE_PAL
+ if (bAllReady)
+ {
+ if (!bShort)
+ {
+ ExtOut ("Finalizable but not rooted: ");
+ }
+
+ TADDR rngStart = (TADDR)SegQueue(heapDetails, gen_segment(GetMaxGeneration()));
+ TADDR rngEnd = (TADDR)SegQueueLimit(heapDetails, gen_segment(0));
+
+ PrintNotReachableInRange(rngStart, rngEnd, TRUE, bAllReady ? stat : NULL, bShort);
+ }
+#endif
+
+ if (!bShort)
+ {
+ ExtOut ("Ready for finalization %d objects ",
+ (SegQueueLimit(heapDetails,FinalizerListSeg)-SegQueue(heapDetails,CriticalFinalizerListSeg)) / sizeof(size_t));
+ ExtOut ("(%p->%p)\n",
+ SOS_PTR(SegQueue(heapDetails,CriticalFinalizerListSeg)),
+ SOS_PTR(SegQueueLimit(heapDetails,FinalizerListSeg)));
+ }
+
+ // if bAllReady we only count objects that are ready for finalization,
+ // otherwise we count all finalizable objects.
+ TADDR taddrLowerLimit = (bAllReady ? (TADDR)SegQueue(heapDetails, CriticalFinalizerListSeg) :
+ (DWORD_PTR)SegQueue(heapDetails, gen_segment(GetMaxGeneration())));
+ for (dwAddr = taddrLowerLimit;
+ dwAddr < (DWORD_PTR)SegQueueLimit(heapDetails, FinalizerListSeg);
+ dwAddr += sizeof (dwAddr))
+ {
+ if (IsInterrupt())
+ {
+ return;
+ }
+
+ DWORD_PTR objAddr = NULL,
+ MTAddr = NULL;
+
+ if (SUCCEEDED(MOVE(objAddr, dwAddr)) && SUCCEEDED(GetMTOfObject(objAddr, &MTAddr)) && MTAddr)
+ {
+ if (bShort)
+ {
+ DMLOut("%s\n", DMLObject(objAddr));
+ }
+ else
+ {
+ size_t s = ObjectSize(objAddr);
+ stat->Add(MTAddr, (DWORD)s);
+ }
+ }
+ }
+}
+
+BOOL GCHeapTraverse(const DacpGcHeapDetails &heap, AllocInfo* pallocInfo, VISITGCHEAPFUNC pFunc, LPVOID token, BOOL verify)
+{
+ DWORD_PTR begin_youngest;
+ DWORD_PTR end_youngest;
+ begin_youngest = (DWORD_PTR)heap.generation_table[0].allocation_start;
+ DWORD_PTR dwAddr = (DWORD_PTR)heap.ephemeral_heap_segment;
+ DacpHeapSegmentData segment;
+
+ end_youngest = (DWORD_PTR)heap.alloc_allocated;
+
+ DWORD_PTR dwAddrSeg = (DWORD_PTR)heap.generation_table[GetMaxGeneration()].start_segment;
+ dwAddr = dwAddrSeg;
+
+ if (segment.Request(g_sos, dwAddr, heap) != S_OK)
+ {
+ ExtOut("Error requesting heap segment %p\n", SOS_PTR(dwAddr));
+ return FALSE;
+ }
+
+ // DWORD_PTR dwAddrCurrObj = (DWORD_PTR)heap.generation_table[GetMaxGeneration()].allocation_start;
+ DWORD_PTR dwAddrCurrObj = (DWORD_PTR)segment.mem;
+
+ size_t s, sPrev=0;
+ BOOL bPrevFree=FALSE;
+ DWORD_PTR dwAddrMethTable;
+ DWORD_PTR dwAddrPrevObj=0;
+
+ while(1)
+ {
+ if (IsInterrupt())
+ {
+ ExtOut("<heap walk interrupted>\n");
+ return FALSE;
+ }
+ DWORD_PTR end_of_segment = (DWORD_PTR)segment.allocated;
+ if (dwAddrSeg == (DWORD_PTR)heap.ephemeral_heap_segment)
+ {
+ end_of_segment = end_youngest;
+ if (dwAddrCurrObj - SIZEOF_OBJHEADER == end_youngest - Align(min_obj_size))
+ break;
+ }
+ if (dwAddrCurrObj >= (DWORD_PTR)end_of_segment)
+ {
+ if (dwAddrCurrObj > (DWORD_PTR)end_of_segment)
+ {
+ ExtOut ("curr_object: %p > heap_segment_allocated (seg: %p)\n",
+ SOS_PTR(dwAddrCurrObj), SOS_PTR(dwAddrSeg));
+ if (dwAddrPrevObj) {
+ ExtOut ("Last good object: %p\n", SOS_PTR(dwAddrPrevObj));
+ }
+ return FALSE;
+ }
+ dwAddrSeg = (DWORD_PTR)segment.next;
+ if (dwAddrSeg)
+ {
+ dwAddr = dwAddrSeg;
+ if (segment.Request(g_sos, dwAddr, heap) != S_OK)
+ {
+ ExtOut("Error requesting heap segment %p\n", SOS_PTR(dwAddr));
+ return FALSE;
+ }
+ dwAddrCurrObj = (DWORD_PTR)segment.mem;
+ continue;
+ }
+ else
+ break; // Done Verifying Heap
+ }
+
+ if (dwAddrSeg == (DWORD_PTR)heap.ephemeral_heap_segment
+ && dwAddrCurrObj >= end_youngest)
+ {
+ if (dwAddrCurrObj > end_youngest)
+ {
+ // prev_object length is too long
+ ExtOut("curr_object: %p > end_youngest: %p\n",
+ SOS_PTR(dwAddrCurrObj), SOS_PTR(end_youngest));
+ if (dwAddrPrevObj) {
+ DMLOut("Last good object: %s\n", DMLObject(dwAddrPrevObj));
+ }
+ return FALSE;
+ }
+ return FALSE;
+ }
+
+ if (FAILED(GetMTOfObject(dwAddrCurrObj, &dwAddrMethTable)))
+ {
+ return FALSE;
+ }
+
+ dwAddrMethTable = dwAddrMethTable & ~3;
+ if (dwAddrMethTable == 0)
+ {
+ // Is this the beginning of an allocation context?
+ int i;
+ for (i = 0; i < pallocInfo->num; i ++)
+ {
+ if (dwAddrCurrObj == (DWORD_PTR)pallocInfo->array[i].alloc_ptr)
+ {
+ dwAddrCurrObj =
+ (DWORD_PTR)pallocInfo->array[i].alloc_limit + Align(min_obj_size);
+ break;
+ }
+ }
+ if (i < pallocInfo->num)
+ continue;
+
+ // We also need to look at the gen0 alloc context.
+ if (dwAddrCurrObj == (DWORD_PTR) heap.generation_table[0].allocContextPtr)
+ {
+ dwAddrCurrObj = (DWORD_PTR) heap.generation_table[0].allocContextLimit + Align(min_obj_size);
+ continue;
+ }
+ }
+
+ BOOL bContainsPointers;
+ BOOL bMTOk = GetSizeEfficient(dwAddrCurrObj, dwAddrMethTable, FALSE, s, bContainsPointers);
+ if (verify && bMTOk)
+ bMTOk = VerifyObject (heap, dwAddrCurrObj, dwAddrMethTable, s, TRUE);
+ if (!bMTOk)
+ {
+ DMLOut("curr_object: %s\n", DMLListNearObj(dwAddrCurrObj));
+ if (dwAddrPrevObj)
+ DMLOut("Last good object: %s\n", DMLObject(dwAddrPrevObj));
+
+ ExtOut ("----------------\n");
+ return FALSE;
+ }
+
+ pFunc (dwAddrCurrObj, s, dwAddrMethTable, token);
+
+ // We believe we did this alignment in ObjectSize above.
+ assert((s & ALIGNCONST) == 0);
+ dwAddrPrevObj = dwAddrCurrObj;
+ sPrev = s;
+ bPrevFree = IsMTForFreeObj(dwAddrMethTable);
+
+ dwAddrCurrObj += s;
+ }
+
+ // Now for the large object generation:
+ dwAddrSeg = (DWORD_PTR)heap.generation_table[GetMaxGeneration()+1].start_segment;
+ dwAddr = dwAddrSeg;
+
+ if (segment.Request(g_sos, dwAddr, heap) != S_OK)
+ {
+ ExtOut("Error requesting heap segment %p\n", SOS_PTR(dwAddr));
+ return FALSE;
+ }
+
+ // dwAddrCurrObj = (DWORD_PTR)heap.generation_table[GetMaxGeneration()+1].allocation_start;
+ dwAddrCurrObj = (DWORD_PTR)segment.mem;
+
+ dwAddrPrevObj=0;
+
+ while(1)
+ {
+ if (IsInterrupt())
+ {
+ ExtOut("<heap traverse interrupted>\n");
+ return FALSE;
+ }
+
+ DWORD_PTR end_of_segment = (DWORD_PTR)segment.allocated;
+
+ if (dwAddrCurrObj >= (DWORD_PTR)end_of_segment)
+ {
+ if (dwAddrCurrObj > (DWORD_PTR)end_of_segment)
+ {
+ ExtOut("curr_object: %p > heap_segment_allocated (seg: %p)\n",
+ SOS_PTR(dwAddrCurrObj), SOS_PTR(dwAddrSeg));
+ if (dwAddrPrevObj) {
+ ExtOut("Last good object: %p\n", SOS_PTR(dwAddrPrevObj));
+ }
+ return FALSE;
+ }
+ dwAddrSeg = (DWORD_PTR)segment.next;
+ if (dwAddrSeg)
+ {
+ dwAddr = dwAddrSeg;
+ if (segment.Request(g_sos, dwAddr, heap) != S_OK)
+ {
+ ExtOut("Error requesting heap segment %p\n", SOS_PTR(dwAddr));
+ return FALSE;
+ }
+ dwAddrCurrObj = (DWORD_PTR)segment.mem;
+ continue;
+ }
+ else
+ break; // Done Verifying Heap
+ }
+
+ if (FAILED(GetMTOfObject(dwAddrCurrObj, &dwAddrMethTable)))
+ {
+ return FALSE;
+ }
+
+ dwAddrMethTable = dwAddrMethTable & ~3;
+ BOOL bContainsPointers;
+ BOOL bMTOk = GetSizeEfficient(dwAddrCurrObj, dwAddrMethTable, TRUE, s, bContainsPointers);
+ if (verify && bMTOk)
+ bMTOk = VerifyObject (heap, dwAddrCurrObj, dwAddrMethTable, s, TRUE);
+ if (!bMTOk)
+ {
+ DMLOut("curr_object: %s\n", DMLListNearObj(dwAddrCurrObj));
+
+ if (dwAddrPrevObj)
+ DMLOut("Last good object: %s\n", dwAddrPrevObj);
+
+ ExtOut ("----------------\n");
+ return FALSE;
+ }
+
+ pFunc (dwAddrCurrObj, s, dwAddrMethTable, token);
+
+ // We believe we did this alignment in ObjectSize above.
+ assert((s & ALIGNCONSTLARGE) == 0);
+ dwAddrPrevObj = dwAddrCurrObj;
+ dwAddrCurrObj += s;
+ }
+
+ return TRUE;
+}
+
+BOOL GCHeapsTraverse(VISITGCHEAPFUNC pFunc, LPVOID token, BOOL verify)
+{
+ // Obtain allocation context for each managed thread.
+ AllocInfo allocInfo;
+ allocInfo.Init();
+
+ if (!IsServerBuild())
+ {
+ DacpGcHeapDetails heapDetails;
+ if (heapDetails.Request(g_sos) != S_OK)
+ {
+ ExtOut("Error requesting gc heap details\n");
+ return FALSE;
+ }
+
+ return GCHeapTraverse (heapDetails, &allocInfo, pFunc, token, verify);
+ }
+ else
+ {
+ DacpGcHeapData gcheap;
+ if (gcheap.Request(g_sos) != S_OK)
+ {
+ ExtOut("Error requesting GC Heap data\n");
+ return FALSE;
+ }
+
+ DWORD dwAllocSize;
+ DWORD dwNHeaps = gcheap.HeapCount;
+ if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
+ {
+ ExtOut("Failed to get GCHeaps: integer overflow error\n");
+ return FALSE;
+ }
+ CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
+ if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
+ {
+ ExtOut("Failed to get GCHeaps\n");
+ return FALSE;
+ }
+
+ DWORD n;
+ for (n = 0; n < dwNHeaps; n ++)
+ {
+ DacpGcHeapDetails heapDetails;
+ if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
+ {
+ ExtOut("Error requesting details\n");
+ return FALSE;
+ }
+
+ if (!GCHeapTraverse (heapDetails, &allocInfo, pFunc, token, verify))
+ {
+ ExtOut("Traversing a gc heap failed\n");
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+GCHeapSnapshot::GCHeapSnapshot()
+{
+ m_isBuilt = FALSE;
+ m_heapDetails = NULL;
+}
+
+///////////////////////////////////////////////////////////
+SegmentLookup::SegmentLookup()
+{
+ m_iSegmentsSize = m_iSegmentCount = 0;
+
+ m_segments = new DacpHeapSegmentData[nSegLookupStgIncrement];
+ if (m_segments == NULL)
+ {
+ ReportOOM();
+ }
+ else
+ {
+ m_iSegmentsSize = nSegLookupStgIncrement;
+ }
+}
+
+BOOL SegmentLookup::AddSegment(DacpHeapSegmentData *pData)
+{
+ // appends the address of a new (initialized) instance of DacpHeapSegmentData to the list of segments
+ // (m_segments) adding space for a segment when necessary.
+ // @todo Microsoft: The field name m_iSegmentSize is a little misleading. It's not the size in bytes,
+ // but the number of elements allocated for the array. It probably should have been named something like
+ // m_iMaxSegments instead.
+ if (m_iSegmentCount >= m_iSegmentsSize)
+ {
+ // expand buffer--allocate enough space to hold the elements we already have plus nSegLookupStgIncrement
+ // more elements
+ DacpHeapSegmentData *pNewBuffer = new DacpHeapSegmentData[m_iSegmentsSize+nSegLookupStgIncrement];
+ if (pNewBuffer==NULL)
+ return FALSE;
+
+ // copy the old elements into the new array
+ memcpy(pNewBuffer, m_segments, sizeof(DacpHeapSegmentData)*m_iSegmentsSize);
+
+ // record the new number of elements available
+ m_iSegmentsSize+=nSegLookupStgIncrement;
+
+ // delete the old array
+ delete [] m_segments;
+
+ // set m_segments to point to the new array
+ m_segments = pNewBuffer;
+ }
+
+ // add pData to the array
+ m_segments[m_iSegmentCount++] = *pData;
+
+ return TRUE;
+}
+
+SegmentLookup::~SegmentLookup()
+{
+ if (m_segments)
+ {
+ delete [] m_segments;
+ m_segments = NULL;
+ }
+}
+
+void SegmentLookup::Clear()
+{
+ m_iSegmentCount = 0;
+}
+
+CLRDATA_ADDRESS SegmentLookup::GetHeap(CLRDATA_ADDRESS object, BOOL& bFound)
+{
+ CLRDATA_ADDRESS ret = NULL;
+ bFound = FALSE;
+
+ // Visit our segments
+ for (int i=0; i<m_iSegmentCount; i++)
+ {
+ if (TO_TADDR(m_segments[i].mem) <= TO_TADDR(object) &&
+ TO_TADDR(m_segments[i].highAllocMark) > TO_TADDR(object))
+ {
+ ret = m_segments[i].gc_heap;
+ bFound = TRUE;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+BOOL GCHeapSnapshot::Build()
+{
+ Clear();
+
+ m_isBuilt = FALSE;
+
+ ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ /// 1. Get some basic information such as the heap type (SVR or WKS), how many heaps there are, mode and max generation
+ /// (See code:ClrDataAccess::RequestGCHeapData)
+ ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ if (m_gcheap.Request(g_sos) != S_OK)
+ {
+ ExtOut("Error requesting GC Heap data\n");
+ return FALSE;
+ }
+
+ ArrayHolder<CLRDATA_ADDRESS> heapAddrs = NULL;
+
+ ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ /// 2. Get a list of the addresses of the heaps when we have multiple heaps in server mode
+ ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ if (m_gcheap.bServerMode)
+ {
+ UINT AllocSize;
+ // allocate an array to hold the starting addresses of each heap when we're in server mode
+ if (!ClrSafeInt<UINT>::multiply(sizeof(CLRDATA_ADDRESS), m_gcheap.HeapCount, AllocSize) ||
+ (heapAddrs = new CLRDATA_ADDRESS [m_gcheap.HeapCount]) == NULL)
+ {
+ ReportOOM();
+ return FALSE;
+ }
+
+ // and initialize it with their addresses (see code:ClrDataAccess::RequestGCHeapList
+ // for details)
+ if (g_sos->GetGCHeapList(m_gcheap.HeapCount, heapAddrs, NULL) != S_OK)
+ {
+ ExtOut("Failed to get GCHeaps\n");
+ return FALSE;
+ }
+ }
+
+ ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ /// 3. Get some necessary information about each heap, such as the card table location, the generation
+ /// table, the heap bounds, etc., and retrieve the heap segments
+ ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ // allocate an array to hold the information
+ m_heapDetails = new DacpGcHeapDetails[m_gcheap.HeapCount];
+
+ if (m_heapDetails == NULL)
+ {
+ ReportOOM();
+ return FALSE;
+ }
+
+ // get the heap information for each heap
+ // See code:ClrDataAccess::RequestGCHeapDetails for details
+ for (UINT n = 0; n < m_gcheap.HeapCount; n ++)
+ {
+ if (m_gcheap.bServerMode)
+ {
+ if (m_heapDetails[n].Request(g_sos, heapAddrs[n]) != S_OK)
+ {
+ ExtOut("Error requesting details\n");
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (m_heapDetails[n].Request(g_sos) != S_OK)
+ {
+ ExtOut("Error requesting details\n");
+ return FALSE;
+ }
+ }
+
+ // now get information about the heap segments for this heap
+ if (!AddSegments(m_heapDetails[n]))
+ {
+ ExtOut("Failed to retrieve segments for gc heap\n");
+ return FALSE;
+ }
+ }
+
+ m_isBuilt = TRUE;
+ return TRUE;
+}
+
+BOOL GCHeapSnapshot::AddSegments(DacpGcHeapDetails& details)
+{
+ int n = 0;
+ DacpHeapSegmentData segment;
+
+ // This array of two addresses gives us access to all the segments. The generation segments are linked
+ // to each other, starting with the maxGeneration segment. The second address gives us the large object heap.
+ CLRDATA_ADDRESS AddrSegs[] =
+ {
+ details.generation_table[GetMaxGeneration()].start_segment,
+ details.generation_table[GetMaxGeneration()+1].start_segment // large object heap
+ };
+
+ // this loop will get information for all the heap segments in this heap. The outer loop iterates once
+ // for the "normal" generation segments and once for the large object heap. The inner loop follows the chain
+ // of segments rooted at AddrSegs[i]
+ for (unsigned int i = 0; i < sizeof(AddrSegs)/sizeof(AddrSegs[0]); ++i)
+ {
+ CLRDATA_ADDRESS AddrSeg = AddrSegs[i];
+
+ while (AddrSeg != NULL)
+ {
+ if (IsInterrupt())
+ {
+ return FALSE;
+ }
+ // Initialize segment by copying fields from the target's heap segment at AddrSeg.
+ // See code:ClrDataAccess::RequestGCHeapSegment for details.
+ if (segment.Request(g_sos, AddrSeg, details) != S_OK)
+ {
+ ExtOut("Error requesting heap segment %p\n", SOS_PTR(AddrSeg));
+ return FALSE;
+ }
+ if (n++ > nMaxHeapSegmentCount) // that would be insane
+ {
+ ExtOut("More than %d heap segments, there must be an error\n", nMaxHeapSegmentCount);
+ return FALSE;
+ }
+
+ // add the new segment to the array of segments. This will expand the array if necessary
+ if (!m_segments.AddSegment(&segment))
+ {
+ ExtOut("strike: Failed to store segment\n");
+ return FALSE;
+ }
+ // get the next segment in the chain
+ AddrSeg = segment.next;
+ }
+ }
+
+ return TRUE;
+}
+
+void GCHeapSnapshot::Clear()
+{
+ if (m_heapDetails != NULL)
+ {
+ delete [] m_heapDetails;
+ m_heapDetails = NULL;
+ }
+
+ m_segments.Clear();
+
+ m_isBuilt = FALSE;
+}
+
+GCHeapSnapshot g_snapshot;
+
+DacpGcHeapDetails *GCHeapSnapshot::GetHeap(CLRDATA_ADDRESS objectPointer)
+{
+ // We need bFound because heap will be NULL if we are Workstation Mode.
+ // We still need a way to know if the address was found in our segment
+ // list.
+ BOOL bFound = FALSE;
+ CLRDATA_ADDRESS heap = m_segments.GetHeap(objectPointer, bFound);
+ if (heap)
+ {
+ for (UINT i=0; i<m_gcheap.HeapCount; i++)
+ {
+ if (m_heapDetails[i].heapAddr == heap)
+ return m_heapDetails + i;
+ }
+ }
+ else if (!m_gcheap.bServerMode)
+ {
+ if (bFound)
+ {
+ return m_heapDetails;
+ }
+ }
+
+ // Not found
+ return NULL;
+}
+
+// TODO: Do we need to handle the LOH here?
+int GCHeapSnapshot::GetGeneration(CLRDATA_ADDRESS objectPointer)
+{
+ DacpGcHeapDetails *pDetails = GetHeap(objectPointer);
+ if (pDetails == NULL)
+ {
+ ExtOut("Object %p has no generation\n", SOS_PTR(objectPointer));
+ return 0;
+ }
+
+ TADDR taObj = TO_TADDR(objectPointer);
+ // The DAC doesn't fill the generation table with true CLRDATA_ADDRESS values
+ // but rather with ULONG64 values (i.e. non-sign-extended 64-bit values)
+ // We use the TO_TADDR below to ensure we won't break if this will ever
+ // be fixed in the DAC.
+ if (taObj >= TO_TADDR(pDetails->generation_table[0].allocation_start) &&
+ taObj <= TO_TADDR(pDetails->alloc_allocated))
+ return 0;
+
+ if (taObj >= TO_TADDR(pDetails->generation_table[1].allocation_start) &&
+ taObj <= TO_TADDR(pDetails->generation_table[0].allocation_start))
+ return 1;
+
+ return 2;
+}
+
+
+DWORD_PTR g_trav_totalSize = 0;
+DWORD_PTR g_trav_wastedSize = 0;
+
+void LoaderHeapTraverse(CLRDATA_ADDRESS blockData,size_t blockSize,BOOL blockIsCurrentBlock)
+{
+ DWORD_PTR dwAddr1;
+ DWORD_PTR curSize = 0;
+ char ch;
+ for (dwAddr1 = (DWORD_PTR)blockData;
+ dwAddr1 < (DWORD_PTR)blockData + blockSize;
+ dwAddr1 += OSPageSize())
+ {
+ if (IsInterrupt())
+ break;
+ if (SafeReadMemory(dwAddr1, &ch, sizeof(ch), NULL))
+ {
+ curSize += OSPageSize();
+ }
+ else
+ break;
+ }
+
+ if (!blockIsCurrentBlock)
+ {
+ g_trav_wastedSize += blockSize - curSize;
+ }
+
+ g_trav_totalSize += curSize;
+ ExtOut("%p(%x:%x) ", SOS_PTR(blockData), blockSize, curSize);
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function prints out the size for various heaps. *
+* total - the total size of the heap *
+* wasted - the amount of size wasted by the heap. *
+* *
+\**********************************************************************/
+void PrintHeapSize(DWORD_PTR total, DWORD_PTR wasted)
+{
+ ExtOut("Size: 0x%" POINTERSIZE_TYPE "x (%" POINTERSIZE_TYPE "lu) bytes", total, total);
+ if (wasted)
+ ExtOut(" total, 0x%" POINTERSIZE_TYPE "x (%" POINTERSIZE_TYPE "lu) bytes wasted", wasted, wasted);
+ ExtOut(".\n");
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function prints out the size information for the JIT heap. *
+* *
+* Returns: The size of this heap. *
+* *
+\**********************************************************************/
+DWORD_PTR JitHeapInfo()
+{
+ // walk ExecutionManager__m_pJitList
+ unsigned int count = 0;
+ if (FAILED(g_sos->GetJitManagerList(0, NULL, &count)))
+ {
+ ExtOut("Unable to get JIT info\n");
+ return 0;
+ }
+
+ ArrayHolder<DacpJitManagerInfo> pArray = new DacpJitManagerInfo[count];
+ if (pArray==NULL)
+ {
+ ReportOOM();
+ return 0;
+ }
+
+ if (g_sos->GetJitManagerList(count, pArray, NULL) != S_OK)
+ {
+ ExtOut("Unable to get array of JIT Managers\n");
+ return 0;
+ }
+
+ DWORD_PTR totalSize = 0;
+ DWORD_PTR wasted = 0;
+
+ for (unsigned int n=0; n < count; n++)
+ {
+ if (IsInterrupt())
+ break;
+
+ if (IsMiIL(pArray[n].codeType)) // JIT
+ {
+ unsigned int heapCount = 0;
+ if (FAILED(g_sos->GetCodeHeapList(pArray[n].managerAddr, 0, NULL, &heapCount)))
+ {
+ ExtOut("Error getting EEJitManager code heaps\n");
+ break;
+ }
+
+ if (heapCount > 0)
+ {
+ ArrayHolder<DacpJitCodeHeapInfo> codeHeapInfo = new DacpJitCodeHeapInfo[heapCount];
+ if (codeHeapInfo == NULL)
+ {
+ ReportOOM();
+ break;
+ }
+
+ if (g_sos->GetCodeHeapList(pArray[n].managerAddr, heapCount, codeHeapInfo, NULL) != S_OK)
+ {
+ ExtOut("Unable to get code heap info\n");
+ break;
+ }
+
+ for (unsigned int iHeaps = 0; iHeaps < heapCount; iHeaps++)
+ {
+ if (IsInterrupt())
+ break;
+
+ if (codeHeapInfo[iHeaps].codeHeapType == CODEHEAP_LOADER)
+ {
+ ExtOut("LoaderCodeHeap: ");
+ totalSize += LoaderHeapInfo(codeHeapInfo[iHeaps].LoaderHeap, &wasted);
+ }
+ else if (codeHeapInfo[iHeaps].codeHeapType == CODEHEAP_HOST)
+ {
+ ExtOut("HostCodeHeap: ");
+ ExtOut("%p ", SOS_PTR(codeHeapInfo[iHeaps].HostData.baseAddr));
+ DWORD dwSize = (DWORD)(codeHeapInfo[iHeaps].HostData.currentAddr - codeHeapInfo[iHeaps].HostData.baseAddr);
+ PrintHeapSize(dwSize, 0);
+ totalSize += dwSize;
+ }
+ }
+ }
+ }
+ else if (!IsMiNative(pArray[n].codeType)) // ignore native heaps for now
+ {
+ ExtOut("Unknown Jit encountered, ignored\n");
+ }
+ }
+
+ ExtOut("Total size: ");
+ PrintHeapSize(totalSize, wasted);
+
+ return totalSize;
+}
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function prints out the loader heap info for a single AD. *
+* pLoaderHeapAddr - pointer to the loader heap *
+* wasted - a pointer to store the number of bytes wasted in this *
+* VSDHeap (this pointer can be NULL) *
+* *
+* Returns: The size of this heap. *
+* *
+\**********************************************************************/
+DWORD_PTR LoaderHeapInfo(CLRDATA_ADDRESS pLoaderHeapAddr, DWORD_PTR *wasted)
+{
+ g_trav_totalSize = 0;
+ g_trav_wastedSize = 0;
+
+ if (pLoaderHeapAddr)
+ g_sos->TraverseLoaderHeap(pLoaderHeapAddr, LoaderHeapTraverse);
+
+ PrintHeapSize(g_trav_totalSize, g_trav_wastedSize);
+
+ if (wasted)
+ *wasted += g_trav_wastedSize;
+ return g_trav_totalSize;
+}
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function prints out the heap info for a single VSDHeap. *
+* name - the name to print *
+* type - the type of heap *
+* appDomain - the app domain in which this resides *
+* wasted - a pointer to store the number of bytes wasted in this *
+* VSDHeap (this pointer can be NULL) *
+* *
+* Returns: The size of this heap. *
+* *
+\**********************************************************************/
+static DWORD_PTR PrintOneVSDHeap(const char *name, VCSHeapType type, CLRDATA_ADDRESS appDomain, DWORD_PTR *wasted)
+{
+ g_trav_totalSize = 0; g_trav_wastedSize = 0;
+
+ ExtOut(name);
+ g_sos->TraverseVirtCallStubHeap(appDomain, type, LoaderHeapTraverse);
+
+ PrintHeapSize(g_trav_totalSize, g_trav_wastedSize);
+ if (wasted)
+ *wasted += g_trav_wastedSize;
+ return g_trav_totalSize;
+}
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function prints out the heap info for VSDHeaps. *
+* appDomain - The AppDomain to print info for. *
+* wasted - a pointer to store the number of bytes wasted in this *
+* AppDomain (this pointer can be NULL) *
+* *
+* Returns: The size of this heap. *
+* *
+\**********************************************************************/
+DWORD_PTR VSDHeapInfo(CLRDATA_ADDRESS appDomain, DWORD_PTR *wasted)
+{
+ DWORD_PTR totalSize = 0;
+
+ if (appDomain)
+ {
+ totalSize += PrintOneVSDHeap(" IndcellHeap: ", IndcellHeap, appDomain, wasted);
+ totalSize += PrintOneVSDHeap(" LookupHeap: ", LookupHeap, appDomain, wasted);
+ totalSize += PrintOneVSDHeap(" ResolveHeap: ", ResolveHeap, appDomain, wasted);
+ totalSize += PrintOneVSDHeap(" DispatchHeap: ", DispatchHeap, appDomain, wasted);
+ totalSize += PrintOneVSDHeap(" CacheEntryHeap: ", CacheEntryHeap, appDomain, wasted);
+ }
+
+ return totalSize;
+}
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function prints out the heap info for a domain *
+* name - the name of the domain (to be printed) *
+* adPtr - a pointer to the AppDomain to print info about *
+* outSize - a pointer to an int to store the size at (this may be *
+* NULL) *
+* outWasted - a pointer to an int to store the number of bytes this *
+* domain is wasting (this may be NULL) *
+* *
+* returns: SUCCESS if we successfully printed out the domain heap *
+* info, FAILED otherwise; if FAILED, outSize and *
+* outWasted are untouched. *
+* *
+\**********************************************************************/
+HRESULT PrintDomainHeapInfo(const char *name, CLRDATA_ADDRESS adPtr, DWORD_PTR *outSize, DWORD_PTR *outWasted)
+{
+ DacpAppDomainData appDomain;
+ HRESULT hr = appDomain.Request(g_sos, adPtr);
+ if (FAILED(hr))
+ {
+ ExtOut("Unable to get information for %s.\n", name);
+ return hr;
+ }
+
+ ExtOut("--------------------------------------\n");
+
+ const int column = 19;
+ ExtOut("%s:", name);
+ WhitespaceOut(column - (int)strlen(name) - 1);
+ DMLOut("%s\n", DMLDomain(adPtr));
+
+ DWORD_PTR domainHeapSize = 0;
+ DWORD_PTR wasted = 0;
+
+ ExtOut("LowFrequencyHeap: ");
+ domainHeapSize += LoaderHeapInfo(appDomain.pLowFrequencyHeap, &wasted);
+
+ ExtOut("HighFrequencyHeap: ");
+ domainHeapSize += LoaderHeapInfo(appDomain.pHighFrequencyHeap, &wasted);
+
+ ExtOut("StubHeap: ");
+ domainHeapSize += LoaderHeapInfo(appDomain.pStubHeap, &wasted);
+
+ ExtOut("Virtual Call Stub Heap:\n");
+ domainHeapSize += VSDHeapInfo(appDomain.AppDomainPtr, &wasted);
+
+ ExtOut("Total size: ");
+ PrintHeapSize(domainHeapSize, wasted);
+
+ if (outSize)
+ *outSize += domainHeapSize;
+ if (outWasted)
+ *outWasted += wasted;
+
+ return hr;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function prints out the heap info for a list of modules. *
+* moduleList - an array of modules *
+* count - the number of modules in moduleList *
+* type - the type of heap *
+* outWasted - a pointer to store the number of bytes wasted in this *
+* heap (this pointer can be NULL) *
+* *
+* Returns: The size of this heap. *
+* *
+\**********************************************************************/
+DWORD_PTR PrintModuleHeapInfo(__out_ecount(count) DWORD_PTR *moduleList, int count, ModuleHeapType type, DWORD_PTR *outWasted)
+{
+ DWORD_PTR toReturn = 0;
+ DWORD_PTR wasted = 0;
+
+ if (IsMiniDumpFile())
+ {
+ ExtOut("<no information>\n");
+ }
+ else
+ {
+ DWORD_PTR thunkHeapSize = 0;
+
+ for (int i = 0; i < count; i++)
+ {
+ CLRDATA_ADDRESS addr = moduleList[i];
+ DacpModuleData dmd;
+ if (dmd.Request(g_sos, addr) != S_OK)
+ {
+ ExtOut("Unable to read module %p\n", SOS_PTR(addr));
+ }
+ else
+ {
+ DMLOut("Module %s: ", DMLModule(addr));
+ CLRDATA_ADDRESS heap = type == ModuleHeapType_ThunkHeap ? dmd.pThunkHeap : dmd.pLookupTableHeap;
+ thunkHeapSize += LoaderHeapInfo(heap, &wasted);
+ }
+ }
+
+ ExtOut("Total size: " WIN86_8SPACES);
+ PrintHeapSize(thunkHeapSize, wasted);
+
+ toReturn = thunkHeapSize;
+ }
+
+ if (outWasted)
+ *outWasted += wasted;
+
+ return toReturn;
+}
diff --git a/src/ToolBox/SOS/Strike/exts.cpp b/src/ToolBox/SOS/Strike/exts.cpp
new file mode 100644
index 0000000000..0b1f976cc2
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/exts.cpp
@@ -0,0 +1,435 @@
+// 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 "exts.h"
+#include "disasm.h"
+#ifndef FEATURE_PAL
+#include "EventCallbacks.h"
+
+#define VER_PRODUCTVERSION_W (0x0100)
+
+//
+// globals
+//
+EXT_API_VERSION ApiVersion = { (VER_PRODUCTVERSION_W >> 8), (VER_PRODUCTVERSION_W & 0xff), EXT_API_VERSION_NUMBER64, 0 };
+WINDBG_EXTENSION_APIS ExtensionApis;
+
+ULONG PageSize;
+
+OnUnloadTask *OnUnloadTask::s_pUnloadTaskList = NULL;
+
+//
+// Valid for the lifetime of the debug session.
+//
+
+ULONG TargetMachine;
+BOOL Connected;
+ULONG g_TargetClass;
+DWORD_PTR g_filterHint = 0;
+
+PDEBUG_CLIENT g_ExtClient;
+PDEBUG_DATA_SPACES2 g_ExtData2;
+PDEBUG_SYMBOLS2 g_ExtSymbols2;
+PDEBUG_ADVANCED3 g_ExtAdvanced3;
+PDEBUG_CLIENT g_pCallbacksClient;
+
+#else
+
+DebugClient* g_DebugClient;
+ILLDBServices* g_ExtServices;
+
+#endif // FEATURE_PAL
+
+IMachine* g_targetMachine = NULL;
+BOOL g_bDacBroken = FALSE;
+
+PDEBUG_CONTROL2 g_ExtControl;
+PDEBUG_DATA_SPACES g_ExtData;
+PDEBUG_REGISTERS g_ExtRegisters;
+PDEBUG_SYMBOLS g_ExtSymbols;
+PDEBUG_SYSTEM_OBJECTS g_ExtSystem;
+
+#define SOS_ExtQueryFailGo(var, riid) \
+ var = NULL; \
+ if ((Status = client->QueryInterface(__uuidof(riid), \
+ (void **)&var)) != S_OK) \
+ { \
+ goto Fail; \
+ }
+
+// Queries for all debugger interfaces.
+#ifndef FEATURE_PAL
+extern "C" HRESULT
+ExtQuery(PDEBUG_CLIENT client)
+{
+ g_ExtClient = client;
+#else
+extern "C" HRESULT
+ExtQuery(ILLDBServices* services)
+{
+ g_ExtServices = services;
+ DebugClient* client = new DebugClient(services);
+ g_DebugClient = client;
+#endif
+ HRESULT Status;
+ SOS_ExtQueryFailGo(g_ExtControl, IDebugControl2);
+ SOS_ExtQueryFailGo(g_ExtData, IDebugDataSpaces);
+ SOS_ExtQueryFailGo(g_ExtRegisters, IDebugRegisters);
+ SOS_ExtQueryFailGo(g_ExtSymbols, IDebugSymbols);
+ SOS_ExtQueryFailGo(g_ExtSystem, IDebugSystemObjects);
+#ifndef FEATURE_PAL
+ SOS_ExtQueryFailGo(g_ExtData2, IDebugDataSpaces2);
+ SOS_ExtQueryFailGo(g_ExtSymbols2, IDebugSymbols2);
+ SOS_ExtQueryFailGo(g_ExtAdvanced3, IDebugAdvanced3);
+#endif // FEATURE_PAL
+ return S_OK;
+
+ Fail:
+ if (Status == E_OUTOFMEMORY)
+ ReportOOM();
+
+ ExtRelease();
+ return Status;
+}
+
+extern "C" HRESULT
+ArchQuery(void)
+{
+ ULONG targetArchitecture;
+ IMachine* targetMachine = NULL;
+
+ g_ExtControl->GetExecutingProcessorType(&targetArchitecture);
+
+#ifdef SOS_TARGET_AMD64
+ if(targetArchitecture == IMAGE_FILE_MACHINE_AMD64)
+ {
+ targetMachine = AMD64Machine::GetInstance();
+ }
+#endif // SOS_TARGET_AMD64
+#ifdef SOS_TARGET_X86
+ if (targetArchitecture == IMAGE_FILE_MACHINE_I386)
+ {
+ targetMachine = X86Machine::GetInstance();
+ }
+#endif // SOS_TARGET_X86
+#ifdef SOS_TARGET_ARM
+ if (targetArchitecture == IMAGE_FILE_MACHINE_ARMNT)
+ {
+ targetMachine = ARMMachine::GetInstance();
+ }
+#endif // SOS_TARGET_ARM
+#ifdef SOS_TARGET_ARM64
+ if (targetArchitecture == IMAGE_FILE_MACHINE_ARM64)
+ {
+ targetMachine = ARM64Machine::GetInstance();
+ }
+#endif // SOS_TARGET_ARM64
+
+ if (targetMachine == NULL)
+ {
+ g_targetMachine = NULL;
+ ExtErr("SOS does not support the current target architecture.\n");
+ return E_FAIL;
+ }
+
+ g_targetMachine = targetMachine;
+ return S_OK;
+}
+
+// Cleans up all debugger interfaces.
+void
+ExtRelease(void)
+{
+ EXT_RELEASE(g_ExtControl);
+ EXT_RELEASE(g_ExtData);
+ EXT_RELEASE(g_ExtRegisters);
+ EXT_RELEASE(g_ExtSymbols);
+ EXT_RELEASE(g_ExtSystem);
+#ifndef FEATURE_PAL
+ EXT_RELEASE(g_ExtData2);
+ EXT_RELEASE(g_ExtSymbols2);
+ EXT_RELEASE(g_ExtAdvanced3);
+ g_ExtClient = NULL;
+#else
+ EXT_RELEASE(g_DebugClient);
+ g_ExtServices = NULL;
+#endif // FEATURE_PAL
+}
+
+#ifndef FEATURE_PAL
+
+BOOL IsMiniDumpFileNODAC();
+extern HMODULE g_hInstance;
+
+// This function throws an exception that can be caught by the debugger,
+// instead of allowing the default CRT behavior of invoking Watson to failfast.
+void __cdecl _SOS_invalid_parameter(
+ const WCHAR * expression,
+ const WCHAR * function,
+ const WCHAR * file,
+ unsigned int line,
+ uintptr_t pReserved
+)
+{
+ ExtErr("\nSOS failure!\n");
+ throw "SOS failure";
+}
+
+// Unregisters our windbg event callbacks and releases the client, event callback objects
+void CleanupEventCallbacks()
+{
+ if(g_pCallbacksClient != NULL)
+ {
+ g_pCallbacksClient->Release();
+ g_pCallbacksClient = NULL;
+ }
+}
+
+bool g_Initialized = false;
+
+extern "C"
+HRESULT
+CALLBACK
+DebugExtensionInitialize(PULONG Version, PULONG Flags)
+{
+ IDebugClient *DebugClient;
+ PDEBUG_CONTROL DebugControl;
+ HRESULT Hr;
+
+ *Version = DEBUG_EXTENSION_VERSION(1, 0);
+ *Flags = 0;
+
+ if (g_Initialized)
+ {
+ return S_OK;
+ }
+ g_Initialized = true;
+
+ if ((Hr = DebugCreate(__uuidof(IDebugClient),
+ (void **)&DebugClient)) != S_OK)
+ {
+ return Hr;
+ }
+ if ((Hr = DebugClient->QueryInterface(__uuidof(IDebugControl),
+ (void **)&DebugControl)) != S_OK)
+ {
+ return Hr;
+ }
+
+ ExtensionApis.nSize = sizeof (ExtensionApis);
+ if ((Hr = DebugControl->GetWindbgExtensionApis64(&ExtensionApis)) != S_OK)
+ {
+ return Hr;
+ }
+
+ // Fixes the "Unable to read dynamic function table entries" error messages by disabling the WinDbg security
+ // feature that prevents the loading of unknown out of proc tack walkers.
+ DebugControl->Execute(DEBUG_OUTCTL_IGNORE, ".settings set EngineInitialization.VerifyFunctionTableCallbacks=false",
+ DEBUG_EXECUTE_NOT_LOGGED | DEBUG_EXECUTE_NO_REPEAT);
+
+ ExtQuery(DebugClient);
+ if (IsMiniDumpFileNODAC())
+ {
+ ExtOut (
+ "----------------------------------------------------------------------------\n"
+ "The user dump currently examined is a minidump. Consequently, only a subset\n"
+ "of sos.dll functionality will be available. If needed, attaching to the live\n"
+ "process or debugging a full dump will allow access to sos.dll's full feature\n"
+ "set.\n"
+ "To create a full user dump use the command: .dump /ma <filename>\n"
+ "----------------------------------------------------------------------------\n");
+ }
+ ExtRelease();
+
+ OnUnloadTask::Register(CleanupEventCallbacks);
+ g_pCallbacksClient = DebugClient;
+ EventCallbacks* pCallbacksObj = new EventCallbacks(DebugClient);
+ IDebugEventCallbacks* pCallbacks = NULL;
+ pCallbacksObj->QueryInterface(__uuidof(IDebugEventCallbacks), (void**)&pCallbacks);
+ pCallbacksObj->Release();
+
+ if(FAILED(Hr = g_pCallbacksClient->SetEventCallbacks(pCallbacks)))
+ {
+ ExtOut ("SOS: Failed to register callback events\n");
+ pCallbacks->Release();
+ return Hr;
+ }
+ pCallbacks->Release();
+
+#ifndef _ARM_
+ // Make sure we do not tear down the debugger when a security function fails
+ // Since we link statically against CRT this will only affect the SOS module.
+ _set_invalid_parameter_handler(_SOS_invalid_parameter);
+#endif
+
+ DebugControl->Release();
+ return S_OK;
+}
+
+extern "C"
+void
+CALLBACK
+DebugExtensionNotify(ULONG Notify, ULONG64 /*Argument*/)
+{
+ //
+ // The first time we actually connect to a target, get the page size
+ //
+
+ if ((Notify == DEBUG_NOTIFY_SESSION_ACCESSIBLE) && (!Connected))
+ {
+ IDebugClient *DebugClient;
+ PDEBUG_DATA_SPACES DebugDataSpaces;
+ PDEBUG_CONTROL DebugControl;
+ HRESULT Hr;
+ ULONG64 Page;
+
+ if ((Hr = DebugCreate(__uuidof(IDebugClient),
+ (void **)&DebugClient)) == S_OK)
+ {
+ //
+ // Get the page size and PAE enable flag
+ //
+
+ if ((Hr = DebugClient->QueryInterface(__uuidof(IDebugDataSpaces),
+ (void **)&DebugDataSpaces)) == S_OK)
+ {
+ if ((Hr = DebugDataSpaces->ReadDebuggerData(
+ DEBUG_DATA_MmPageSize, &Page,
+ sizeof(Page), NULL)) == S_OK)
+ {
+ PageSize = (ULONG)(ULONG_PTR)Page;
+ }
+
+ DebugDataSpaces->Release();
+ }
+ //
+ // Get the architecture type.
+ //
+
+ if ((Hr = DebugClient->QueryInterface(__uuidof(IDebugControl),
+ (void **)&DebugControl)) == S_OK)
+ {
+ if ((Hr = DebugControl->GetActualProcessorType(
+ &TargetMachine)) == S_OK)
+ {
+ Connected = TRUE;
+ }
+ ULONG Qualifier;
+ if ((Hr = DebugControl->GetDebuggeeType(&g_TargetClass, &Qualifier)) == S_OK)
+ {
+ }
+
+ DebugControl->Release();
+ }
+
+ DebugClient->Release();
+ }
+ }
+
+
+ if (Notify == DEBUG_NOTIFY_SESSION_INACTIVE)
+ {
+ Connected = FALSE;
+ PageSize = 0;
+ TargetMachine = 0;
+ }
+
+ return;
+}
+
+extern "C"
+void
+CALLBACK
+DebugExtensionUninitialize(void)
+{
+ // execute all registered cleanup tasks
+ OnUnloadTask::Run();
+ return;
+}
+
+
+BOOL DllInit(
+ HANDLE /*hModule*/,
+ DWORD dwReason,
+ DWORD /*dwReserved*/
+ )
+{
+ switch (dwReason) {
+ case DLL_THREAD_ATTACH:
+ break;
+
+ case DLL_THREAD_DETACH:
+ break;
+
+ case DLL_PROCESS_DETACH:
+ break;
+
+ case DLL_PROCESS_ATTACH:
+ break;
+ }
+
+ return TRUE;
+}
+
+BOOL WINAPI DllMain(HANDLE hInstance, DWORD dwReason, LPVOID lpReserved)
+{
+ if (dwReason == DLL_PROCESS_ATTACH)
+ {
+ g_hInstance = (HMODULE) hInstance;
+ }
+ return true;
+}
+
+#else // FEATURE_PAL
+
+HRESULT
+DebugClient::QueryInterface(
+ REFIID InterfaceId,
+ PVOID* Interface
+ )
+{
+ if (InterfaceId == __uuidof(IUnknown) ||
+ InterfaceId == __uuidof(IDebugControl2) ||
+ InterfaceId == __uuidof(IDebugControl4) ||
+ InterfaceId == __uuidof(IDebugDataSpaces) ||
+ InterfaceId == __uuidof(IDebugSymbols) ||
+ InterfaceId == __uuidof(IDebugSystemObjects) ||
+ InterfaceId == __uuidof(IDebugRegisters))
+ {
+ *Interface = this;
+ AddRef();
+ return S_OK;
+ }
+ else
+ {
+ *Interface = NULL;
+ return E_NOINTERFACE;
+ }
+}
+
+ULONG
+DebugClient::AddRef()
+{
+ LONG ref = InterlockedIncrement(&m_ref);
+ return ref;
+}
+
+ULONG
+DebugClient::Release()
+{
+ LONG ref = InterlockedDecrement(&m_ref);
+ if (ref == 0)
+ {
+ m_lldbservices->Release();
+ delete this;
+ }
+ return ref;
+}
+
+#endif // FEATURE_PAL
diff --git a/src/ToolBox/SOS/Strike/exts.h b/src/ToolBox/SOS/Strike/exts.h
new file mode 100644
index 0000000000..36b5230c37
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/exts.h
@@ -0,0 +1,513 @@
+// 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.
+
+// ==++==
+//
+
+//
+// ==--==
+#ifndef __exts_h__
+#define __exts_h__
+
+#define KDEXT_64BIT
+
+#include <windows.h>
+#include <winternl.h>
+
+#if defined(_MSC_VER)
+#pragma warning(disable:4245) // signed/unsigned mismatch
+#pragma warning(disable:4100) // unreferenced formal parameter
+#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
+#pragma warning(disable:4127) // conditional expression is constant
+#pragma warning(disable:4430) // missing type specifier: C++ doesn't support default-int
+#endif
+#include "strike.h"
+#include <wdbgexts.h>
+#include <dbgeng.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// wdbgexts.h defines StackTrace which interferes with other parts of the
+// system that use the StackTrace identifier
+#ifdef StackTrace
+#undef StackTrace
+#endif
+
+#include "platformspecific.h"
+
+// We need to define the target address type. This has to be used in the
+// functions that read directly from the debuggee address space, vs. using
+// the DAC to read the DAC-ized data structures.
+#include "daccess.h"
+
+#include "gcinfo.h"
+
+// Convert between CLRDATA_ADDRESS and TADDR.
+#define TO_TADDR(cdaddr) ((TADDR)(cdaddr))
+#define TO_CDADDR(taddr) ((CLRDATA_ADDRESS)(LONG_PTR)(taddr))
+
+// We also need a "correction" macro: there are a number of places in the DAC
+// where instead of using the CLRDATA_ADDRESS sign-extension convention
+// we 0-extend (most notably DacpGcHeapDetails)
+#define NEED_DAC_CLRDATA_ADDRESS_CORRECTION 1
+#if NEED_DAC_CLRDATA_ADDRESS_CORRECTION == 1
+ // the macro below "corrects" a CDADDR to always represent the
+ // sign-extended equivalent ULONG64 value of the original TADDR
+ #define UL64_TO_CDA(ul64) (TO_CDADDR(TO_TADDR(ul64)))
+#else
+ #define UL64_TO_CDA(ul64) (ul64)
+#endif // NEED_DAC_CLRDATA_ADDRESS_CORRECTION 1
+
+// The macro below removes the sign extension, returning the
+// equivalent ULONG64 value to the original TADDR. Useful when
+// printing CDA values.
+#define CDA_TO_UL64(cda) ((ULONG64)(TO_TADDR(cda)))
+
+typedef struct _TADDR_RANGE
+{
+ TADDR start;
+ TADDR end;
+} TADDR_RANGE;
+
+typedef struct _TADDR_SEGINFO
+{
+ TADDR segAddr;
+ TADDR start;
+ TADDR end;
+} TADDR_SEGINFO;
+
+#include "util.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Cleanup tasks to be executed when the extension is unloaded
+class OnUnloadTask
+{
+public:
+ FORCEINLINE static void Register(void (*fn)())
+ {
+ // append a new unload task to the head of the list
+ OnUnloadTask *pNew = new OnUnloadTask(fn);
+ pNew->pNext = s_pUnloadTaskList;
+ s_pUnloadTaskList = pNew;
+ }
+
+ static void Run()
+ {
+ // walk the list of UnloadTasks and execute each in turn
+ OnUnloadTask* pCur = s_pUnloadTaskList;
+ while (pCur != NULL)
+ {
+ OnUnloadTask* pNext = pCur->pNext;
+ pCur->OnUnloadFn();
+ delete pCur;
+ pCur = pNext;
+ }
+ s_pUnloadTaskList = NULL;
+ }
+
+private:
+ OnUnloadTask(void(*fn)())
+ : OnUnloadFn(fn)
+ , pNext(NULL)
+ { }
+
+private:
+ void (*OnUnloadFn)();
+ OnUnloadTask* pNext;
+
+ static OnUnloadTask *s_pUnloadTaskList;
+};
+
+#ifndef MINIDUMP
+
+#define EXIT_API ExtRelease
+
+// Safe release and NULL.
+#define EXT_RELEASE(Unk) \
+ ((Unk) != NULL ? ((Unk)->Release(), (Unk) = NULL) : NULL)
+
+extern PDEBUG_CONTROL2 g_ExtControl;
+extern PDEBUG_DATA_SPACES g_ExtData;
+extern PDEBUG_SYMBOLS g_ExtSymbols;
+extern PDEBUG_SYSTEM_OBJECTS g_ExtSystem;
+extern PDEBUG_REGISTERS g_ExtRegisters;
+
+#ifndef FEATURE_PAL
+
+// Global variables initialized by query.
+extern PDEBUG_CLIENT g_ExtClient;
+extern PDEBUG_DATA_SPACES2 g_ExtData2;
+extern PDEBUG_SYMBOLS2 g_ExtSymbols2;
+extern PDEBUG_ADVANCED3 g_ExtAdvanced3;
+
+#else // FEATURE_PAL
+
+extern ILLDBServices* g_ExtServices;
+
+#endif // FEATURE_PAL
+
+HRESULT
+ExtQuery(PDEBUG_CLIENT client);
+
+HRESULT
+ArchQuery(void);
+
+void
+ExtRelease(void);
+
+#ifdef _DEBUG
+extern DWORD_PTR g_filterHint;
+#endif
+
+extern BOOL ControlC;
+
+inline BOOL IsInterrupt()
+{
+ if (!ControlC && g_ExtControl->GetInterrupt() == S_OK)
+ {
+ ExtOut("Command cancelled at the user's request.\n");
+ ControlC = TRUE;
+ }
+
+ return ControlC;
+}
+
+//
+// undef the wdbgexts
+//
+#undef DECLARE_API
+
+#define DECLARE_API(extension) \
+CPPMOD HRESULT CALLBACK extension(PDEBUG_CLIENT client, PCSTR args)
+
+class __ExtensionCleanUp
+{
+public:
+ __ExtensionCleanUp(){}
+ ~__ExtensionCleanUp(){ExtRelease();}
+};
+
+inline void EENotLoadedMessage(HRESULT Status)
+{
+ ExtOut("Failed to find runtime DLL (%s), 0x%08x\n", MAKEDLLNAME_A("coreclr"), Status);
+ ExtOut("Extension commands need it in order to have something to do.\n");
+}
+
+inline void DACMessage(HRESULT Status)
+{
+ ExtOut("Failed to load data access DLL, 0x%08x\n", Status);
+#ifndef FEATURE_PAL
+ ExtOut("Verify that 1) you have a recent build of the debugger (6.2.14 or newer)\n");
+ ExtOut(" 2) the file mscordacwks.dll that matches your version of coreclr.dll is \n");
+ ExtOut(" in the version directory or on the symbol path\n");
+ ExtOut(" 3) or, if you are debugging a dump file, verify that the file \n");
+ ExtOut(" mscordacwks_<arch>_<arch>_<version>.dll is on your symbol path.\n");
+ ExtOut(" 4) you are debugging on supported cross platform architecture as \n");
+ ExtOut(" the dump file. For example, an ARM dump file must be debugged\n");
+ ExtOut(" on an X86 or an ARM machine; an AMD64 dump file must be\n");
+ ExtOut(" debugged on an AMD64 machine.\n");
+ ExtOut("\n");
+ ExtOut("You can also run the debugger command .cordll to control the debugger's\n");
+ ExtOut("load of mscordacwks.dll. .cordll -ve -u -l will do a verbose reload.\n");
+ ExtOut("If that succeeds, the SOS command should work on retry.\n");
+ ExtOut("\n");
+ ExtOut("If you are debugging a minidump, you need to make sure that your executable\n");
+ ExtOut("path is pointing to coreclr.dll as well.\n");
+#else // FEATURE_PAL
+ if (Status == CORDBG_E_MISSING_DEBUGGER_EXPORTS)
+ {
+ ExtOut("You can run the debugger command 'setclrpath' to control the load of %s.\n", MAKEDLLNAME_A("mscordaccore"));
+ ExtOut("If that succeeds, the SOS command should work on retry.\n");
+ }
+ else
+ {
+ ExtOut("Can not load or initialize %s. The target runtime may not be initialized.\n", MAKEDLLNAME_A("mscordaccore"));
+ }
+#endif // FEATURE_PAL
+}
+
+HRESULT CheckEEDll();
+
+#define INIT_API_NOEE() \
+ HRESULT Status; \
+ __ExtensionCleanUp __extensionCleanUp; \
+ if ((Status = ExtQuery(client)) != S_OK) return Status; \
+ if ((Status = ArchQuery()) != S_OK) return Status; \
+ ControlC = FALSE; \
+ g_bDacBroken = TRUE; \
+ g_clrData = NULL; \
+ g_sos = NULL;
+
+#define INIT_API_EE() \
+ if ((Status = CheckEEDll()) != S_OK) \
+ { \
+ EENotLoadedMessage(Status); \
+ return Status; \
+ }
+
+#define INIT_API_NODAC() \
+ INIT_API_NOEE() \
+ INIT_API_EE()
+
+#define INIT_API_DAC() \
+ if ((Status = LoadClrDebugDll()) != S_OK) \
+ { \
+ DACMessage(Status); \
+ return Status; \
+ } \
+ g_bDacBroken = FALSE; \
+ /* If LoadClrDebugDll() succeeded make sure we release g_clrData. */ \
+ /* We may reconsider caching g_clrData in the future */ \
+ ToRelease<IXCLRDataProcess> spIDP(g_clrData); \
+ ToRelease<ISOSDacInterface> spISD(g_sos); \
+ ResetGlobals();
+
+#define INIT_API() \
+ INIT_API_NODAC() \
+ INIT_API_DAC()
+
+// Attempt to initialize DAC and SOS globals, but do not "return" on failure.
+// Instead, mark the failure to initialize the DAC by setting g_bDacBroken to TRUE.
+// This should be used from extension commands that should work OK even when no
+// runtime is loaded in the debuggee, e.g. DumpLog, DumpStack. These extensions
+// and functions they call should test g_bDacBroken before calling any DAC enabled
+// feature.
+#define INIT_API_NO_RET_ON_FAILURE() \
+ INIT_API_NOEE() \
+ if ((Status = CheckEEDll()) != S_OK) \
+ { \
+ ExtOut("Failed to find runtime DLL (%s), 0x%08x\n", MAKEDLLNAME_A("coreclr"), Status); \
+ ExtOut("Some functionality may be impaired\n"); \
+ } \
+ else if ((Status = LoadClrDebugDll()) != S_OK) \
+ { \
+ ExtOut("Failed to load data access DLL (%s), 0x%08x\n", MAKEDLLNAME_A("mscordaccore"), Status); \
+ ExtOut("Some functionality may be impaired\n"); \
+ } \
+ else \
+ { \
+ g_bDacBroken = FALSE; \
+ ResetGlobals(); \
+ } \
+ /* If LoadClrDebugDll() succeeded make sure we release g_clrData. */ \
+ /* We may reconsider caching g_clrData in the future */ \
+ ToRelease<ISOSDacInterface> spISD(g_sos); \
+ ToRelease<IXCLRDataProcess> spIDP(g_clrData);
+
+extern BOOL g_bDacBroken;
+
+#define PAGE_ALIGN64(Va) ((ULONG64)((Va) & ~((ULONG64) ((LONG64) (LONG) PageSize - 1))))
+
+extern ULONG PageSize;
+
+
+//-----------------------------------------------------------------------------------------
+//
+// Target platform abstraction
+//
+//-----------------------------------------------------------------------------------------
+
+// some needed forward declarations
+struct StackTrace_SimpleContext;
+struct GCEncodingInfo;
+struct SOSEHInfo;
+class GCDump;
+
+///
+/// IMachine interface
+///
+/// Note:
+/// The methods accepting target address args take them as size_t==DWORD_PTR==TADDR,
+/// which means this can only provide cross platform support for same-word size
+/// architectures (only ARM on x86 currently). Since this is not exposed outside SOS
+/// and since the some-word-size limitation exists across EE/DAC/SOS this is not an
+/// actual limitation.
+///
+
+class IMachine
+{
+public:
+ // Returns the IMAGE_FILE_MACHINE_*** constant corresponding to the target machine
+ virtual ULONG GetPlatform() const = 0;
+ // Returns the size of the CONTEXT for the target machine
+ virtual ULONG GetContextSize() const = 0;
+
+ // Disassembles a managed method specified by the IPBegin-IPEnd range
+ virtual void Unassembly(
+ TADDR IPBegin,
+ TADDR IPEnd,
+ TADDR IPAskedFor,
+ TADDR GCStressCodeCopy,
+ GCEncodingInfo *pGCEncodingInfo,
+ SOSEHInfo *pEHInfo,
+ BOOL bSuppressLines,
+ BOOL bDisplayOffsets) const = 0;
+
+ // Validates whether retAddr represents a return address by unassembling backwards.
+ // If the instruction before retAddr represents a target-specific call instruction
+ // it attempts to identify the target of the call. If successful it sets *whereCalled
+ // to the call target, otherwise it sets it to 0xffffffff.
+ virtual void IsReturnAddress(
+ TADDR retAddr,
+ TADDR* whereCalled) const = 0;
+
+ // If, while unwinding the stack, "PC" represents a known return address in
+ // KiUserExceptionDispatcher, "stack" is used to retrieve an exception context record
+ // in "cxr", and an exception record in "exr"
+ virtual BOOL GetExceptionContext (
+ TADDR stack,
+ TADDR PC,
+ TADDR *cxrAddr,
+ CROSS_PLATFORM_CONTEXT * cxr,
+ TADDR *exrAddr,
+ PEXCEPTION_RECORD exr) const = 0;
+
+ // Retrieves stack pointer, frame pointer, and instruction pointer from the target context
+ virtual TADDR GetSP(const CROSS_PLATFORM_CONTEXT & ctx) const = 0;
+ virtual TADDR GetBP(const CROSS_PLATFORM_CONTEXT & ctx) const = 0;
+ virtual TADDR GetIP(const CROSS_PLATFORM_CONTEXT & ctx) const = 0;
+
+ // Fills dest's data fields from a target specific context
+ virtual void FillSimpleContext(StackTrace_SimpleContext * dest, LPVOID srcCtx) const = 0;
+ // Fills a target specific context, destCtx, from the idx-th location in a target specific
+ // array of contexts that start at srcCtx
+ virtual void FillTargetContext(LPVOID destCtx, LPVOID srcCtx, int idx = 0) const = 0;
+
+ // Retrieve some target specific output strings
+ virtual LPCSTR GetDumpStackHeading() const = 0;
+ virtual LPCSTR GetDumpStackObjectsHeading() const = 0;
+ virtual LPCSTR GetSPName() const = 0;
+ // Retrieves the non-volatile registers reported to the GC
+ virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const = 0;
+
+ typedef void (*printfFtn)(const char* fmt, ...);
+ // Dumps the GCInfo
+ virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const = 0;
+
+protected:
+ IMachine() {}
+ virtual ~IMachine() {}
+
+private:
+ IMachine(const IMachine& machine); // undefined
+ IMachine & operator=(const IMachine&); // undefined
+}; // class IMachine
+
+
+extern IMachine* g_targetMachine;
+
+
+inline BOOL IsDbgTargetX86() { return g_targetMachine->GetPlatform() == IMAGE_FILE_MACHINE_I386; }
+inline BOOL IsDbgTargetAmd64() { return g_targetMachine->GetPlatform() == IMAGE_FILE_MACHINE_AMD64; }
+inline BOOL IsDbgTargetArm() { return g_targetMachine->GetPlatform() == IMAGE_FILE_MACHINE_ARMNT; }
+inline BOOL IsDbgTargetWin64() { return IsDbgTargetAmd64(); }
+
+/* Returns the instruction pointer for the given CONTEXT. We need this and its family of
+ * functions because certain headers are inconsistantly included on the various platforms,
+ * meaning that we cannot use GetIP and GetSP as defined by CLR.
+ */
+inline CLRDATA_ADDRESS GetIP(const CROSS_PLATFORM_CONTEXT& context)
+{
+ return TO_CDADDR(g_targetMachine->GetIP(context));
+}
+
+/* Returns the stack pointer for the given CONTEXT.
+ */
+inline CLRDATA_ADDRESS GetSP(const CROSS_PLATFORM_CONTEXT& context)
+{
+ return TO_CDADDR(g_targetMachine->GetSP(context));
+}
+
+/* Returns the base/frame pointer for the given CONTEXT.
+ */
+inline CLRDATA_ADDRESS GetBP(const CROSS_PLATFORM_CONTEXT& context)
+{
+ return TO_CDADDR(g_targetMachine->GetBP(context));
+}
+
+
+//-----------------------------------------------------------------------------------------
+//
+// api declaration macros & api access macros
+//
+//-----------------------------------------------------------------------------------------
+
+#ifndef FEATURE_PAL
+
+extern WINDBG_EXTENSION_APIS ExtensionApis;
+#define GetExpression (ExtensionApis.lpGetExpressionRoutine)
+
+extern ULONG TargetMachine;
+extern ULONG g_TargetClass;
+extern ULONG g_VDbgEng;
+
+#else // FEATURE_PAL
+
+#define GetExpression(exp) g_ExtServices->GetExpression(exp)
+
+#endif // FEATURE_PAL
+
+#define CACHE_SIZE DT_OS_PAGE_SIZE
+
+struct ReadVirtualCache
+{
+ BYTE m_cache[CACHE_SIZE];
+ TADDR m_startCache;
+ BOOL m_cacheValid;
+ ULONG m_cacheSize;
+
+ ReadVirtualCache() { Clear(); }
+ HRESULT Read(TADDR Offset, PVOID Buffer, ULONG BufferSize, PULONG lpcbBytesRead);
+ void Clear() { m_cacheValid = FALSE; m_cacheSize = CACHE_SIZE; }
+};
+
+extern ReadVirtualCache *rvCache;
+
+#define MOVE(dst, src) rvCache->Read(TO_TADDR(src), &(dst), sizeof(dst), NULL)
+#define MOVEBLOCK(dst, src, size) rvCache->Read(TO_TADDR(src), &(dst), size, NULL)
+
+#define moveN(dst, src) \
+{ \
+ HRESULT ret = MOVE(dst, src); \
+ if (FAILED(ret)) return ret; \
+}
+
+#define moveBlockN(dst, src, size) \
+{ \
+ HRESULT ret = MOVEBLOCK(dst, src, size); \
+ if (FAILED(ret)) return ret; \
+}
+
+// move cross-process: reads memory from the debuggee into
+// debugger address space and returns in case of error
+#define move_xp(dst, src) \
+{ \
+ HRESULT ret = MOVE(dst, src); \
+ if (FAILED(ret)) return; \
+}
+
+#define moveBlock(dst, src, size) \
+{ \
+ HRESULT ret = MOVEBLOCK(dst, src, size); \
+ if (FAILED(ret)) return; \
+}
+
+#ifdef __cplusplus
+#define CPPMOD extern "C"
+#else
+#define CPPMOD
+#endif
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __exts_h__
+
diff --git a/src/ToolBox/SOS/Strike/gchist.cpp b/src/ToolBox/SOS/Strike/gchist.cpp
new file mode 100644
index 0000000000..ee9d5b4033
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/gchist.cpp
@@ -0,0 +1,636 @@
+// 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.
+
+// ==++==
+//
+
+//
+// ==--==
+/****************************************************************************
+* STRIKE.C *
+* Routines for the NTSD extension - STRIKE *
+* *
+* History: *
+* 09/07/99 Microsoft Created *
+* *
+* *
+\***************************************************************************/
+#include <windows.h>
+#include <winternl.h>
+#include <winver.h>
+#include <wchar.h>
+
+#define NOEXTAPI
+#define KDEXT_64BIT
+#include <wdbgexts.h>
+#undef DECLARE_API
+#undef StackTrace
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stddef.h>
+
+#include "strike.h"
+// We need to define the target address type. This will be used in the
+// functions that read directly from the debuggee address space, vs. using
+// the DAC tgo read the DAC-ized data structures.
+#include "daccess.h"
+//#include "dbgeng.h"
+
+
+#ifndef STRESS_LOG
+#define STRESS_LOG
+#endif // STRESS_LOG
+#define STRESS_LOG_READONLY
+#include "stresslog.h"
+#include <dbghelp.h>
+
+#include "corhdr.h"
+#include "dacprivate.h"
+
+#define CORHANDLE_MASK 0x1
+#define DEFINE_EXT_GLOBALS
+
+#include "util.h"
+
+#ifndef _ASSERTE
+#ifdef _DEBUG
+#define _ASSERTE(expr) \
+ do { if (!(expr) ) { ExtOut(#expr); DebugBreak(); } } while (0)
+#else // _DEBUG
+#define _ASSERTE(expr)
+#endif // _DEBUG else
+#endif // !_ASSERTE
+
+#ifdef _MSC_VER
+#pragma warning(disable:4244) // conversion from 'unsigned int' to 'unsigned short', possible loss of data
+#pragma warning(disable:4189) // local variable is initialized but not referenced
+#endif // _MSC_VER
+
+struct PlugRecord
+{
+ PlugRecord *next;
+
+ size_t PlugStart;
+ size_t PlugEnd;
+ size_t Delta;
+
+ PlugRecord() { ZeroMemory(this,sizeof(PlugRecord)); }
+};
+
+struct PromoteRecord
+{
+ PromoteRecord *next;
+
+ size_t Root;
+ size_t Value;
+ size_t methodTable;
+
+ PromoteRecord() { ZeroMemory(this,sizeof(PromoteRecord)); }
+};
+
+struct RelocRecord
+{
+ RelocRecord *next;
+
+ size_t Root;
+ size_t PrevValue;
+ size_t NewValue;
+ size_t methodTable;
+
+ RelocRecord() { ZeroMemory(this,sizeof(RelocRecord)); }
+};
+
+struct GCRecord
+{
+ ULONG64 GCCount;
+
+ // BOOL IsComplete() { return bFinished && bHaveStart; }
+
+ PlugRecord *PlugList;
+ RelocRecord *RelocList;
+ PromoteRecord *PromoteList;
+
+ void AddPlug(PlugRecord& p) {
+ PlugRecord *pTmp = PlugList;
+ PlugList = new PlugRecord(p);
+ PlugList->next = pTmp;
+ }
+
+ void AddReloc(RelocRecord& r) {
+ RelocRecord *pTmp = RelocList;
+ RelocList = new RelocRecord(r);
+ RelocList->next = pTmp;
+ }
+
+ void AddPromote(PromoteRecord& r) {
+ PromoteRecord *pTmp = PromoteList;
+ PromoteList = new PromoteRecord(r);
+ PromoteList->next = pTmp;
+ }
+
+ UINT PlugCount() {
+ UINT ret = 0;
+ PlugRecord *Iter = PlugList;
+ while (Iter) {
+ Iter = Iter->next;
+ ret++;
+ }
+ return ret;
+ }
+
+ UINT RelocCount() {
+ UINT ret = 0;
+ RelocRecord *Iter = RelocList;
+ while (Iter) {
+ Iter = Iter->next;
+ ret++;
+ }
+ return ret;
+ }
+
+ UINT PromoteCount() {
+ UINT ret = 0;
+ PromoteRecord *Iter = PromoteList;
+ while (Iter) {
+ Iter = Iter->next;
+ ret++;
+ }
+ return ret;
+ }
+
+ void Clear() {
+
+ PlugRecord *pTrav = PlugList;
+ while (pTrav) {
+ PlugRecord *pTmp = pTrav->next;
+ delete pTrav;
+ pTrav = pTmp;
+ }
+
+ RelocRecord *pTravR = RelocList;
+ while (pTravR) {
+ RelocRecord *pTmp = pTravR->next;
+ delete pTravR;
+ pTravR = pTmp;
+ }
+
+ PromoteRecord *pTravP = PromoteList;
+ while (pTravP) {
+ PromoteRecord *pTmp = pTravP->next;
+ delete pTravP;
+ pTravP = pTmp;
+ }
+
+ ZeroMemory(this,sizeof(GCRecord));
+ }
+
+};
+
+#define MAX_GCRECORDS 500
+UINT g_recordCount = 0;
+GCRecord g_records[MAX_GCRECORDS];
+
+void GcHistClear()
+{
+ for (UINT i=0; i < g_recordCount; i++)
+ {
+ g_records[i].Clear();
+ }
+ g_recordCount = 0;
+}
+
+void GcHistAddLog(LPCSTR msg, StressMsg* stressMsg)
+{
+ if (g_recordCount >= MAX_GCRECORDS)
+ {
+ return;
+ }
+
+ if (strcmp(msg, ThreadStressLog::gcPlugMoveMsg()) == 0)
+ {
+ PlugRecord pr;
+ // this is a plug message
+ _ASSERTE(stressMsg->numberOfArgs == 3);
+ pr.PlugStart = (size_t) stressMsg->args[0];
+ pr.PlugEnd = (size_t) stressMsg->args[1];
+ pr.Delta = (size_t) stressMsg->args[2];
+
+ g_records[g_recordCount].AddPlug(pr);
+ }
+ else if (strcmp(msg, ThreadStressLog::gcRootMsg()) == 0)
+ {
+ // this is a root message
+ _ASSERTE(stressMsg->numberOfArgs == 4);
+ RelocRecord rr;
+ rr.Root = (size_t) stressMsg->args[0];
+ rr.PrevValue = (size_t) stressMsg->args[1];
+ rr.NewValue = (size_t) stressMsg->args[2];
+ rr.methodTable = (size_t) stressMsg->args[3];
+ g_records[g_recordCount].AddReloc(rr);
+ }
+ else if (strcmp(msg, ThreadStressLog::gcRootPromoteMsg()) == 0)
+ {
+ // this is a promote message
+ _ASSERTE(stressMsg->numberOfArgs == 3);
+ PromoteRecord pr;
+ pr.Root = (size_t) stressMsg->args[0];
+ pr.Value = (size_t) stressMsg->args[1];
+ pr.methodTable = (size_t) stressMsg->args[2];
+ g_records[g_recordCount].AddPromote(pr);
+ }
+ else if (strcmp(msg, ThreadStressLog::gcStartMsg()) == 0)
+ {
+ // Gc start!
+ _ASSERTE(stressMsg->numberOfArgs == 3);
+ ULONG64 gc_count = (ULONG64) stressMsg->args[0];
+ g_records[g_recordCount].GCCount = gc_count;
+ g_recordCount++;
+ }
+ else if (strcmp(msg, ThreadStressLog::gcEndMsg()) == 0)
+ {
+ // Gc end!
+ // ULONG64 gc_count = (ULONG64) stressMsg->data;
+ // ExtOut ("ENDGC %d\n", gc_count);
+ }
+}
+
+DECLARE_API(HistStats)
+{
+ INIT_API();
+
+ ExtOut ("%8s %8s %8s\n",
+ "GCCount", "Promotes", "Relocs");
+ ExtOut ("-----------------------------------\n");
+
+ // Just traverse the data structure, printing basic stats
+ for (UINT i=0; i < g_recordCount; i++)
+ {
+ UINT PromoteCount = g_records[i].PromoteCount();
+ UINT RelocCount = g_records[i].RelocCount();
+ UINT GCCount = (UINT) g_records[i].GCCount;
+
+ ExtOut ("%8d %8d %8d\n",
+ GCCount,
+ PromoteCount,
+ RelocCount);
+ }
+
+ BOOL bErrorFound = FALSE;
+
+ // Check for duplicate Reloc or Promote messages within one gc.
+ // Method is very inefficient, improve it later.
+ for (UINT i=0; i < g_recordCount; i++)
+ {
+ { // Promotes
+ PromoteRecord *Iter = g_records[i].PromoteList;
+ UINT GCCount = (UINT) g_records[i].GCCount;
+ while (Iter)
+ {
+ PromoteRecord *innerIter = Iter->next;
+ while (innerIter)
+ {
+ if (Iter->Root == innerIter->Root)
+ {
+ ExtOut ("Root %p promoted multiple times in gc %d\n",
+ SOS_PTR(Iter->Root),
+ GCCount);
+ bErrorFound = TRUE;
+ }
+ innerIter = innerIter->next;
+ }
+
+ Iter = Iter->next;
+ }
+ }
+
+ { // Relocates
+ RelocRecord *Iter = g_records[i].RelocList;
+ UINT GCCount = (UINT) g_records[i].GCCount;
+ while (Iter)
+ {
+ RelocRecord *innerIter = Iter->next;
+ while (innerIter)
+ {
+ if (Iter->Root == innerIter->Root)
+ {
+ ExtOut ("Root %p relocated multiple times in gc %d\n",
+ SOS_PTR(Iter->Root),
+ GCCount);
+ bErrorFound = TRUE;
+ }
+ innerIter = innerIter->next;
+ }
+
+ Iter = Iter->next;
+ }
+ }
+ }
+
+ if (!bErrorFound)
+ {
+ ExtOut ("No duplicate promote or relocate messages found in the log.\n");
+ }
+
+ return Status;
+}
+
+DECLARE_API(HistRoot)
+{
+ INIT_API();
+ size_t nArg;
+
+ StringHolder rootstr;
+ CMDValue arg[] =
+ {
+ // vptr, type
+ {&rootstr.data, COSTRING},
+ };
+
+ if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ return Status;
+
+ if (nArg != 1)
+ {
+ ExtOut ("!Root <valid object pointer>\n");
+ return Status;
+ }
+
+ size_t Root = (size_t) GetExpression(rootstr.data);
+
+ ExtOut ("%8s %" POINTERSIZE "s %" POINTERSIZE "s %9s %20s\n",
+ "GCCount", "Value", "MT", "Promoted?", "Notes");
+ ExtOut ("---------------------------------------------------------\n");
+
+ bool bBoringPeople = false;
+
+ // Just traverse the data structure, printing basic stats
+ for (UINT i=0; i < g_recordCount; i++)
+ {
+ UINT GCCount = (UINT) g_records[i].GCCount;
+
+ // Find promotion records...there should only be one.
+ PromoteRecord *pPtr = g_records[i].PromoteList;
+ PromoteRecord *pPromoteRec = NULL;
+ bool bPromotedMoreThanOnce = false;
+ while(pPtr)
+ {
+ if (pPtr->Root == Root)
+ {
+ if (pPromoteRec)
+ {
+ bPromotedMoreThanOnce = true;
+ }
+ else
+ {
+ pPromoteRec = pPtr;
+ }
+ }
+ pPtr = pPtr->next;
+ }
+
+ RelocRecord *pReloc = g_records[i].RelocList;
+ RelocRecord *pRelocRec = NULL;
+ bool bRelocatedMoreThanOnce = false;
+ while(pReloc)
+ {
+ if (pReloc->Root == Root)
+ {
+ if (pRelocRec)
+ {
+ bRelocatedMoreThanOnce = true;
+ }
+ else
+ {
+ pRelocRec = pReloc;
+ }
+ }
+ pReloc = pReloc->next;
+ }
+
+ // Validate the records found for this root.
+ if (pRelocRec != NULL)
+ {
+ bBoringPeople = false;
+
+ ExtOut ("%8d %p %p %9s ", GCCount,
+ SOS_PTR(pRelocRec->NewValue),
+ SOS_PTR(pRelocRec->methodTable),
+ pPromoteRec ? "yes" : "no");
+ if (pPromoteRec != NULL)
+ {
+ // There should be similarities between the promote and reloc record
+ if (pPromoteRec->Value != pRelocRec->PrevValue ||
+ pPromoteRec->methodTable != pRelocRec->methodTable)
+ {
+ ExtOut ("promote/reloc records in error ");
+ }
+
+ if (bPromotedMoreThanOnce || bRelocatedMoreThanOnce)
+ {
+ ExtOut ("Duplicate promote/relocs");
+ }
+ }
+ ExtOut ("\n");
+ }
+ else if (pPromoteRec)
+ {
+ ExtOut ("Error: There is a promote record for root %p, but no relocation record\n",
+ (ULONG64) pPromoteRec->Root);
+ }
+ else
+ {
+ if (!bBoringPeople)
+ {
+ ExtOut ("...\n");
+ bBoringPeople = true;
+ }
+ }
+ }
+ return Status;
+}
+
+DECLARE_API(HistObjFind)
+{
+ INIT_API();
+ size_t nArg;
+
+ StringHolder objstr;
+ CMDValue arg[] =
+ {
+ // vptr, type
+ {&objstr.data, COSTRING},
+ };
+
+ if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ return Status;
+
+ if (nArg != 1)
+ {
+ ExtOut ("!ObjSearch <valid object pointer>\n");
+ return Status;
+ }
+
+ size_t object = (size_t) GetExpression(objstr.data);
+
+ ExtOut ("%8s %" POINTERSIZE "s %40s\n",
+ "GCCount", "Object", "Message");
+ ExtOut ("---------------------------------------------------------\n");
+
+ size_t curAddress = object;
+ bool bBoringPeople = false;
+
+ // Just traverse the data structure, printing basic stats
+ for (UINT i=0; i < g_recordCount; i++)
+ {
+ if (curAddress == 0)
+ {
+ break;
+ }
+
+ UINT GCCount = (UINT) g_records[i].GCCount;
+
+ PromoteRecord *pPtr = g_records[i].PromoteList;
+ while(pPtr)
+ {
+ if (pPtr->Value == curAddress)
+ {
+ bBoringPeople = false;
+ ExtOut ("%8d %p ", GCCount, SOS_PTR(curAddress));
+ ExtOut ("Promotion for root %p (MT = %p)\n",
+ SOS_PTR(pPtr->Root),
+ SOS_PTR(pPtr->methodTable));
+ }
+ pPtr = pPtr->next;
+ }
+
+ RelocRecord *pReloc = g_records[i].RelocList;
+ while(pReloc)
+ {
+ if (pReloc->NewValue == curAddress ||
+ pReloc->PrevValue == curAddress)
+ {
+ bBoringPeople = false;
+ ExtOut ("%8d %p ", GCCount, SOS_PTR(curAddress));
+ ExtOut ("Relocation %s for root %p\n",
+ (pReloc->NewValue == curAddress) ? "NEWVALUE" : "PREVVALUE",
+ SOS_PTR(pReloc->Root));
+ }
+ pReloc = pReloc->next;
+ }
+
+ if (!bBoringPeople)
+ {
+ ExtOut ("...\n");
+ bBoringPeople = true;
+ }
+
+ }
+ return Status;
+}
+
+DECLARE_API(HistObj)
+{
+ INIT_API();
+ size_t nArg;
+
+ StringHolder objstr;
+ CMDValue arg[] =
+ {
+ // vptr, type
+ {&objstr.data, COSTRING},
+ };
+
+ if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ return Status;
+
+ if (nArg != 1)
+ {
+ ExtOut ("!object <valid object pointer>\n");
+ return Status;
+ }
+
+ size_t object = (size_t) GetExpression(objstr.data);
+
+ ExtOut ("%8s %" POINTERSIZE "s %40s\n",
+ "GCCount", "Object", "Roots");
+ ExtOut ("---------------------------------------------------------\n");
+
+ size_t curAddress = object;
+
+ // Just traverse the data structure, printing basic stats
+ for (UINT i=0; i < g_recordCount; i++)
+ {
+ if (curAddress == 0)
+ {
+ break;
+ }
+
+ UINT GCCount = (UINT) g_records[i].GCCount;
+
+ ExtOut ("%8d %p ", GCCount, SOS_PTR(curAddress));
+
+ RelocRecord *pReloc = g_records[i].RelocList;
+ size_t candidateCurAddress = curAddress;
+ bool bFirstReloc = true;
+ while(pReloc)
+ {
+ if (pReloc->NewValue == curAddress)
+ {
+ ExtOut ("%p, ", SOS_PTR(pReloc->Root));
+ if (bFirstReloc)
+ {
+ candidateCurAddress = pReloc->PrevValue;
+ bFirstReloc = false;
+ }
+ else if (candidateCurAddress != pReloc->PrevValue)
+ {
+ ExtOut ("differing reloc values for this object!\n");
+ }
+ }
+ pReloc = pReloc->next;
+ }
+
+ ExtOut ("\n");
+ curAddress = candidateCurAddress;
+ }
+ return Status;
+}
+
+DECLARE_API(HistInit)
+{
+ INIT_API();
+
+ GcHistClear();
+
+ CLRDATA_ADDRESS stressLogAddr = 0;
+ if (g_sos->GetStressLogAddress(&stressLogAddr) != S_OK)
+ {
+ ExtOut("Unable to find stress log via DAC\n");
+ return E_FAIL;
+ }
+
+ ExtOut ("Attempting to read Stress log\n");
+
+ Status = StressLog::Dump(stressLogAddr, NULL, g_ExtData);
+ if (Status == S_OK)
+ ExtOut("SUCCESS: GCHist structures initialized\n");
+ else if (Status == S_FALSE)
+ ExtOut("No Stress log in the image, GCHist commands unavailable\n");
+ else
+ ExtOut("FAILURE: Stress log unreadable\n");
+
+ return Status;
+}
+
+DECLARE_API(HistClear)
+{
+ INIT_API();
+ GcHistClear();
+ ExtOut("Completed successfully.\n");
+ return Status;
+}
+
diff --git a/src/ToolBox/SOS/Strike/gcroot.cpp b/src/ToolBox/SOS/Strike/gcroot.cpp
new file mode 100644
index 0000000000..f68b935e21
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/gcroot.cpp
@@ -0,0 +1,2503 @@
+// 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.
+
+// ==++==
+//
+
+//
+// ==--==
+
+/*
+ * This file defines the support classes that allow us to operate on the object graph of the process that SOS
+ * is analyzing.
+ *
+ * The GCRoot algorithm is based on three simple principles:
+ * 1. Only consider an object once. When we inspect an object, read its references and don't ever touch
+ * it again. This ensures that our upper bound on the amount of time we spend walking the object
+ * graph very quickly reaches resolution. The objects we've already inspected (and thus won't inspect
+ * again) is tracked by the mConsidered variable.
+ * 2. Be extremely careful about reads from the target process. We use a linear cache for reading from
+ * object data. We also cache everything about the method tables we read out of, as well as caching
+ * the GCDesc which is required to walk the object's references.
+ * 3. Use O(1) data structures for anything perf-critical. Almost all of the data structures we use to
+ * keep track of data have very fast lookups. For example, to keep track of the objects we've considered
+ * we use a unordered_set. Similarly to keep track of MethodTable data we use a unordered_map to track the
+ * mt -> mtinfo mapping.
+ */
+
+#include "sos.h"
+#include "disasm.h"
+
+#ifdef _ASSERTE
+#undef _ASSERTE
+#endif
+
+#define _ASSERTE(a) {;}
+
+#include "gcdesc.h"
+
+#include "safemath.h"
+
+
+#ifdef _ASSERTE
+#undef _ASSERTE
+#endif
+
+#ifndef _ASSERTE
+#ifdef _DEBUG
+#define _ASSERTE(expr) \
+ do { if (!(expr) ) { ExtErr("_ASSERTE fired:\n\t%s\n", #expr); if (IsDebuggerPresent()) DebugBreak(); } } while (0)
+#else
+#define _ASSERTE(x)
+#endif
+#endif // ASSERTE
+
+inline size_t ALIGN_DOWN( size_t val, size_t alignment )
+{
+ // alignment must be a power of 2 for this implementation to work (need modulo otherwise)
+ _ASSERTE( 0 == (alignment & (alignment - 1)) );
+ size_t result = val & ~(alignment - 1);
+ return result;
+}
+
+inline void* ALIGN_DOWN( void* val, size_t alignment )
+{
+ return (void*) ALIGN_DOWN( (size_t)val, alignment );
+}
+
+LinearReadCache::LinearReadCache(ULONG pageSize)
+ : mCurrPageStart(0), mPageSize(pageSize), mCurrPageSize(0), mPage(0)
+{
+ mPage = new BYTE[pageSize];
+ ClearStats();
+}
+
+LinearReadCache::~LinearReadCache()
+{
+ if (mPage)
+ delete [] mPage;
+}
+
+bool LinearReadCache::MoveToPage(TADDR addr, unsigned int size)
+{
+ if (size > mPageSize)
+ size = mPageSize;
+
+ mCurrPageStart = addr;
+ HRESULT hr = g_ExtData->ReadVirtual(mCurrPageStart, mPage, size, &mCurrPageSize);
+
+ if (hr != S_OK)
+ {
+ mCurrPageStart = 0;
+ mCurrPageSize = 0;
+ return false;
+ }
+
+#ifdef _DEBUG
+ mMisses++;
+#endif
+ return true;
+}
+
+
+static const char *NameForHandle(unsigned int type)
+{
+ switch (type)
+ {
+ case 0:
+ return "weak short";
+ case 1:
+ return "weak long";
+ case 2:
+ return "strong";
+ case 3:
+ return "pinned";
+ case 5:
+ return "ref counted";
+ case 6:
+ return "dependent";
+ case 7:
+ return "async pinned";
+ case 8:
+ return "sized ref";
+ }
+
+ return "unknown";
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// GCRoot functions to cleanup data.
+///////////////////////////////////////////////////////////////////////////////
+void GCRootImpl::ClearSizeData()
+{
+ mConsidered.clear();
+ mSizes.clear();
+}
+
+void GCRootImpl::ClearAll()
+{
+ ClearNodes();
+
+ {
+ std::unordered_map<TADDR, MTInfo*>::iterator itr;
+ for (itr = mMTs.begin(); itr != mMTs.end(); ++itr)
+ delete itr->second;
+ }
+
+ {
+ std::unordered_map<TADDR, RootNode*>::iterator itr;
+ for (itr = mTargets.begin(); itr != mTargets.end(); ++itr)
+ delete itr->second;
+ }
+
+ mMTs.clear();
+ mTargets.clear();
+ mConsidered.clear();
+ mSizes.clear();
+ mDependentHandleMap.clear();
+ mCache.ClearStats();
+
+ mAll = false;
+ mSize = false;
+}
+
+void GCRootImpl::ClearNodes()
+{
+ std::list<RootNode*>::iterator itr;
+
+ for (itr = mCleanupList.begin(); itr != mCleanupList.end(); ++itr)
+ delete *itr;
+
+ mCleanupList.clear();
+ mRootNewList.clear();
+}
+
+GCRootImpl::RootNode *GCRootImpl::NewNode(TADDR obj, MTInfo *mtInfo, bool fromDependent)
+{
+ // We need to create/destroy a TON of nodes during execution of GCRoot functions.
+ // To avoid heap fragmentation (and since it's faster), we don't actually new/delete
+ // nodes unless we have to. Instead we keep a stl list with free nodes to use.
+ RootNode *toReturn = NULL;
+
+ if (mRootNewList.size())
+ {
+ toReturn = mRootNewList.back();
+ mRootNewList.pop_back();
+ }
+ else
+ {
+ toReturn = new RootNode();
+ mCleanupList.push_back(toReturn);
+ }
+
+ toReturn->Object = obj;
+ toReturn->MTInfo = mtInfo;
+ toReturn->FromDependentHandle = fromDependent;
+ return toReturn;
+}
+
+void GCRootImpl::DeleteNode(RootNode *node)
+{
+ // Add node to the "new list".
+ node->Clear();
+ mRootNewList.push_back(node);
+}
+
+void GCRootImpl::GetDependentHandleMap(std::unordered_map<TADDR, std::list<TADDR>> &map)
+{
+ unsigned int type = HNDTYPE_DEPENDENT;
+ ToRelease<ISOSHandleEnum> handles;
+
+ HRESULT hr = g_sos->GetHandleEnumForTypes(&type, 1, &handles);
+
+ if (FAILED(hr))
+ {
+ ExtOut("Failed to walk dependent handles. GCRoot may miss paths.\n");
+ return;
+ }
+
+ SOSHandleData data[4];
+ unsigned int fetched = 0;
+
+ do
+ {
+ hr = handles->Next(_countof(data), data, &fetched);
+
+ if (FAILED(hr))
+ {
+ ExtOut("Error walking dependent handles. GCRoot may miss paths.\n");
+ return;
+ }
+
+ for (unsigned int i = 0; i < fetched; ++i)
+ {
+ if (data[i].Secondary != 0)
+ {
+ TADDR obj = 0;
+ TADDR target = TO_TADDR(data[i].Secondary);
+
+ MOVE(obj, TO_TADDR(data[i].Handle));
+
+ map[obj].push_back(target);
+ }
+ }
+ } while (fetched == _countof(data));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Public functions.
+///////////////////////////////////////////////////////////////////////////////
+int GCRootImpl::PrintRootsForObject(TADDR target, bool all, bool noStacks)
+{
+ ClearAll();
+ GetDependentHandleMap(mDependentHandleMap);
+
+ mAll = all;
+
+ // Add "target" to the mTargets list.
+ TADDR mt = ReadPointerCached(target);
+ RootNode *node = NewNode(target, GetMTInfo(mt));
+ mTargets[target] = node;
+
+ // Look for roots on the HandleTable, FQ, and all threads.
+ int count = 0;
+
+ if (!noStacks)
+ count += PrintRootsOnAllThreads();
+
+ count += PrintRootsOnHandleTable();
+ count += PrintRootsOnFQ();
+
+ if(count == 0)
+ {
+ count += PrintRootsOnFQ(true);
+ if(count)
+ {
+ ExtOut("Warning: These roots are from finalizable objects that are not yet ready for finalization.\n");
+ ExtOut("This is to handle the case where objects re-register themselves for finalization.\n");
+ ExtOut("These roots may be false positives.\n");
+ }
+ }
+
+ mCache.PrintStats(__FUNCTION__);
+ return count;
+}
+
+
+bool GCRootImpl::PrintPathToObject(TADDR root, TADDR target)
+{
+ ClearAll();
+ GetDependentHandleMap(mDependentHandleMap);
+
+ // Add "target" to the mTargets list.
+ TADDR mt = ReadPointerCached(target);
+ RootNode *node = NewNode(target, GetMTInfo(mt));
+ mTargets[target] = node;
+
+ // Check to see if the root reaches the target.
+ RootNode *path = FindPathToTarget(root);
+ if (path)
+ {
+ ExtOut("%p %S\n", SOS_PTR(path->Object), path->GetTypeName());
+ path = path->Next;
+
+ while (path)
+ {
+ ExtOut(" -> %p %S%s\n",SOS_PTR(path->Object), path->GetTypeName(), path->FromDependentHandle ? " (dependent handle)" : "");
+ path = path->Next;
+ }
+
+ mCache.PrintStats(__FUNCTION__);
+ return true;
+ }
+
+ mCache.PrintStats(__FUNCTION__);
+ return false;
+}
+
+size_t GCRootImpl::ObjSize(TADDR root)
+{
+ // Calculates the size of the closure of objects kept alive by root.
+ ClearAll();
+ GetDependentHandleMap(mDependentHandleMap);
+
+ // mSize tells GCRootImpl to build the "mSizes" table with the total size
+ // each object roots.
+ mSize = true;
+
+ // Note that we are calling the same method as we would to locate the rooting
+ // chain for an object, but we haven't added any items to mTargets. This means
+ // the algorithm will scan all objects and never terminate until it has walked
+ // all objects in the closure.
+ FindPathToTarget(root);
+
+ mCache.PrintStats(__FUNCTION__);
+ return mSizes[root];
+}
+
+void GCRootImpl::ObjSize()
+{
+ ClearAll();
+ GetDependentHandleMap(mDependentHandleMap);
+ mSize = true;
+
+ // Walks all roots in the process, and prints out the object size for each one.
+ PrintRootsOnAllThreads();
+ PrintRootsOnHandleTable();
+ PrintRootsOnFQ();
+
+ mCache.PrintStats(__FUNCTION__);
+}
+
+
+const std::unordered_set<TADDR> &GCRootImpl::GetLiveObjects(bool excludeFQ)
+{
+ ClearAll();
+ GetDependentHandleMap(mDependentHandleMap);
+
+ // Walk each root in the process without setting a target. This has the effect of
+ // causing us to walk every object in the process, adding them to mConsidered as we
+ // go.
+ PrintRootsOnAllThreads();
+ PrintRootsOnHandleTable();
+
+ if (!excludeFQ)
+ PrintRootsOnFQ();
+
+ mCache.PrintStats(__FUNCTION__);
+ return mConsidered;
+}
+
+int GCRootImpl::FindRoots(int gen, TADDR target)
+{
+ ClearAll();
+ GetDependentHandleMap(mDependentHandleMap);
+
+ if (gen == -1 || ((UINT)gen) == GetMaxGeneration())
+ {
+ // If this is a gen 2 !FindRoots, just do a regular !GCRoot
+ return PrintRootsForObject(target, false, false);
+ }
+ else
+ {
+ // Otherwise walk all roots for only the given generation.
+ int count = PrintRootsInOlderGen();
+ count += PrintRootsOnHandleTable(gen);
+ count += PrintRootsOnFQ();
+ return count;
+ }
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// GCRoot Methods for printing out results.
+///////////////////////////////////////////////////////////////////////////////
+void GCRootImpl::ReportSizeInfo(const SOSHandleData &handle, TADDR obj)
+{
+ // Print size for a handle (!objsize)
+ TADDR mt = ReadPointer(obj);
+ MTInfo *mtInfo = GetMTInfo(mt);
+
+ const WCHAR *type = mtInfo ? mtInfo->GetTypeName() : W("unknown type");
+
+ size_t size = mSizes[obj];
+ ExtOut("Handle (%s): %p -> %p: %d (0x%x) bytes (%S)\n", NameForHandle(handle.Type), SOS_PTR(handle.Handle),
+ SOS_PTR(obj), size, size, type);
+}
+
+
+void GCRootImpl::ReportSizeInfo(DWORD thread, const SOSStackRefData &stackRef, TADDR obj)
+{
+ // Print size for a stack root (!objsize)
+ WString frame;
+ if (stackRef.SourceType == SOS_StackSourceIP)
+ frame = MethodNameFromIP(stackRef.Source);
+ else
+ frame = GetFrameFromAddress(TO_TADDR(stackRef.Source));
+
+ WString regOutput = BuildRegisterOutput(stackRef);
+
+ TADDR mt = ReadPointer(obj);
+ MTInfo *mtInfo = GetMTInfo(mt);
+ const WCHAR *type = mtInfo ? mtInfo->GetTypeName() : W("unknown type");
+
+ size_t size = mSizes[obj];
+ ExtOut("Thread %x (%S): %S: %d (0x%x) bytes (%S)\n", thread, frame.c_str(), regOutput.c_str(), size, size, type);
+}
+
+void GCRootImpl::ReportOneHandlePath(const SOSHandleData &handle, RootNode *path, bool printHeader)
+{
+ if (printHeader)
+ ExtOut("HandleTable:\n");
+
+ ExtOut(" %p (%s handle)\n", SOS_PTR(handle.Handle), NameForHandle(handle.Type));
+ while (path)
+ {
+ ExtOut(" -> %p %S%s\n", SOS_PTR(path->Object), path->GetTypeName(), path->FromDependentHandle ? " (dependent handle)" : "");
+ path = path->Next;
+ }
+
+ ExtOut("\n");
+}
+
+void GCRootImpl::ReportOnePath(DWORD thread, const SOSStackRefData &stackRef, RootNode *path, bool printThread, bool printFrame)
+{
+ if (printThread)
+ ExtOut("Thread %x:\n", thread);
+
+ if (printFrame)
+ {
+ if (stackRef.SourceType == SOS_StackSourceIP)
+ {
+ WString methodName = MethodNameFromIP(stackRef.Source);
+ ExtOut(" %p %p %S\n", SOS_PTR(stackRef.StackPointer), SOS_PTR(stackRef.Source), methodName.c_str());
+ }
+ else
+ {
+ WString frameName = GetFrameFromAddress(TO_TADDR(stackRef.Source));
+ ExtOut(" %p %S\n", SOS_PTR(stackRef.Source), frameName.c_str());
+ }
+ }
+
+ WString regOutput = BuildRegisterOutput(stackRef, false);
+ ExtOut(" %S\n", regOutput.c_str());
+
+ while (path)
+ {
+ ExtOut(" -> %p %S%s\n", SOS_PTR(path->Object), path->GetTypeName(), path->FromDependentHandle ? " (dependent handle)" : "");
+ path = path->Next;
+ }
+
+ ExtOut("\n");
+}
+
+void GCRootImpl::ReportOneFQEntry(TADDR root, RootNode *path, bool printHeader)
+{
+ if (printHeader)
+ ExtOut("Finalizer Queue:\n");
+
+ ExtOut(" %p\n", SOS_PTR(root));
+ while (path)
+ {
+ ExtOut(" -> %p %S%s\n", SOS_PTR(path->Object), path->GetTypeName(), path->FromDependentHandle ? " (dependent handle)" : "");
+ path = path->Next;
+ }
+
+ ExtOut("\n");
+}
+
+void GCRootImpl::ReportOlderGenEntry(TADDR root, RootNode *path, bool printHeader)
+{
+ if (printHeader)
+ ExtOut("Older Generation:\n");
+
+ ExtOut(" %p\n", SOS_PTR(root));
+ while (path)
+ {
+ ExtOut(" -> %p %S%s\n", SOS_PTR(path->Object), path->GetTypeName(), path->FromDependentHandle ? " (dependent handle)" : "");
+ path = path->Next;
+ }
+
+ ExtOut("\n");
+}
+
+//////////////////////////////////////////////////////
+int GCRootImpl::PrintRootsInOlderGen()
+{
+ // Use a different linear read cache here instead of polluting the object read cache.
+ LinearReadCache cache(512);
+
+ if (!IsServerBuild())
+ {
+ DacpGcHeapAnalyzeData analyzeData;
+ if (analyzeData.Request(g_sos) != S_OK)
+ {
+ ExtErr("Error requesting gc heap analyze data\n");
+ return 0;
+ }
+
+ if (!analyzeData.heap_analyze_success)
+ {
+ ExtOut("Failed to gather needed data, possibly due to memory contraints in the debuggee.\n");
+ ExtOut("To try again re-issue the !FindRoots -gen <N> command.\n");
+ return 0;
+ }
+
+ ExtDbgOut("internal_root_array = %#p\n", SOS_PTR(analyzeData.internal_root_array));
+ ExtDbgOut("internal_root_array_index = %#p\n", SOS_PTR(analyzeData.internal_root_array_index));
+
+ TADDR start = TO_TADDR(analyzeData.internal_root_array);
+ TADDR stop = TO_TADDR(analyzeData.internal_root_array + sizeof(TADDR) * (size_t)analyzeData.internal_root_array_index);
+
+ return PrintRootsInRange(cache, start, stop, &GCRootImpl::ReportOlderGenEntry, true);
+ }
+ else
+ {
+ int total = 0;
+ DWORD dwAllocSize;
+ DWORD dwNHeaps = GetGcHeapCount();
+ if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
+ {
+ ExtErr("Failed to get GCHeaps: integer overflow\n");
+ return 0;
+ }
+
+ CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
+ if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
+ {
+ ExtErr("Failed to get GCHeaps\n");
+ return 0;
+ }
+
+ for (UINT n = 0; n < dwNHeaps; n ++)
+ {
+ DacpGcHeapAnalyzeData analyzeData;
+ if (analyzeData.Request(g_sos, heapAddrs[n]) != S_OK)
+ {
+ ExtErr("Error requesting gc heap analyze data for heap %p\n", SOS_PTR(heapAddrs[n]));
+ continue;
+ }
+
+ if (!analyzeData.heap_analyze_success)
+ {
+ ExtOut("Failed to gather needed data, possibly due to memory contraints in the debuggee.\n");
+ ExtOut("To try again re-issue the !FindRoots -gen <N> command.\n");
+ continue;
+ }
+
+ ExtDbgOut("internal_root_array = %#p\n", SOS_PTR(analyzeData.internal_root_array));
+ ExtDbgOut("internal_root_array_index = %#p\n", SOS_PTR(analyzeData.internal_root_array_index));
+
+ TADDR start = TO_TADDR(analyzeData.internal_root_array);
+ TADDR stop = TO_TADDR(analyzeData.internal_root_array + sizeof(TADDR) * (size_t)analyzeData.internal_root_array_index);
+
+ total += PrintRootsInRange(cache, start, stop, &GCRootImpl::ReportOlderGenEntry, total == 0);
+ }
+
+ return total;
+ }
+}
+
+
+int GCRootImpl::PrintRootsOnFQ(bool notReadyForFinalization)
+{
+ // Here are objects kept alive by objects in the finalizer queue.
+ DacpGcHeapDetails heapDetails;
+
+ // Use a different linear read cache here instead of polluting the object read cache.
+ LinearReadCache cache(512);
+
+ if (!IsServerBuild())
+ {
+ if (heapDetails.Request(g_sos) != S_OK)
+ {
+ ExtErr("Error requesting heap data.\n");
+ return 0;
+ }
+
+ // If we include objects that are not ready for finalization, we may report
+ // false positives. False positives occur if the object is not ready for finalization
+ // and does not re-register itself for finalization inside the finalizer.
+ TADDR start = 0;
+ TADDR stop = 0;
+ if(notReadyForFinalization)
+ {
+ start = TO_TADDR(SegQueue(heapDetails, gen_segment(GetMaxGeneration())));
+ stop = TO_TADDR(SegQueueLimit(heapDetails, CriticalFinalizerListSeg));
+ }
+ else
+ {
+ start = TO_TADDR(SegQueue(heapDetails, CriticalFinalizerListSeg));
+ stop = TO_TADDR(SegQueue(heapDetails, FinalizerListSeg));
+ }
+
+ return PrintRootsInRange(cache, start, stop, &GCRootImpl::ReportOneFQEntry, true);
+ }
+ else
+ {
+ DWORD dwAllocSize;
+ DWORD dwNHeaps = GetGcHeapCount();
+ if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
+ {
+ ExtErr("Failed to get GCHeaps: integer overflow\n");
+ return 0;
+ }
+
+ CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
+ if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
+ {
+ ExtErr("Error requesting heap data.\n");
+ return 0;
+ }
+
+ int total = 0;
+ for (UINT n = 0; n < dwNHeaps; n ++)
+ {
+ if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
+ {
+ ExtErr("Error requesting heap data for heap %d.\n", n);
+ continue;
+ }
+
+ // If we include objects that are not ready for finalization, we may report
+ // false positives. False positives occur if the object is not ready for finalization
+ // and does not re-register itself for finalization inside the finalizer.
+ TADDR start = 0;
+ TADDR stop = 0;
+ if(notReadyForFinalization)
+ {
+ start = TO_TADDR(SegQueue(heapDetails, gen_segment(GetMaxGeneration())));
+ stop = TO_TADDR(SegQueueLimit(heapDetails, CriticalFinalizerListSeg));
+ }
+ else
+ {
+ start = TO_TADDR(SegQueue(heapDetails, CriticalFinalizerListSeg));
+ stop = TO_TADDR(SegQueueLimit(heapDetails, FinalizerListSeg));
+ }
+
+ total += PrintRootsInRange(cache, start, stop, &GCRootImpl::ReportOneFQEntry, total == 0);
+ }
+
+ return total;
+ }
+}
+
+int GCRootImpl::PrintRootsInRange(LinearReadCache &cache, TADDR start, TADDR stop, ReportCallback func, bool printHeader)
+{
+ int total = 0;
+
+ // Walk the range [start, stop) and consider each pointer in the range as a root.
+ while (start < stop)
+ {
+ if (IsInterrupt())
+ return total;
+
+ // Use the cache parameter here instead of mCache. If you use mCache it will be reset
+ // when calling into FindPathToTarget.
+ TADDR root = 0;
+ bool res = cache.Read(start, &root, true);
+
+ if (res && root)
+ {
+ RootNode *path = FindPathToTarget(root);
+ if (path)
+ {
+ func(root, path, printHeader);
+ total++;
+ printHeader = false;
+ }
+ }
+
+ start += sizeof(TADDR);
+ }
+
+ return total;
+}
+
+int GCRootImpl::PrintRootsOnAllThreads()
+{
+ ArrayHolder<DWORD_PTR> threadList = NULL;
+ int numThreads = 0;
+
+ // GetThreadList calls ReportOOM so we don't need to do that here.
+ HRESULT hr = GetThreadList(&threadList, &numThreads);
+ if (FAILED(hr) || !threadList)
+ return 0;
+
+ // Walk each thread and process the roots on it.
+ int total = 0;
+ DacpThreadData vThread;
+ for (int i = 0; i < numThreads; i++)
+ {
+ if (IsInterrupt())
+ return total;
+
+ if (FAILED(vThread.Request(g_sos, threadList[i])))
+ continue;
+
+ if (vThread.osThreadId)
+ total += PrintRootsOnThread(vThread.osThreadId);
+ }
+
+ return total;
+}
+
+int GCRootImpl::PrintRootsOnThread(DWORD osThreadId)
+{
+ // Grab all object rootson the thread.
+ unsigned int refCount = 0;
+ ArrayHolder<SOSStackRefData> refs = NULL;
+
+ int total = 0;
+ bool first = true;
+ if (FAILED(::GetGCRefs(osThreadId, &refs, &refCount, NULL, NULL)))
+ {
+ ExtOut("Failed to walk thread %x\n", osThreadId);
+ return total;
+ }
+
+ // Walk each non-null root, find if it contains a path to the target,
+ // and if so print it out.
+ CLRDATA_ADDRESS prevSource = 0, prevSP = 0;
+ for (unsigned int i = 0; i < refCount; ++i)
+ {
+ if (IsInterrupt())
+ return total;
+
+ if (refs[i].Object)
+ {
+ if (mSize)
+ ClearSizeData();
+
+ RootNode *path = FindPathToTarget(TO_TADDR(refs[i].Object));
+ if (path)
+ {
+ bool reportFrame = refs[i].Source != prevSource || refs[i].StackPointer != prevSP;
+ ReportOnePath(osThreadId, refs[i], path, first, reportFrame);
+ first = false;
+ total++;
+ }
+
+ if (mSize)
+ ReportSizeInfo(osThreadId, refs[i], TO_TADDR(refs[i].Object));
+ }
+ }
+
+ return total;
+}
+
+int GCRootImpl::PrintRootsOnHandleTable(int gen)
+{
+ // Get handle data.
+ ToRelease<ISOSHandleEnum> pEnum = NULL;
+ HRESULT hr = S_OK;
+
+ if (gen == -1 || (ULONG)gen == GetMaxGeneration())
+ hr = g_sos->GetHandleEnum(&pEnum);
+ else
+ hr = g_sos->GetHandleEnumForGC(gen, &pEnum);
+
+ if (FAILED(hr))
+ {
+ ExtOut("Failed to walk the HandleTable!\n");
+ return 0;
+ }
+
+ int total = 0;
+ unsigned int fetched = 0;
+ SOSHandleData handles[8];
+
+ bool printHeader = true;
+ do
+ {
+ // Fetch more handles
+ hr = pEnum->Next(_countof(handles), handles, &fetched);
+ if (FAILED(hr))
+ {
+ ExtOut("Failed to request more handles.");
+ return total;
+ }
+
+ // Find rooting info for each handle.
+ for (unsigned int i = 0; i < fetched; ++i)
+ {
+ if (IsInterrupt())
+ return total;
+
+ // Ignore handles which aren't actually roots.
+ if (!handles[i].StrongReference)
+ continue;
+
+ // clear the size table
+ if (mAll)
+ ClearSizeData();
+
+ // Get the object the handle points to.
+ TADDR root = ReadPointer(TO_TADDR(handles[i].Handle));
+
+ // Only inspect handle if the object is non-null, and not one we've already walked.
+ if (root)
+ {
+ // Find all paths to the object and don't clean up the return value.
+ // It's tracked by mCleanupList.
+ RootNode *path = FindPathToTarget(root);
+ if (path)
+ {
+ ReportOneHandlePath(handles[i], path, printHeader);
+ printHeader = false;
+ total++;
+ }
+
+ if (mSize)
+ ReportSizeInfo(handles[i], root);
+ }
+ }
+ }
+ while (_countof(handles) == fetched);
+
+ return total;
+}
+
+GCRootImpl::RootNode *GCRootImpl::FilterRoots(RootNode *&list)
+{
+ // Filter the list of GC refs:
+ // - Remove objects that we have already considered
+ // - Check to see if we've located the target object (or an object which points to the target).
+ RootNode *curr = list;
+ RootNode *keep = NULL;
+
+ while (curr)
+ {
+ // We don't check for Control-C in this loop to avoid inconsistent data.
+ RootNode *curr_next = curr->Next;
+
+ std::unordered_map<TADDR, RootNode *>::iterator targetItr = mTargets.find(curr->Object);
+ if (targetItr != mTargets.end())
+ {
+ // We found the object we are looking for (or an object which points to it)!
+ // Return the target, propogate whether we got the target from a dependent handle.
+ targetItr->second->FromDependentHandle = curr->FromDependentHandle;
+ return targetItr->second;
+ }
+ else if (mConsidered.find(curr->Object) != mConsidered.end())
+ {
+ curr->Remove(list);
+
+ DeleteNode(curr);
+ }
+
+ curr = curr_next;
+ }
+
+ return NULL;
+}
+
+/* This is the core of the GCRoot algorithm. It is:
+ * 1. Start with a list of "targets" (objects we are trying to find the roots for) and a root
+ * in the process.
+ * 2. Let the root be "curr".
+ * 3. Find all objects curr points to and place them in curr->GCRefs (a linked list).
+ * 4. Walk curr->GCRefs. If we find any of the "targets", return the current path. If not,
+ * filter out any objects we've already considered (the mConsidered set).
+ * 5. Look at curr->GCRefs:
+ * 5a. If curr->GCRefs is NULL then we have walked all references of this object. Pop "curr"
+ * from the current path (curr = curr->Prev). If curr is NULL, we walked all objects and
+ * didn't find a target, return NULL. If curr is non-null, goto 5.
+ * 5b. If curr->GCRefs is non-NULL, pop one entry and push it onto the path (that is:
+ * curr->Next = curr->GCRefs; curr = curr->Next; curr->GCRefs = curr->GCRefs->Next)
+ * 6. Goto 3.
+ */
+GCRootImpl::RootNode *GCRootImpl::FindPathToTarget(TADDR root)
+{
+ // Early out. We may have already looked at this object.
+ std::unordered_map<TADDR, RootNode *>::iterator targetItr = mTargets.find(root);
+ if (targetItr != mTargets.end())
+ return targetItr->second;
+ else if (mConsidered.find(root) != mConsidered.end())
+ return NULL;
+
+ // Add obj as a considered node (since we are considering it now).
+ mConsidered.insert(root);
+
+ // Create path.
+ RootNode *path = NewNode(root);
+
+ RootNode *curr = path;
+ while (curr)
+ {
+ if (IsInterrupt())
+ return NULL;
+
+ // If this is a new reference we are walking, we haven't filled the list of objects
+ // this one points to. Update that first.
+ if (!curr->FilledRefs)
+ {
+ // Get the list of GC refs.
+ curr->GCRefs = GetGCRefs(path, curr);
+
+ // Filter the refs to remove objects we've already inspected.
+ RootNode *foundTarget = FilterRoots(curr->GCRefs);
+
+ // If we've found the target, great! Return the path to the target.
+ if (foundTarget)
+ {
+ // Link the current to the target.
+ curr->Next = foundTarget;
+ foundTarget->Prev = curr;
+
+ // If the user requested all paths, set each node in the path to be a target.
+ // Normally, we don't consider a node we've already seen, which means if we don't
+ // get a *completely* unique path, it's not printed out. By adding each of the
+ // nodes in the paths we find as potential targets, it prints out *every* path
+ // to the target, including ones with duplicate nodes. This is much slower.
+ if (mAll)
+ {
+ RootNode *tmp = path;
+
+ while (tmp)
+ {
+ if (mTargets.find(tmp->Object) != mTargets.end())
+ break;
+
+ mTargets[tmp->Object] = tmp;
+ tmp = tmp->Next;
+ }
+ }
+
+ return path;
+ }
+ }
+
+ // We have filled the references, now walk them depth-first.
+ if (curr->GCRefs)
+ {
+ RootNode *next = curr->GCRefs;
+ curr->GCRefs = next->Next;
+
+ if (mConsidered.find(next->Object) != mConsidered.end())
+ {
+ // Whoops. This object was considered deeper down the tree, so we
+ // don't need to do it again. Delete this node and continue looping.
+ DeleteNode(next);
+ }
+ else
+ {
+ // Clear associations.
+ if (curr->GCRefs)
+ curr->GCRefs->Prev = NULL;
+
+ next->Next = NULL;
+
+ // Link curr and next, make next the current node.
+ curr->Next = next;
+ next->Prev = curr;
+ curr = next;
+
+ // Finally, insert the current object into the considered set.
+ mConsidered.insert(curr->Object);
+ // Now the next iteration will operate on "next".
+ }
+ }
+ else
+ {
+ // This object has no more GCRefs. We now need to "pop" it from the current path.
+ RootNode *tmp = curr;
+ curr = curr->Prev;
+ DeleteNode(tmp);
+ }
+ }
+
+ return NULL;
+}
+
+
+GCRootImpl::RootNode *GCRootImpl::GetGCRefs(RootNode *path, RootNode *node)
+{
+ // If this node doesn't have the method table ready, fill it out
+ TADDR obj = node->Object;
+ if (!node->MTInfo)
+ {
+ TADDR mt = ReadPointerCached(obj);
+ MTInfo *mtInfo = GetMTInfo(mt);
+ node->MTInfo = mtInfo;
+ }
+
+ node->FilledRefs = true;
+
+ // MTInfo can be null if we encountered an error reading out of the target
+ // process, just early out here as if it has no references.
+ if (!node->MTInfo)
+ return NULL;
+
+ // Only calculate the size if we need it.
+ size_t objSize = 0;
+ if (mSize || node->MTInfo->ContainsPointers)
+ {
+ objSize = GetSizeOfObject(obj, node->MTInfo);
+
+ // Update object size list, if requested.
+ if (mSize)
+ {
+ mSizes[obj] = 0;
+
+ while (path)
+ {
+ mSizes[path->Object] += objSize;
+ path = path->Next;
+ }
+ }
+ }
+
+ // Early out: If the object doesn't contain any pointers, return.
+ if (!node->MTInfo->ContainsPointers)
+ return NULL;
+
+ // Make sure we have the object's data in the cache.
+ mCache.EnsureRangeInCache(obj, (unsigned int)objSize);
+
+ // Storage for the gc refs.
+ RootNode *refs = NewNode();
+ RootNode *curr = refs;
+
+ // Walk the GCDesc, fill "refs" with non-null references.
+ CGCDesc *gcdesc = node->MTInfo->GCDesc;
+ bool aovc = node->MTInfo->ArrayOfVC;
+ for (sos::RefIterator itr(obj, gcdesc, aovc, &mCache); itr; ++itr)
+ {
+ if (*itr)
+ {
+ curr->Next = NewNode(*itr);
+ curr->Next->Prev = curr;
+ curr = curr->Next;
+ }
+ }
+
+ // Add edges from dependent handles.
+ std::unordered_map<TADDR, std::list<TADDR>>::iterator itr = mDependentHandleMap.find(obj);
+ if (itr != mDependentHandleMap.end())
+ {
+ for (std::list<TADDR>::iterator litr = itr->second.begin(); litr != itr->second.end(); ++litr)
+ {
+ curr->Next = NewNode(*litr, NULL, true);
+ curr->Next->Prev = curr;
+ curr = curr->Next;
+ }
+ }
+
+ // The gcrefs actually start on refs->Next.
+ curr = refs;
+ refs = refs->Next;
+ DeleteNode(curr);
+
+ return refs;
+}
+
+DWORD GCRootImpl::GetComponents(TADDR obj, TADDR mt)
+{
+ // Get the number of components in the object (for arrays and such).
+ DWORD Value = 0;
+
+ // If we fail to read out the number of components, let's assume 0 so we don't try to
+ // read further data from the object.
+ if (!mCache.Read(obj + sizeof(TADDR), &Value, false))
+ return 0;
+
+ // The component size on a String does not contain the trailing NULL character,
+ // so we must add that ourselves.
+ if(TO_TADDR(g_special_usefulGlobals.StringMethodTable) == mt)
+ return Value+1;
+
+ return Value;
+}
+
+// Get the size of the object.
+size_t GCRootImpl::GetSizeOfObject(TADDR obj, MTInfo *info)
+{
+ size_t res = info->BaseSize;
+
+ if (info->ComponentSize)
+ {
+ // this is an array, so the size has to include the size of the components. We read the number
+ // of components from the target and multiply by the component size to get the size.
+ DWORD components = GetComponents(obj, info->MethodTable);
+ res += info->ComponentSize * components;
+ }
+
+#ifdef _TARGET_WIN64_
+ // On x64 we do an optimization to save 4 bytes in almost every string we create, so
+ // pad to min object size if necessary.
+ if (res < min_obj_size)
+ res = min_obj_size;
+#endif // _TARGET_WIN64_
+
+ return (res > 0x10000) ? AlignLarge(res) : Align(res);
+}
+
+GCRootImpl::MTInfo *GCRootImpl::GetMTInfo(TADDR mt)
+{
+ // Remove lower bits in case we are in mark phase
+ mt &= ~3;
+
+ // Do we already have this MethodTable?
+ std::unordered_map<TADDR, MTInfo *>::iterator itr = mMTs.find(mt);
+
+ if (itr != mMTs.end())
+ return itr->second;
+
+ MTInfo *curr = new MTInfo;
+ curr->MethodTable = mt;
+
+ // Get Base/Component size.
+ DacpMethodTableData dmtd;
+
+ if (dmtd.Request(g_sos, mt) != S_OK)
+ {
+ delete curr;
+ return NULL;
+ }
+
+ // Fill out size info.
+ curr->BaseSize = (size_t)dmtd.BaseSize;
+ curr->ComponentSize = (size_t)dmtd.ComponentSize;
+ curr->ContainsPointers = dmtd.bContainsPointers ? true : false;
+
+ // If this method table contains pointers, fill out and cache the GCDesc.
+ if (curr->ContainsPointers)
+ {
+ int nEntries;
+
+ if (FAILED(MOVE(nEntries, mt-sizeof(TADDR))))
+ {
+ ExtOut("Failed to request number of entries.");
+ delete curr;
+ return NULL;
+ }
+
+ if (nEntries < 0)
+ {
+ curr->ArrayOfVC = true;
+ nEntries = -nEntries;
+ }
+ else
+ {
+ curr->ArrayOfVC = false;
+ }
+
+ size_t nSlots = 1 + nEntries * sizeof(CGCDescSeries)/sizeof(TADDR);
+ curr->Buffer = new TADDR[nSlots];
+
+ if (curr->Buffer == NULL)
+ {
+ ReportOOM();
+ delete curr;
+ return NULL;
+ }
+
+ if (FAILED(g_ExtData->ReadVirtual(TO_CDADDR(mt - nSlots*sizeof(TADDR)), curr->Buffer, (ULONG)(nSlots*sizeof(TADDR)), NULL)))
+ {
+ ExtOut("Failed to read GCDesc for MethodTable %p.\n", SOS_PTR(mt));
+ delete curr;
+ return NULL;
+ }
+
+ // Construct the GCDesc map and series.
+ curr->GCDesc = (CGCDesc *)(curr->Buffer+nSlots);
+ }
+
+ mMTs[mt] = curr;
+ return curr;
+}
+
+
+TADDR GCRootImpl::ReadPointer(TADDR location)
+{
+ // Reads a pointer from the cache, but doesn't update the cache if it wasn't in it.
+ TADDR obj = NULL;
+ bool res = mCache.Read(location, &obj, false);
+
+ if (!res)
+ return NULL;
+
+ return obj;
+}
+
+TADDR GCRootImpl::ReadPointerCached(TADDR location)
+{
+ // Reads a pointer from the cache, but updates the cache if it wasn't in it.
+ TADDR obj = NULL;
+ bool res = mCache.Read(location, &obj, true);
+
+ if (!res)
+ return NULL;
+
+ return obj;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+UINT FindAllPinnedAndStrong(DWORD_PTR handlearray[], UINT arraySize)
+{
+ unsigned int fetched = 0;
+ SOSHandleData data[64];
+ UINT pos = 0;
+
+ // We do not call GetHandleEnumByType here with a list of strong handles since we would be
+ // statically setting the list of strong handles, which could change in a future release.
+ // Instead we rely on the dac to provide whether a handle is strong or not.
+ ToRelease<ISOSHandleEnum> handles;
+ HRESULT hr = g_sos->GetHandleEnum(&handles);
+ if (FAILED(hr))
+ {
+ // This should basically never happen unless there's an OOM.
+ ExtOut("Failed to enumerate GC handles. HRESULT=%x.\n", hr);
+ return 0;
+ }
+
+ do
+ {
+ hr = handles->Next(_countof(data), data, &fetched);
+
+ if (FAILED(hr))
+ {
+ ExtOut("Failed to enumerate GC handles. HRESULT=%x.\n", hr);
+ break;
+ }
+
+ for (unsigned int i = 0; i < fetched; ++i)
+ {
+ if (pos >= arraySize)
+ {
+ ExtOut("Buffer overflow while enumerating handles.\n");
+ return pos;
+ }
+
+ if (data[i].StrongReference)
+ {
+ handlearray[pos++] = (DWORD_PTR)data[i].Handle;
+ }
+ }
+ } while (fetched == _countof(data));
+
+ return pos;
+}
+
+
+
+void PrintNotReachableInRange(TADDR rngStart, TADDR rngEnd, BOOL bExcludeReadyForFinalization, HeapStat* hpstat, BOOL bShort)
+{
+ GCRootImpl gcroot;
+ const std::unordered_set<TADDR> &liveObjs = gcroot.GetLiveObjects(bExcludeReadyForFinalization == TRUE);
+
+ LinearReadCache cache(512);
+ cache.EnsureRangeInCache(rngStart, (unsigned int)(rngEnd-rngStart));
+
+ for (TADDR p = rngStart; p < rngEnd; p += sizeof(TADDR))
+ {
+ if (IsInterrupt())
+ break;
+
+ TADDR header = 0;
+ TADDR obj = 0;
+ TADDR taddrMT = 0;
+
+ bool read = cache.Read(p-sizeof(SIZEOF_OBJHEADER), &header);
+ read = read && cache.Read(p, &obj);
+ if (read && ((header & BIT_SBLK_FINALIZER_RUN) == 0) && liveObjs.find(obj) == liveObjs.end())
+ {
+ if (bShort)
+ {
+ DMLOut("%s\n", DMLObject(obj));
+ }
+ else
+ {
+ DMLOut("%s ", DMLObject(obj));
+ if (SUCCEEDED(GetMTOfObject(obj, &taddrMT)) && taddrMT)
+ {
+ size_t s = ObjectSize(obj);
+ if (hpstat)
+ {
+ hpstat->Add(taddrMT, (DWORD)s);
+ }
+ }
+ }
+ }
+ }
+
+ if (!bShort)
+ ExtOut("\n");
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Some defines for cards taken from gc code
+//
+#define card_word_width ((size_t)32)
+
+//
+// The value of card_size is determined empirically according to the average size of an object
+// In the code we also rely on the assumption that one card_table entry (DWORD) covers an entire os page
+//
+#if defined (_TARGET_WIN64_)
+#define card_size ((size_t)(2*DT_OS_PAGE_SIZE/card_word_width))
+#else
+#define card_size ((size_t)(DT_OS_PAGE_SIZE/card_word_width))
+#endif //_TARGET_WIN64_
+
+// so card_size = 128 on x86, 256 on x64
+
+inline
+size_t card_word (size_t card)
+{
+ return card / card_word_width;
+}
+
+inline
+unsigned card_bit (size_t card)
+{
+ return (unsigned)(card % card_word_width);
+}
+
+inline
+size_t card_of ( BYTE* object)
+{
+ return (size_t)(object) / card_size;
+}
+
+BOOL CardIsSet(const DacpGcHeapDetails &heap, TADDR objAddr)
+{
+ // The card table has to be translated to look at the refcount, etc.
+ // g_card_table[card_word(card_of(g_lowest_address))].
+
+ TADDR card_table = TO_TADDR(heap.card_table);
+ card_table = card_table + card_word(card_of((BYTE *)heap.lowest_address))*sizeof(DWORD);
+
+ do
+ {
+ TADDR card_table_lowest_addr;
+ TADDR card_table_next;
+
+ if (MOVE(card_table_lowest_addr, ALIGN_DOWN(card_table, 0x1000) + sizeof(PVOID)) != S_OK)
+ {
+ ExtErr("Error getting card table lowest address\n");
+ return FALSE;
+ }
+
+ if (MOVE(card_table_next, card_table - sizeof(PVOID)) != S_OK)
+ {
+ ExtErr("Error getting next card table\n");
+ return FALSE;
+ }
+
+ size_t card = (objAddr - card_table_lowest_addr) / card_size;
+ DWORD value;
+ if (MOVE(value, card_table + card_word(card)*sizeof(DWORD)) != S_OK)
+ {
+ ExtErr("Error reading card bits\n");
+ return FALSE;
+ }
+
+ if (value & 1<<card_bit(card))
+ return TRUE;
+
+ card_table = card_table_next;
+ }
+ while(card_table);
+
+ return FALSE;
+}
+
+BOOL NeedCard(TADDR parent, TADDR child)
+{
+ int iChildGen = g_snapshot.GetGeneration(child);
+
+ if (iChildGen == 2)
+ return FALSE;
+
+ int iParentGen = g_snapshot.GetGeneration(parent);
+
+ return (iChildGen < iParentGen);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Some defines for mark_array taken from gc code
+//
+
+#define mark_bit_pitch 8
+#define mark_word_width 32
+#define mark_word_size (mark_word_width * mark_bit_pitch)
+#define heap_segment_flags_swept 16
+
+inline
+size_t mark_bit_bit_of(CLRDATA_ADDRESS add)
+{
+ return (size_t)((add / mark_bit_pitch) % mark_word_width);
+}
+
+inline
+size_t mark_word_of(CLRDATA_ADDRESS add)
+{
+ return (size_t)(add / mark_word_size);
+}
+
+inline BOOL mark_array_marked(const DacpGcHeapDetails &heap, CLRDATA_ADDRESS add)
+{
+
+ DWORD entry = 0;
+ HRESULT hr = MOVE(entry, heap.mark_array + sizeof(DWORD) * mark_word_of(add));
+
+ if (FAILED(hr))
+ ExtOut("Failed to read card table entry.\n");
+
+ return entry & (1 << mark_bit_bit_of(add));
+}
+
+BOOL background_object_marked(const DacpGcHeapDetails &heap, CLRDATA_ADDRESS o)
+{
+ BOOL m = TRUE;
+
+ if ((o >= heap.background_saved_lowest_address) && (o < heap.background_saved_highest_address))
+ m = mark_array_marked(heap, o);
+
+ return m;
+}
+
+BOOL fgc_should_consider_object(const DacpGcHeapDetails &heap,
+ CLRDATA_ADDRESS o,
+ const DacpHeapSegmentData &seg,
+ BOOL consider_bgc_mark_p,
+ BOOL check_current_sweep_p,
+ BOOL check_saved_sweep_p)
+{
+ // the logic for this function must be kept in sync with the analogous function in gc.cpp
+ BOOL no_bgc_mark_p = FALSE;
+
+ if (consider_bgc_mark_p)
+ {
+ if (check_current_sweep_p && (o < heap.next_sweep_obj))
+ {
+ no_bgc_mark_p = TRUE;
+ }
+
+ if (!no_bgc_mark_p)
+ {
+ if(check_saved_sweep_p && (o >= heap.saved_sweep_ephemeral_start))
+ {
+ no_bgc_mark_p = TRUE;
+ }
+
+ if (!check_saved_sweep_p)
+ {
+ CLRDATA_ADDRESS background_allocated = seg.background_allocated;
+ if (o >= background_allocated)
+ {
+ no_bgc_mark_p = TRUE;
+ }
+ }
+ }
+ }
+ else
+ {
+ no_bgc_mark_p = TRUE;
+ }
+
+ return no_bgc_mark_p ? TRUE : background_object_marked(heap, o);
+}
+
+enum c_gc_state
+{
+ c_gc_state_marking,
+ c_gc_state_planning,
+ c_gc_state_free
+};
+
+inline BOOL in_range_for_segment(const DacpHeapSegmentData &seg, CLRDATA_ADDRESS addr)
+{
+ return (addr >= seg.mem) && (addr < seg.reserved);
+}
+
+void should_check_bgc_mark(const DacpGcHeapDetails &heap,
+ const DacpHeapSegmentData &seg,
+ BOOL* consider_bgc_mark_p,
+ BOOL* check_current_sweep_p,
+ BOOL* check_saved_sweep_p)
+{
+ // the logic for this function must be kept in sync with the analogous function in gc.cpp
+ *consider_bgc_mark_p = FALSE;
+ *check_current_sweep_p = FALSE;
+ *check_saved_sweep_p = FALSE;
+
+ if (heap.current_c_gc_state == c_gc_state_planning)
+ {
+ // We are doing the next_sweep_obj comparison here because we have yet to
+ // turn on the swept flag for the segment but in_range_for_segment will return
+ // FALSE if the address is the same as reserved.
+ if ((seg.flags & heap_segment_flags_swept) || (heap.next_sweep_obj == seg.reserved))
+ {
+ // this seg was already swept.
+ }
+ else
+ {
+ *consider_bgc_mark_p = TRUE;
+
+ if (seg.segmentAddr == heap.saved_sweep_ephemeral_seg)
+ {
+ *check_saved_sweep_p = TRUE;
+ }
+
+ if (in_range_for_segment(seg, heap.next_sweep_obj))
+ {
+ *check_current_sweep_p = TRUE;
+ }
+ }
+ }
+}
+
+// TODO: FACTOR TOGETHER THE OBJECT MEMBER WALKING CODE FROM
+// TODO: VerifyObjectMember(), GetListOfRefs(), HeapTraverser::PrintRefs()
+BOOL VerifyObjectMember(const DacpGcHeapDetails &heap, DWORD_PTR objAddr)
+{
+ BOOL ret = TRUE;
+ BOOL bCheckCard = TRUE;
+ size_t size = 0;
+ {
+ DWORD_PTR dwAddrCard = objAddr;
+ while (dwAddrCard < objAddr + size)
+ {
+ if (CardIsSet(heap, dwAddrCard))
+ {
+ bCheckCard = FALSE;
+ break;
+ }
+ dwAddrCard += card_size;
+ }
+
+ if (bCheckCard)
+ {
+ dwAddrCard = objAddr + size - 2*sizeof(PVOID);
+ if (CardIsSet(heap, dwAddrCard))
+ {
+ bCheckCard = FALSE;
+ }
+ }
+ }
+
+ for (sos::RefIterator itr(TO_TADDR(objAddr)); itr; ++itr)
+ {
+ TADDR dwAddr1 = (DWORD_PTR)*itr;
+ if (dwAddr1)
+ {
+ TADDR dwChild = dwAddr1;
+ // Try something more efficient than IsObject here. Is the methodtable valid?
+ size_t s;
+ BOOL bPointers;
+ TADDR dwAddrMethTable;
+ if (FAILED(GetMTOfObject(dwAddr1, &dwAddrMethTable)) ||
+ (GetSizeEfficient(dwAddr1, dwAddrMethTable, FALSE, s, bPointers) == FALSE))
+ {
+ DMLOut("object %s: bad member %p at %p\n", DMLObject(objAddr), SOS_PTR(dwAddr1), SOS_PTR(itr.GetOffset()));
+ ret = FALSE;
+ }
+
+ if (IsMTForFreeObj(dwAddrMethTable))
+ {
+ DMLOut("object %s contains free object %p at %p\n", DMLObject(objAddr),
+ SOS_PTR(dwAddr1), SOS_PTR(objAddr+itr.GetOffset()));
+ ret = FALSE;
+ }
+
+ // verify card table
+ if (bCheckCard && NeedCard(objAddr+itr.GetOffset(), dwAddr1))
+ {
+ DMLOut("object %s:%s missing card_table entry for %p\n",
+ DMLObject(objAddr), (dwChild == dwAddr1) ? "" : " maybe",
+ SOS_PTR(objAddr+itr.GetOffset()));
+ ret = FALSE;
+ }
+ }
+ }
+
+ return ret;
+}
+
+// search for can_verify_deep in gc.cpp for examples of how these functions are used.
+BOOL VerifyObject(const DacpGcHeapDetails &heap, const DacpHeapSegmentData &seg, DWORD_PTR objAddr, DWORD_PTR MTAddr, size_t objSize,
+ BOOL bVerifyMember)
+{
+ if (IsMTForFreeObj(MTAddr))
+ {
+ return TRUE;
+ }
+
+ if (objSize < min_obj_size)
+ {
+ DMLOut("object %s: size %d too small\n", DMLObject(objAddr), objSize);
+ return FALSE;
+ }
+
+ // If we requested to verify the object's members, the GC may be in a state where that's not possible.
+ // Here we check to see if the object in question needs to have its members updated. If so, we turn off
+ // verification for the object.
+ if (bVerifyMember)
+ {
+ BOOL consider_bgc_mark = FALSE, check_current_sweep = FALSE, check_saved_sweep = FALSE;
+ should_check_bgc_mark(heap, seg, &consider_bgc_mark, &check_current_sweep, &check_saved_sweep);
+ bVerifyMember = fgc_should_consider_object(heap, objAddr, seg, consider_bgc_mark, check_current_sweep, check_saved_sweep);
+ }
+
+ return bVerifyMember ? VerifyObjectMember(heap, objAddr) : TRUE;
+}
+
+
+BOOL FindSegment(const DacpGcHeapDetails &heap, DacpHeapSegmentData &seg, CLRDATA_ADDRESS addr)
+{
+ CLRDATA_ADDRESS dwAddrSeg = heap.generation_table[GetMaxGeneration()].start_segment;
+
+ // Request the inital segment.
+ if (seg.Request(g_sos, dwAddrSeg, heap) != S_OK)
+ {
+ ExtOut("Error requesting heap segment %p.\n", SOS_PTR(dwAddrSeg));
+ return FALSE;
+ }
+
+ // Loop while the object is not in range of the segment.
+ while (addr < TO_TADDR(seg.mem) ||
+ addr >= (dwAddrSeg == heap.ephemeral_heap_segment ? heap.alloc_allocated : TO_TADDR(seg.allocated)))
+ {
+ // get the next segment
+ dwAddrSeg = seg.next;
+
+ // We reached the last segment without finding the object.
+ if (dwAddrSeg == NULL)
+ return FALSE;
+
+ if (seg.Request(g_sos, dwAddrSeg, heap) != S_OK)
+ {
+ ExtOut("Error requesting heap segment %p.\n", SOS_PTR(dwAddrSeg));
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL VerifyObject(const DacpGcHeapDetails &heap, DWORD_PTR objAddr, DWORD_PTR MTAddr, size_t objSize, BOOL bVerifyMember)
+{
+ // This is only used by the other VerifyObject function if bVerifyMember is true,
+ // so we only intialize it if we need it for verifying object members.
+ DacpHeapSegmentData seg;
+
+ if (bVerifyMember)
+ {
+ // if we fail to find the segment, we cannot verify the object's members
+ bVerifyMember = FindSegment(heap, seg, objAddr);
+ }
+
+ return VerifyObject(heap, seg, objAddr, MTAddr, objSize, bVerifyMember);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+typedef void (*TYPETREEVISIT)(size_t methodTable, size_t ID, LPVOID token);
+
+// TODO remove this. MethodTableCache already maps method tables to
+// various information. We don't need TypeTree to do this too.
+// Straightfoward to do, but low priority.
+class TypeTree
+{
+private:
+ size_t methodTable;
+ size_t ID;
+ TypeTree *pLeft;
+ TypeTree *pRight;
+
+public:
+ TypeTree(size_t MT) : methodTable(MT),ID(0),pLeft(NULL),pRight(NULL) { }
+
+ BOOL isIn(size_t MT, size_t *pID)
+ {
+ TypeTree *pCur = this;
+
+ while (pCur)
+ {
+ if (MT == pCur->methodTable)
+ {
+ if (pID)
+ *pID = pCur->ID;
+ return TRUE;
+ }
+ else if (MT < pCur->methodTable)
+ pCur = pCur->pLeft;
+ else
+ pCur = pCur->pRight;
+ }
+
+ return FALSE;
+ }
+
+ BOOL insert(size_t MT)
+ {
+ TypeTree *pCur = this;
+
+ while (pCur)
+ {
+ if (MT == pCur->methodTable)
+ return TRUE;
+ else if ((MT < pCur->methodTable))
+ {
+ if (pCur->pLeft)
+ pCur = pCur->pLeft;
+ else
+ break;
+ }
+ else if (pCur->pRight)
+ pCur = pCur->pRight;
+ else
+ break;
+ }
+
+ // If we got here, we need to append at the current node.
+ TypeTree *pNewNode = new TypeTree(MT);
+ if (pNewNode == NULL)
+ return FALSE;
+
+ if (MT < pCur->methodTable)
+ pCur->pLeft = pNewNode;
+ else
+ pCur->pRight = pNewNode;
+
+ return TRUE;
+ }
+
+ static void destroy(TypeTree *pStart)
+ {
+ TypeTree *pCur = pStart;
+
+ if (pCur)
+ {
+ destroy(pCur->pLeft);
+ destroy(pCur->pRight);
+ delete [] pCur;
+ }
+ }
+
+ static void visit_inorder(TypeTree *pStart, TYPETREEVISIT pFunc, LPVOID token)
+ {
+ TypeTree *pCur = pStart;
+
+ if (pCur)
+ {
+ visit_inorder(pCur->pLeft, pFunc, token);
+ pFunc (pCur->methodTable, pCur->ID, token);
+ visit_inorder(pCur->pRight, pFunc, token);
+ }
+ }
+
+ static void setTypeIDs(TypeTree *pStart, size_t *pCurID)
+ {
+ TypeTree *pCur = pStart;
+
+ if (pCur)
+ {
+ setTypeIDs(pCur->pLeft, pCurID);
+ pCur->ID = *pCurID;
+ (*pCurID)++;
+ setTypeIDs(pCur->pRight, pCurID);
+ }
+ }
+
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+
+HeapTraverser::HeapTraverser(bool verify)
+{
+ m_format = 0;
+ m_file = NULL;
+ m_objVisited = 0;
+ m_pTypeTree = NULL;
+ m_curNID = 1;
+ m_verify = verify;
+}
+
+HeapTraverser::~HeapTraverser()
+{
+ if (m_pTypeTree) {
+ TypeTree::destroy(m_pTypeTree);
+ m_pTypeTree = NULL;
+ }
+}
+
+BOOL HeapTraverser::Initialize()
+{
+ if (!GCHeapsTraverse (HeapTraverser::GatherTypes, this, m_verify))
+ {
+ ExtOut("Error during heap traverse\n");
+ return FALSE;
+ }
+
+ GCRootImpl::GetDependentHandleMap(mDependentHandleMap);
+
+ size_t startID = 1;
+ TypeTree::setTypeIDs(m_pTypeTree, &startID);
+
+ return TRUE;
+}
+
+BOOL HeapTraverser::CreateReport (FILE *fp, int format)
+{
+ if (fp == NULL || (format!=FORMAT_XML && format != FORMAT_CLRPROFILER))
+ {
+ return FALSE;
+ }
+
+ m_file = fp;
+ m_format = format;
+
+ PrintSection(TYPE_START,TRUE);
+
+ PrintSection(TYPE_TYPES,TRUE);
+ TypeTree::visit_inorder(m_pTypeTree, HeapTraverser::PrintOutTree, this);
+ PrintSection(TYPE_TYPES,FALSE);
+
+ ExtOut("tracing roots...\n");
+ PrintSection(TYPE_ROOTS,TRUE);
+ PrintRootHead();
+
+ TraceHandles();
+ FindGCRootOnStacks();
+
+ PrintRootTail();
+ PrintSection(TYPE_ROOTS,FALSE);
+
+ // now print type tree
+ PrintSection(TYPE_OBJECTS,TRUE);
+ ExtOut("\nWalking heap...\n");
+ m_objVisited = 0; // for UI updates
+ GCHeapsTraverse (HeapTraverser::PrintHeap, this, FALSE); // Never verify on the second pass
+ PrintSection(TYPE_OBJECTS,FALSE);
+
+ PrintSection(TYPE_START,FALSE);
+
+ m_file = NULL;
+ return TRUE;
+}
+
+void HeapTraverser::insert(size_t mTable)
+{
+ if (m_pTypeTree == NULL)
+ {
+ m_pTypeTree = new TypeTree(mTable);
+ if (m_pTypeTree == NULL)
+ {
+ ReportOOM();
+ return;
+ }
+ }
+ else
+ {
+ m_pTypeTree->insert(mTable);
+ }
+}
+
+size_t HeapTraverser::getID(size_t mTable)
+{
+ if (m_pTypeTree == NULL)
+ {
+ return 0;
+ }
+ // IDs start at 1, so we can return 0 if not found.
+ size_t ret;
+ if (m_pTypeTree->isIn(mTable,&ret))
+ {
+ return ret;
+ }
+
+ return 0;
+}
+
+#ifndef FEATURE_PAL
+void replace(std::wstring &str, const WCHAR *toReplace, const WCHAR *replaceWith)
+{
+ const size_t replaceLen = _wcslen(toReplace);
+ const size_t replaceWithLen = _wcslen(replaceWith);
+
+ size_t i = str.find(toReplace);
+ while (i != std::wstring::npos)
+ {
+ str.replace(i, replaceLen, replaceWith);
+ i = str.find(toReplace, i + replaceWithLen);
+ }
+}
+#endif
+
+void HeapTraverser::PrintType(size_t ID,LPCWSTR name)
+{
+ if (m_format==FORMAT_XML)
+ {
+#ifndef FEATURE_PAL
+ // Sanitize name based on XML spec.
+ std::wstring wname = name;
+ replace(wname, W("&"), W("&amp;"));
+ replace(wname, W("\""), W("&quot;"));
+ replace(wname, W("'"), W("&apos;"));
+ replace(wname, W("<"), W("&lt;"));
+ replace(wname, W(">"), W("&gt;"));
+ name = wname.c_str();
+#endif
+ fprintf(m_file,
+ "<type id=\"%d\" name=\"%S\"/>\n",
+ ID, name);
+ }
+ else if (m_format==FORMAT_CLRPROFILER)
+ {
+ fprintf(m_file,
+ "t %d 0 %S\n",
+ ID,name);
+ }
+}
+
+void HeapTraverser::PrintObjectHead(size_t objAddr,size_t typeID,size_t Size)
+{
+ if (m_format==FORMAT_XML)
+ {
+ fprintf(m_file,
+ "<object address=\"0x%p\" typeid=\"%d\" size=\"%d\">\n",
+ (PBYTE)objAddr,typeID, Size);
+ }
+ else if (m_format==FORMAT_CLRPROFILER)
+ {
+ fprintf(m_file,
+ "n %d 1 %d %d\n",
+ m_curNID,typeID,Size);
+
+ fprintf(m_file,
+ "! 1 0x%p %d\n",
+ (PBYTE)objAddr,m_curNID);
+
+ m_curNID++;
+
+ fprintf(m_file,
+ "o 0x%p %d %d ",
+ (PBYTE)objAddr,typeID,Size);
+ }
+}
+
+void HeapTraverser::PrintObjectMember(size_t memberValue, bool dependentHandle)
+{
+ if (m_format==FORMAT_XML)
+ {
+ fprintf(m_file,
+ " <member address=\"0x%p\"%s/>\n",
+ (PBYTE)memberValue, dependentHandle ? " dependentHandle=\"1\"" : "");
+ }
+ else if (m_format==FORMAT_CLRPROFILER)
+ {
+ fprintf(m_file,
+ " 0x%p",
+ (PBYTE)memberValue);
+ }
+}
+
+void HeapTraverser::PrintObjectTail()
+{
+ if (m_format==FORMAT_XML)
+ {
+ fprintf(m_file,
+ "</object>\n");
+ }
+ else if (m_format==FORMAT_CLRPROFILER)
+ {
+ fprintf(m_file,
+ "\n");
+ }
+}
+
+void HeapTraverser::PrintRootHead()
+{
+ if (m_format==FORMAT_CLRPROFILER)
+ {
+ fprintf(m_file,
+ "r ");
+ }
+}
+
+void HeapTraverser::PrintRoot(LPCWSTR kind,size_t Value)
+{
+ if (m_format==FORMAT_XML)
+ {
+ fprintf(m_file,
+ "<root kind=\"%S\" address=\"0x%p\"/>\n",
+ kind,
+ (PBYTE)Value);
+ }
+ else if (m_format==FORMAT_CLRPROFILER)
+ {
+ fprintf(m_file,
+ "0x%p ",
+ (PBYTE)Value);
+ }
+}
+
+void HeapTraverser::PrintRootTail()
+{
+ if (m_format==FORMAT_CLRPROFILER)
+ {
+ fprintf(m_file,
+ "\n");
+ }
+}
+
+void HeapTraverser::PrintSection(int Type,BOOL bOpening)
+{
+ const char *const pTypes[] = {"<gcheap>","<types>","<roots>","<objects>"};
+ const char *const pTypeEnds[] = {"</gcheap>","</types>","</roots>","</objects>"};
+
+ if (m_format==FORMAT_XML)
+ {
+ if ((Type >= 0) && (Type < TYPE_HIGHEST))
+ {
+ fprintf(m_file,"%s\n",bOpening ? pTypes[Type] : pTypeEnds[Type]);
+ }
+ else
+ {
+ ExtOut ("INVALID TYPE %d\n", Type);
+ }
+ }
+ else if (m_format==FORMAT_CLRPROFILER)
+ {
+ if ((Type == TYPE_START) && !bOpening) // a final newline is needed
+ {
+ fprintf(m_file,"\n");
+ }
+ }
+}
+
+void HeapTraverser::FindGCRootOnStacks()
+{
+ ArrayHolder<DWORD_PTR> threadList = NULL;
+ int numThreads = 0;
+
+ // GetThreadList calls ReportOOM so we don't need to do that here.
+ HRESULT hr = GetThreadList(&threadList, &numThreads);
+ if (FAILED(hr) || !threadList)
+ {
+ ExtOut("Failed to enumerate threads in the process.\n");
+ return;
+ }
+
+ int total = 0;
+ DacpThreadData vThread;
+ for (int i = 0; i < numThreads; i++)
+ {
+ if (FAILED(vThread.Request(g_sos, threadList[i])))
+ continue;
+
+ if (vThread.osThreadId)
+ {
+ unsigned int refCount = 0;
+ ArrayHolder<SOSStackRefData> refs = NULL;
+
+ if (FAILED(::GetGCRefs(vThread.osThreadId, &refs, &refCount, NULL, NULL)))
+ {
+ ExtOut("Failed to walk thread %x\n", vThread.osThreadId);
+ continue;
+ }
+
+ for (unsigned int i = 0; i < refCount; ++i)
+ if (refs[i].Object)
+ PrintRoot(W("stack"), TO_TADDR(refs[i].Object));
+ }
+ }
+
+}
+
+
+/* static */ void HeapTraverser::PrintOutTree(size_t methodTable, size_t ID,
+ LPVOID token)
+{
+ HeapTraverser *pHolder = (HeapTraverser *) token;
+ NameForMT_s(methodTable, g_mdName, mdNameLen);
+ pHolder->PrintType(ID,g_mdName);
+}
+
+
+/* static */ void HeapTraverser::PrintHeap(DWORD_PTR objAddr,size_t Size,
+ DWORD_PTR methodTable, LPVOID token)
+{
+ if (!IsMTForFreeObj (methodTable))
+ {
+ HeapTraverser *pHolder = (HeapTraverser *) token;
+ pHolder->m_objVisited++;
+ size_t ID = pHolder->getID(methodTable);
+
+ pHolder->PrintObjectHead(objAddr, ID, Size);
+ pHolder->PrintRefs(objAddr, methodTable, Size);
+ pHolder->PrintObjectTail();
+
+ if (pHolder->m_objVisited % 1024 == 0) {
+ ExtOut(".");
+ if (pHolder->m_objVisited % (1024*64) == 0)
+ ExtOut("\r\n");
+ }
+ }
+}
+
+void HeapTraverser::TraceHandles()
+{
+ unsigned int fetched = 0;
+ SOSHandleData data[64];
+
+ ToRelease<ISOSHandleEnum> handles;
+ HRESULT hr = g_sos->GetHandleEnum(&handles);
+ if (FAILED(hr))
+ return;
+
+ do
+ {
+ hr = handles->Next(_countof(data), data, &fetched);
+
+ if (FAILED(hr))
+ break;
+
+ for (unsigned int i = 0; i < fetched; ++i)
+ PrintRoot(W("handle"), (size_t)data[i].Handle);
+ } while (fetched == _countof(data));
+}
+
+/* static */ void HeapTraverser::GatherTypes(DWORD_PTR objAddr,size_t Size,
+ DWORD_PTR methodTable, LPVOID token)
+{
+ if (!IsMTForFreeObj (methodTable))
+ {
+ HeapTraverser *pHolder = (HeapTraverser *) token;
+ pHolder->insert(methodTable);
+ }
+}
+
+void HeapTraverser::PrintRefs(size_t obj, size_t methodTable, size_t size)
+{
+ DWORD_PTR dwAddr = methodTable;
+
+ // TODO: pass info to callback having to lookup the MethodTableInfo again
+ MethodTableInfo* info = g_special_mtCache.Lookup((DWORD_PTR)methodTable);
+ _ASSERTE(info->IsInitialized()); // This is the second pass, so we should be intialized
+
+ if (!info->bContainsPointers)
+ return;
+
+ // Fetch the GCInfo from the other process
+ CGCDesc *map = info->GCInfo;
+ if (map == NULL)
+ {
+ INT_PTR nEntries;
+ move_xp (nEntries, dwAddr-sizeof(PVOID));
+ bool arrayOfVC = false;
+ if (nEntries<0)
+ {
+ arrayOfVC = true;
+ nEntries = -nEntries;
+ }
+
+ size_t nSlots = 1+nEntries*sizeof(CGCDescSeries)/sizeof(DWORD_PTR);
+ info->GCInfoBuffer = new DWORD_PTR[nSlots];
+ if (info->GCInfoBuffer == NULL)
+ {
+ ReportOOM();
+ return;
+ }
+
+ if (FAILED(rvCache->Read(TO_CDADDR(dwAddr - nSlots*sizeof(DWORD_PTR)),
+ info->GCInfoBuffer, (ULONG) (nSlots*sizeof(DWORD_PTR)), NULL)))
+ return;
+
+ map = info->GCInfo = (CGCDesc*)(info->GCInfoBuffer+nSlots);
+ info->ArrayOfVC = arrayOfVC;
+ }
+
+ mCache.EnsureRangeInCache((TADDR)obj, (unsigned int)size);
+ for (sos::RefIterator itr(obj, info->GCInfo, info->ArrayOfVC, &mCache); itr; ++itr)
+ {
+ if (*itr && (!m_verify || sos::IsObject(*itr)))
+ PrintObjectMember(*itr, false);
+ }
+
+ std::unordered_map<TADDR, std::list<TADDR>>::iterator itr = mDependentHandleMap.find((TADDR)obj);
+ if (itr != mDependentHandleMap.end())
+ {
+ for (std::list<TADDR>::iterator litr = itr->second.begin(); litr != itr->second.end(); ++litr)
+ {
+ PrintObjectMember(*litr, true);
+ }
+ }
+}
+
+
+void sos::ObjectIterator::BuildError(char *out, size_t count, const char *format, ...) const
+{
+ if (out == NULL || count == 0)
+ return;
+
+ va_list args;
+ va_start(args, format);
+
+ int written = vsprintf_s(out, count, format, args);
+ if (written > 0 && mLastObj)
+ sprintf_s(out+written, count-written, "\nLast good object: %p.\n", (int*)mLastObj);
+
+ va_end(args);
+}
+
+bool sos::ObjectIterator::VerifyObjectMembers(char *reason, size_t count) const
+{
+ if (!mCurrObj.HasPointers())
+ return true;
+
+ size_t size = mCurrObj.GetSize();
+ size_t objAddr = (size_t)mCurrObj.GetAddress();
+ TADDR mt = mCurrObj.GetMT();
+
+ INT_PTR nEntries;
+ MOVE(nEntries, mt-sizeof(PVOID));
+ if (nEntries < 0)
+ nEntries = -nEntries;
+
+ size_t nSlots = 1 + nEntries * sizeof(CGCDescSeries)/sizeof(DWORD_PTR);
+ ArrayHolder<DWORD_PTR> buffer = new DWORD_PTR[nSlots];
+
+ if (FAILED(g_ExtData->ReadVirtual(TO_CDADDR(mt - nSlots*sizeof(DWORD_PTR)),
+ buffer, (ULONG) (nSlots*sizeof(DWORD_PTR)), NULL)))
+ {
+ BuildError(reason, count, "Object %s has a bad GCDesc.", DMLObject(objAddr));
+ return false;
+ }
+
+ CGCDesc *map = (CGCDesc *)(buffer+nSlots);
+ CGCDescSeries* cur = map->GetHighestSeries();
+ CGCDescSeries* last = map->GetLowestSeries();
+
+ const size_t bufferSize = sizeof(size_t)*128;
+ size_t objBuffer[bufferSize/sizeof(size_t)];
+ size_t dwBeginAddr = (size_t)objAddr;
+ size_t bytesInBuffer = bufferSize;
+ if (size < bytesInBuffer)
+ bytesInBuffer = size;
+
+
+ if (FAILED(g_ExtData->ReadVirtual(TO_CDADDR(dwBeginAddr), objBuffer, (ULONG) bytesInBuffer,NULL)))
+ {
+ BuildError(reason, count, "Object %s: Failed to read members.", DMLObject(objAddr));
+ return false;
+ }
+
+ BOOL bCheckCard = TRUE;
+ {
+ DWORD_PTR dwAddrCard = (DWORD_PTR)objAddr;
+ while (dwAddrCard < objAddr + size)
+ {
+ if (CardIsSet (mHeaps[mCurrHeap], dwAddrCard))
+ {
+ bCheckCard = FALSE;
+ break;
+ }
+ dwAddrCard += card_size;
+ }
+ if (bCheckCard)
+ {
+ dwAddrCard = objAddr + size - 2*sizeof(PVOID);
+ if (CardIsSet (mHeaps[mCurrHeap], dwAddrCard))
+ {
+ bCheckCard = FALSE;
+ }
+ }
+ }
+
+ if (cur >= last)
+ {
+ do
+ {
+ BYTE** parm = (BYTE**)((objAddr) + cur->GetSeriesOffset());
+ BYTE** ppstop =
+ (BYTE**)((BYTE*)parm + cur->GetSeriesSize() + (size));
+ while (parm < ppstop)
+ {
+ CheckInterrupt();
+ size_t dwAddr1;
+
+ // Do we run out of cache?
+ if ((size_t)parm >= dwBeginAddr+bytesInBuffer)
+ {
+ // dwBeginAddr += bytesInBuffer;
+ dwBeginAddr = (size_t)parm;
+ if (dwBeginAddr >= objAddr + size)
+ {
+ return true;
+ }
+ bytesInBuffer = bufferSize;
+ if (objAddr+size-dwBeginAddr < bytesInBuffer)
+ {
+ bytesInBuffer = objAddr+size-dwBeginAddr;
+ }
+ if (FAILED(g_ExtData->ReadVirtual(TO_CDADDR(dwBeginAddr), objBuffer, (ULONG) bytesInBuffer, NULL)))
+ {
+ BuildError(reason, count, "Object %s: Failed to read members.", DMLObject(objAddr));
+ return false;
+ }
+ }
+ dwAddr1 = objBuffer[((size_t)parm-dwBeginAddr)/sizeof(size_t)];
+ if (dwAddr1) {
+ DWORD_PTR dwChild = dwAddr1;
+ // Try something more efficient than IsObject here. Is the methodtable valid?
+ size_t s;
+ BOOL bPointers;
+ DWORD_PTR dwAddrMethTable;
+ if (FAILED(GetMTOfObject(dwAddr1, &dwAddrMethTable)) ||
+ (GetSizeEfficient(dwAddr1, dwAddrMethTable, FALSE, s, bPointers) == FALSE))
+ {
+ BuildError(reason, count, "object %s: bad member %p at %p", DMLObject(objAddr),
+ SOS_PTR(dwAddr1), SOS_PTR(objAddr+(size_t)parm-objAddr));
+
+ return false;
+ }
+
+ if (IsMTForFreeObj(dwAddrMethTable))
+ {
+ sos::Throw<HeapCorruption>("object %s contains free object %p at %p", DMLObject(objAddr),
+ SOS_PTR(dwAddr1), SOS_PTR(objAddr+(size_t)parm-objAddr));
+ }
+
+ // verify card table
+ if (bCheckCard &&
+ NeedCard(objAddr+(size_t)parm-objAddr,dwChild))
+ {
+ BuildError(reason, count, "Object %s: %s missing card_table entry for %p",
+ DMLObject(objAddr), (dwChild == dwAddr1)? "" : " maybe",
+ SOS_PTR(objAddr+(size_t)parm-objAddr));
+
+ return false;
+ }
+ }
+ parm++;
+ }
+ cur--;
+ CheckInterrupt();
+
+ } while (cur >= last);
+ }
+ else
+ {
+ int cnt = (int) map->GetNumSeries();
+ BYTE** parm = (BYTE**)((objAddr) + cur->startoffset);
+ while ((BYTE*)parm < (BYTE*)((objAddr)+(size)-plug_skew))
+ {
+ for (int __i = 0; __i > cnt; __i--)
+ {
+ CheckInterrupt();
+
+ unsigned skip = cur->val_serie[__i].skip;
+ unsigned nptrs = cur->val_serie[__i].nptrs;
+ BYTE** ppstop = parm + nptrs;
+ do
+ {
+ size_t dwAddr1;
+ // Do we run out of cache?
+ if ((size_t)parm >= dwBeginAddr+bytesInBuffer)
+ {
+ // dwBeginAddr += bytesInBuffer;
+ dwBeginAddr = (size_t)parm;
+ if (dwBeginAddr >= objAddr + size)
+ return true;
+
+ bytesInBuffer = bufferSize;
+ if (objAddr+size-dwBeginAddr < bytesInBuffer)
+ bytesInBuffer = objAddr+size-dwBeginAddr;
+
+ if (FAILED(g_ExtData->ReadVirtual(TO_CDADDR(dwBeginAddr), objBuffer, (ULONG) bytesInBuffer, NULL)))
+ {
+ BuildError(reason, count, "Object %s: Failed to read members.", DMLObject(objAddr));
+ return false;
+ }
+ }
+ dwAddr1 = objBuffer[((size_t)parm-dwBeginAddr)/sizeof(size_t)];
+ {
+ if (dwAddr1)
+ {
+ DWORD_PTR dwChild = dwAddr1;
+ // Try something more efficient than IsObject here. Is the methodtable valid?
+ size_t s;
+ BOOL bPointers;
+ DWORD_PTR dwAddrMethTable;
+ if (FAILED(GetMTOfObject(dwAddr1, &dwAddrMethTable)) ||
+ (GetSizeEfficient(dwAddr1, dwAddrMethTable, FALSE, s, bPointers) == FALSE))
+ {
+ BuildError(reason, count, "Object %s: Bad member %p at %p.\n", DMLObject(objAddr),
+ SOS_PTR(dwAddr1), SOS_PTR(objAddr+(size_t)parm-objAddr));
+
+ return false;
+ }
+
+ if (IsMTForFreeObj(dwAddrMethTable))
+ {
+ BuildError(reason, count, "Object %s contains free object %p at %p.", DMLObject(objAddr),
+ SOS_PTR(dwAddr1), SOS_PTR(objAddr+(size_t)parm-objAddr));
+ return false;
+ }
+
+ // verify card table
+ if (bCheckCard &&
+ NeedCard (objAddr+(size_t)parm-objAddr,dwAddr1))
+ {
+ BuildError(reason, count, "Object %s:%s missing card_table entry for %p",
+ DMLObject(objAddr), (dwChild == dwAddr1) ? "" : " maybe",
+ SOS_PTR(objAddr+(size_t)parm-objAddr));
+
+ return false;
+ }
+ }
+ }
+ parm++;
+ CheckInterrupt();
+ } while (parm < ppstop);
+ parm = (BYTE**)((BYTE*)parm + skip);
+ }
+ }
+ }
+
+ return true;
+}
+
+bool sos::ObjectIterator::Verify(char *reason, size_t count) const
+{
+ try
+ {
+ TADDR mt = mCurrObj.GetMT();
+
+ if (MethodTable::GetFreeMT() == mt)
+ {
+ return true;
+ }
+
+ size_t size = mCurrObj.GetSize();
+ if (size < min_obj_size)
+ {
+ BuildError(reason, count, "Object %s: Size %d is too small.", DMLObject(mCurrObj.GetAddress()), size);
+ return false;
+ }
+
+ if (mCurrObj.GetAddress() + mCurrObj.GetSize() > mSegmentEnd)
+ {
+ BuildError(reason, count, "Object %s is too large. End of segment at %p.", DMLObject(mCurrObj), mSegmentEnd);
+ return false;
+ }
+
+ BOOL bVerifyMember = TRUE;
+
+ // If we requested to verify the object's members, the GC may be in a state where that's not possible.
+ // Here we check to see if the object in question needs to have its members updated. If so, we turn off
+ // verification for the object.
+ BOOL consider_bgc_mark = FALSE, check_current_sweep = FALSE, check_saved_sweep = FALSE;
+ should_check_bgc_mark(mHeaps[mCurrHeap], mSegment, &consider_bgc_mark, &check_current_sweep, &check_saved_sweep);
+ bVerifyMember = fgc_should_consider_object(mHeaps[mCurrHeap], mCurrObj.GetAddress(), mSegment,
+ consider_bgc_mark, check_current_sweep, check_saved_sweep);
+
+ if (bVerifyMember)
+ return VerifyObjectMembers(reason, count);
+ }
+ catch(const sos::Exception &e)
+ {
+ BuildError(reason, count, e.GetMesssage());
+ return false;
+ }
+
+ return true;
+}
+
+bool sos::ObjectIterator::Verify() const
+{
+ char *c = NULL;
+ return Verify(c, 0);
+}
diff --git a/src/ToolBox/SOS/Strike/inc/.gitmirror b/src/ToolBox/SOS/Strike/inc/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/inc/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/ToolBox/SOS/Strike/inc/dbgeng.h b/src/ToolBox/SOS/Strike/inc/dbgeng.h
new file mode 100644
index 0000000000..73e4d19f99
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/inc/dbgeng.h
@@ -0,0 +1,16122 @@
+// 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.
+
+//----------------------------------------------------------------------------
+//
+// Debugger engine interfaces.
+//
+
+//
+//----------------------------------------------------------------------------
+
+#ifndef __DBGENG_H__
+#define __DBGENG_H__
+
+#include <stdarg.h>
+#include <objbase.h>
+
+#ifndef _WDBGEXTS_
+typedef struct _WINDBG_EXTENSION_APIS32* PWINDBG_EXTENSION_APIS32;
+typedef struct _WINDBG_EXTENSION_APIS64* PWINDBG_EXTENSION_APIS64;
+#endif
+
+#ifndef _CRASHLIB_
+typedef struct _MEMORY_BASIC_INFORMATION64* PMEMORY_BASIC_INFORMATION64;
+#endif
+
+#ifndef __specstrings
+// Should include SpecStrings.h to get proper definitions.
+#define __in
+#define __in_opt
+#define __in_bcount(x)
+#define __in_bcount_opt(x)
+#define __in_ecount(x)
+#define __in_ecount_opt(x)
+#define __out
+#define __out_opt
+#define __out_bcount(x)
+#define __out_bcount_opt(x)
+#define __out_ecount(x)
+#define __out_ecount_opt(x)
+#define __out_xcount(x)
+#define __inout
+#define __inout_opt
+#define __reserved
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//----------------------------------------------------------------------------
+//
+// GUIDs and interface forward declarations.
+//
+//----------------------------------------------------------------------------
+
+/* f2df5f53-071f-47bd-9de6-5734c3fed689 */
+DEFINE_GUID(IID_IDebugAdvanced, 0xf2df5f53, 0x071f, 0x47bd,
+ 0x9d, 0xe6, 0x57, 0x34, 0xc3, 0xfe, 0xd6, 0x89);
+/* 716d14c9-119b-4ba5-af1f-0890e672416a */
+DEFINE_GUID(IID_IDebugAdvanced2, 0x716d14c9, 0x119b, 0x4ba5,
+ 0xaf, 0x1f, 0x08, 0x90, 0xe6, 0x72, 0x41, 0x6a);
+/* cba4abb4-84c4-444d-87ca-a04e13286739 */
+DEFINE_GUID(IID_IDebugAdvanced3, 0xcba4abb4, 0x84c4, 0x444d,
+ 0x87, 0xca, 0xa0, 0x4e, 0x13, 0x28, 0x67, 0x39);
+/* 5bd9d474-5975-423a-b88b-65a8e7110e65 */
+DEFINE_GUID(IID_IDebugBreakpoint, 0x5bd9d474, 0x5975, 0x423a,
+ 0xb8, 0x8b, 0x65, 0xa8, 0xe7, 0x11, 0x0e, 0x65);
+/* 1b278d20-79f2-426e-a3f9-c1ddf375d48e */
+DEFINE_GUID(IID_IDebugBreakpoint2, 0x1b278d20, 0x79f2, 0x426e,
+ 0xa3, 0xf9, 0xc1, 0xdd, 0xf3, 0x75, 0xd4, 0x8e);
+/* 27fe5639-8407-4f47-8364-ee118fb08ac8 */
+DEFINE_GUID(IID_IDebugClient, 0x27fe5639, 0x8407, 0x4f47,
+ 0x83, 0x64, 0xee, 0x11, 0x8f, 0xb0, 0x8a, 0xc8);
+/* edbed635-372e-4dab-bbfe-ed0d2f63be81 */
+DEFINE_GUID(IID_IDebugClient2, 0xedbed635, 0x372e, 0x4dab,
+ 0xbb, 0xfe, 0xed, 0x0d, 0x2f, 0x63, 0xbe, 0x81);
+/* dd492d7f-71b8-4ad6-a8dc-1c887479ff91 */
+DEFINE_GUID(IID_IDebugClient3, 0xdd492d7f, 0x71b8, 0x4ad6,
+ 0xa8, 0xdc, 0x1c, 0x88, 0x74, 0x79, 0xff, 0x91);
+/* ca83c3de-5089-4cf8-93c8-d892387f2a5e */
+DEFINE_GUID(IID_IDebugClient4, 0xca83c3de, 0x5089, 0x4cf8,
+ 0x93, 0xc8, 0xd8, 0x92, 0x38, 0x7f, 0x2a, 0x5e);
+/* e3acb9d7-7ec2-4f0c-a0da-e81e0cbbe628 */
+DEFINE_GUID(IID_IDebugClient5, 0xe3acb9d7, 0x7ec2, 0x4f0c,
+ 0xa0, 0xda, 0xe8, 0x1e, 0x0c, 0xbb, 0xe6, 0x28);
+/* 5182e668-105e-416e-ad92-24ef800424ba */
+DEFINE_GUID(IID_IDebugControl, 0x5182e668, 0x105e, 0x416e,
+ 0xad, 0x92, 0x24, 0xef, 0x80, 0x04, 0x24, 0xba);
+/* d4366723-44df-4bed-8c7e-4c05424f4588 */
+DEFINE_GUID(IID_IDebugControl2, 0xd4366723, 0x44df, 0x4bed,
+ 0x8c, 0x7e, 0x4c, 0x05, 0x42, 0x4f, 0x45, 0x88);
+/* 7df74a86-b03f-407f-90ab-a20dadcead08 */
+DEFINE_GUID(IID_IDebugControl3, 0x7df74a86, 0xb03f, 0x407f,
+ 0x90, 0xab, 0xa2, 0x0d, 0xad, 0xce, 0xad, 0x08);
+/* 94e60ce9-9b41-4b19-9fc0-6d9eb35272b3 */
+DEFINE_GUID(IID_IDebugControl4, 0x94e60ce9, 0x9b41, 0x4b19,
+ 0x9f, 0xc0, 0x6d, 0x9e, 0xb3, 0x52, 0x72, 0xb3);
+/* 88f7dfab-3ea7-4c3a-aefb-c4e8106173aa */
+DEFINE_GUID(IID_IDebugDataSpaces, 0x88f7dfab, 0x3ea7, 0x4c3a,
+ 0xae, 0xfb, 0xc4, 0xe8, 0x10, 0x61, 0x73, 0xaa);
+/* 7a5e852f-96e9-468f-ac1b-0b3addc4a049 */
+DEFINE_GUID(IID_IDebugDataSpaces2, 0x7a5e852f, 0x96e9, 0x468f,
+ 0xac, 0x1b, 0x0b, 0x3a, 0xdd, 0xc4, 0xa0, 0x49);
+/* 23f79d6c-8aaf-4f7c-a607-9995f5407e63 */
+DEFINE_GUID(IID_IDebugDataSpaces3, 0x23f79d6c, 0x8aaf, 0x4f7c,
+ 0xa6, 0x07, 0x99, 0x95, 0xf5, 0x40, 0x7e, 0x63);
+/* d98ada1f-29e9-4ef5-a6c0-e53349883212 */
+DEFINE_GUID(IID_IDebugDataSpaces4, 0xd98ada1f, 0x29e9, 0x4ef5,
+ 0xa6, 0xc0, 0xe5, 0x33, 0x49, 0x88, 0x32, 0x12);
+/* 337be28b-5036-4d72-b6bf-c45fbb9f2eaa */
+DEFINE_GUID(IID_IDebugEventCallbacks, 0x337be28b, 0x5036, 0x4d72,
+ 0xb6, 0xbf, 0xc4, 0x5f, 0xbb, 0x9f, 0x2e, 0xaa);
+/* 0690e046-9c23-45ac-a04f-987ac29ad0d3 */
+DEFINE_GUID(IID_IDebugEventCallbacksWide, 0x0690e046, 0x9c23, 0x45ac,
+ 0xa0, 0x4f, 0x98, 0x7a, 0xc2, 0x9a, 0xd0, 0xd3);
+/* 9f50e42c-f136-499e-9a97-73036c94ed2d */
+DEFINE_GUID(IID_IDebugInputCallbacks, 0x9f50e42c, 0xf136, 0x499e,
+ 0x9a, 0x97, 0x73, 0x03, 0x6c, 0x94, 0xed, 0x2d);
+/* 4bf58045-d654-4c40-b0af-683090f356dc */
+DEFINE_GUID(IID_IDebugOutputCallbacks, 0x4bf58045, 0xd654, 0x4c40,
+ 0xb0, 0xaf, 0x68, 0x30, 0x90, 0xf3, 0x56, 0xdc);
+/* 4c7fd663-c394-4e26-8ef1-34ad5ed3764c */
+DEFINE_GUID(IID_IDebugOutputCallbacksWide, 0x4c7fd663, 0xc394, 0x4e26,
+ 0x8e, 0xf1, 0x34, 0xad, 0x5e, 0xd3, 0x76, 0x4c);
+/* 67721fe9-56d2-4a44-a325-2b65513ce6eb */
+DEFINE_GUID(IID_IDebugOutputCallbacks2, 0x67721fe9, 0x56d2, 0x4a44,
+ 0xa3, 0x25, 0x2b, 0x65, 0x51, 0x3c, 0xe6, 0xeb);
+/* ce289126-9e84-45a7-937e-67bb18691493 */
+DEFINE_GUID(IID_IDebugRegisters, 0xce289126, 0x9e84, 0x45a7,
+ 0x93, 0x7e, 0x67, 0xbb, 0x18, 0x69, 0x14, 0x93);
+/* 1656afa9-19c6-4e3a-97e7-5dc9160cf9c4 */
+DEFINE_GUID(IID_IDebugRegisters2, 0x1656afa9, 0x19c6, 0x4e3a,
+ 0x97, 0xe7, 0x5d, 0xc9, 0x16, 0x0c, 0xf9, 0xc4);
+/* f2528316-0f1a-4431-aeed-11d096e1e2ab */
+DEFINE_GUID(IID_IDebugSymbolGroup, 0xf2528316, 0x0f1a, 0x4431,
+ 0xae, 0xed, 0x11, 0xd0, 0x96, 0xe1, 0xe2, 0xab);
+/* 6a7ccc5f-fb5e-4dcc-b41c-6c20307bccc7 */
+DEFINE_GUID(IID_IDebugSymbolGroup2, 0x6a7ccc5f, 0xfb5e, 0x4dcc,
+ 0xb4, 0x1c, 0x6c, 0x20, 0x30, 0x7b, 0xcc, 0xc7);
+/* 8c31e98c-983a-48a5-9016-6fe5d667a950 */
+DEFINE_GUID(IID_IDebugSymbols, 0x8c31e98c, 0x983a, 0x48a5,
+ 0x90, 0x16, 0x6f, 0xe5, 0xd6, 0x67, 0xa9, 0x50);
+/* 3a707211-afdd-4495-ad4f-56fecdf8163f */
+DEFINE_GUID(IID_IDebugSymbols2, 0x3a707211, 0xafdd, 0x4495,
+ 0xad, 0x4f, 0x56, 0xfe, 0xcd, 0xf8, 0x16, 0x3f);
+/* f02fbecc-50ac-4f36-9ad9-c975e8f32ff8 */
+DEFINE_GUID(IID_IDebugSymbols3, 0xf02fbecc, 0x50ac, 0x4f36,
+ 0x9a, 0xd9, 0xc9, 0x75, 0xe8, 0xf3, 0x2f, 0xf8);
+/* 6b86fe2c-2c4f-4f0c-9da2-174311acc327 */
+DEFINE_GUID(IID_IDebugSystemObjects, 0x6b86fe2c, 0x2c4f, 0x4f0c,
+ 0x9d, 0xa2, 0x17, 0x43, 0x11, 0xac, 0xc3, 0x27);
+/* 0ae9f5ff-1852-4679-b055-494bee6407ee */
+DEFINE_GUID(IID_IDebugSystemObjects2, 0x0ae9f5ff, 0x1852, 0x4679,
+ 0xb0, 0x55, 0x49, 0x4b, 0xee, 0x64, 0x07, 0xee);
+/* e9676e2f-e286-4ea3-b0f9-dfe5d9fc330e */
+DEFINE_GUID(IID_IDebugSystemObjects3, 0xe9676e2f, 0xe286, 0x4ea3,
+ 0xb0, 0xf9, 0xdf, 0xe5, 0xd9, 0xfc, 0x33, 0x0e);
+/* 489468e6-7d0f-4af5-87ab-25207454d553 */
+DEFINE_GUID(IID_IDebugSystemObjects4, 0x489468e6, 0x7d0f, 0x4af5,
+ 0x87, 0xab, 0x25, 0x20, 0x74, 0x54, 0xd5, 0x53);
+
+typedef interface DECLSPEC_UUID("f2df5f53-071f-47bd-9de6-5734c3fed689")
+ IDebugAdvanced* PDEBUG_ADVANCED;
+typedef interface DECLSPEC_UUID("716d14c9-119b-4ba5-af1f-0890e672416a")
+ IDebugAdvanced2* PDEBUG_ADVANCED2;
+typedef interface DECLSPEC_UUID("cba4abb4-84c4-444d-87ca-a04e13286739")
+ IDebugAdvanced3* PDEBUG_ADVANCED3;
+typedef interface DECLSPEC_UUID("5bd9d474-5975-423a-b88b-65a8e7110e65")
+ IDebugBreakpoint* PDEBUG_BREAKPOINT;
+typedef interface DECLSPEC_UUID("1b278d20-79f2-426e-a3f9-c1ddf375d48e")
+ IDebugBreakpoint2* PDEBUG_BREAKPOINT2;
+typedef interface DECLSPEC_UUID("27fe5639-8407-4f47-8364-ee118fb08ac8")
+ IDebugClient* PDEBUG_CLIENT;
+typedef interface DECLSPEC_UUID("edbed635-372e-4dab-bbfe-ed0d2f63be81")
+ IDebugClient2* PDEBUG_CLIENT2;
+typedef interface DECLSPEC_UUID("dd492d7f-71b8-4ad6-a8dc-1c887479ff91")
+ IDebugClient3* PDEBUG_CLIENT3;
+typedef interface DECLSPEC_UUID("ca83c3de-5089-4cf8-93c8-d892387f2a5e")
+ IDebugClient4* PDEBUG_CLIENT4;
+typedef interface DECLSPEC_UUID("e3acb9d7-7ec2-4f0c-a0da-e81e0cbbe628")
+ IDebugClient5* PDEBUG_CLIENT5;
+typedef interface DECLSPEC_UUID("5182e668-105e-416e-ad92-24ef800424ba")
+ IDebugControl* PDEBUG_CONTROL;
+typedef interface DECLSPEC_UUID("d4366723-44df-4bed-8c7e-4c05424f4588")
+ IDebugControl2* PDEBUG_CONTROL2;
+typedef interface DECLSPEC_UUID("7df74a86-b03f-407f-90ab-a20dadcead08")
+ IDebugControl3* PDEBUG_CONTROL3;
+typedef interface DECLSPEC_UUID("94e60ce9-9b41-4b19-9fc0-6d9eb35272b3")
+ IDebugControl4* PDEBUG_CONTROL4;
+typedef interface DECLSPEC_UUID("88f7dfab-3ea7-4c3a-aefb-c4e8106173aa")
+ IDebugDataSpaces* PDEBUG_DATA_SPACES;
+typedef interface DECLSPEC_UUID("7a5e852f-96e9-468f-ac1b-0b3addc4a049")
+ IDebugDataSpaces2* PDEBUG_DATA_SPACES2;
+typedef interface DECLSPEC_UUID("23f79d6c-8aaf-4f7c-a607-9995f5407e63")
+ IDebugDataSpaces3* PDEBUG_DATA_SPACES3;
+typedef interface DECLSPEC_UUID("d98ada1f-29e9-4ef5-a6c0-e53349883212")
+ IDebugDataSpaces4* PDEBUG_DATA_SPACES4;
+typedef interface DECLSPEC_UUID("337be28b-5036-4d72-b6bf-c45fbb9f2eaa")
+ IDebugEventCallbacks* PDEBUG_EVENT_CALLBACKS;
+typedef interface DECLSPEC_UUID("0690e046-9c23-45ac-a04f-987ac29ad0d3")
+ IDebugEventCallbacksWide* PDEBUG_EVENT_CALLBACKS_WIDE;
+typedef interface DECLSPEC_UUID("9f50e42c-f136-499e-9a97-73036c94ed2d")
+ IDebugInputCallbacks* PDEBUG_INPUT_CALLBACKS;
+typedef interface DECLSPEC_UUID("4bf58045-d654-4c40-b0af-683090f356dc")
+ IDebugOutputCallbacks* PDEBUG_OUTPUT_CALLBACKS;
+typedef interface DECLSPEC_UUID("4c7fd663-c394-4e26-8ef1-34ad5ed3764c")
+ IDebugOutputCallbacksWide* PDEBUG_OUTPUT_CALLBACKS_WIDE;
+typedef interface DECLSPEC_UUID("67721fe9-56d2-4a44-a325-2b65513ce6eb")
+ IDebugOutputCallbacks2* PDEBUG_OUTPUT_CALLBACKS2;
+typedef interface DECLSPEC_UUID("ce289126-9e84-45a7-937e-67bb18691493")
+ IDebugRegisters* PDEBUG_REGISTERS;
+typedef interface DECLSPEC_UUID("1656afa9-19c6-4e3a-97e7-5dc9160cf9c4")
+ IDebugRegisters2* PDEBUG_REGISTERS2;
+typedef interface DECLSPEC_UUID("f2528316-0f1a-4431-aeed-11d096e1e2ab")
+ IDebugSymbolGroup* PDEBUG_SYMBOL_GROUP;
+typedef interface DECLSPEC_UUID("6a7ccc5f-fb5e-4dcc-b41c-6c20307bccc7")
+ IDebugSymbolGroup2* PDEBUG_SYMBOL_GROUP2;
+typedef interface DECLSPEC_UUID("8c31e98c-983a-48a5-9016-6fe5d667a950")
+ IDebugSymbols* PDEBUG_SYMBOLS;
+typedef interface DECLSPEC_UUID("3a707211-afdd-4495-ad4f-56fecdf8163f")
+ IDebugSymbols2* PDEBUG_SYMBOLS2;
+typedef interface DECLSPEC_UUID("f02fbecc-50ac-4f36-9ad9-c975e8f32ff8")
+ IDebugSymbols3* PDEBUG_SYMBOLS3;
+typedef interface DECLSPEC_UUID("6b86fe2c-2c4f-4f0c-9da2-174311acc327")
+ IDebugSystemObjects* PDEBUG_SYSTEM_OBJECTS;
+typedef interface DECLSPEC_UUID("0ae9f5ff-1852-4679-b055-494bee6407ee")
+ IDebugSystemObjects2* PDEBUG_SYSTEM_OBJECTS2;
+typedef interface DECLSPEC_UUID("e9676e2f-e286-4ea3-b0f9-dfe5d9fc330e")
+ IDebugSystemObjects3* PDEBUG_SYSTEM_OBJECTS3;
+typedef interface DECLSPEC_UUID("489468e6-7d0f-4af5-87ab-25207454d553")
+ IDebugSystemObjects4* PDEBUG_SYSTEM_OBJECTS4;
+
+//----------------------------------------------------------------------------
+//
+// Macros.
+//
+//----------------------------------------------------------------------------
+
+// Extends a 32-bit address into a 64-bit address.
+#define DEBUG_EXTEND64(Addr) ((ULONG64)(LONG64)(LONG)(Addr))
+
+//----------------------------------------------------------------------------
+//
+// Client creation functions.
+//
+//----------------------------------------------------------------------------
+
+// RemoteOptions specifies connection types and
+// their parameters. Supported strings are:
+// npipe:Server=<Machine>,Pipe=<Pipe name>
+// tcp:Server=<Machine>,Port=<IP port>
+STDAPI
+DebugConnect(
+ __in PCSTR RemoteOptions,
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ );
+
+STDAPI
+DebugConnectWide(
+ __in PCWSTR RemoteOptions,
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ );
+
+STDAPI
+DebugCreate(
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ );
+
+//----------------------------------------------------------------------------
+//
+// IDebugAdvanced.
+//
+//----------------------------------------------------------------------------
+
+typedef struct _DEBUG_OFFSET_REGION
+{
+ ULONG64 Base;
+ ULONG64 Size;
+} DEBUG_OFFSET_REGION, *PDEBUG_OFFSET_REGION;
+
+#undef INTERFACE
+#define INTERFACE IDebugAdvanced
+DECLARE_INTERFACE_(IDebugAdvanced, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugAdvanced.
+
+ // Get/SetThreadContext offer control over
+ // the full processor context for a thread.
+ // Higher-level functions, such as the
+ // IDebugRegisters interface, allow similar
+ // access in simpler and more generic ways.
+ // Get/SetThreadContext are useful when
+ // large amounts of thread context must
+ // be changed and processor-specific code
+ // is not a problem.
+ STDMETHOD(GetThreadContext)(
+ THIS_
+ __out_bcount(ContextSize) /* align_is(16) */ PVOID Context,
+ __in ULONG ContextSize
+ ) PURE;
+ STDMETHOD(SetThreadContext)(
+ THIS_
+ __in_bcount(ContextSize) /* align_is(16) */ PVOID Context,
+ __in ULONG ContextSize
+ ) PURE;
+};
+
+typedef struct _DEBUG_READ_USER_MINIDUMP_STREAM
+{
+ IN ULONG StreamType;
+ IN ULONG Flags;
+ IN ULONG64 Offset;
+ OUT PVOID Buffer;
+ IN ULONG BufferSize;
+ OUT ULONG BufferUsed;
+} DEBUG_READ_USER_MINIDUMP_STREAM, *PDEBUG_READ_USER_MINIDUMP_STREAM;
+
+#define DEBUG_GET_TEXT_COMPLETIONS_NO_DOT_COMMANDS 0x00000001
+#define DEBUG_GET_TEXT_COMPLETIONS_NO_EXTENSION_COMMANDS 0x00000002
+#define DEBUG_GET_TEXT_COMPLETIONS_NO_SYMBOLS 0x00000004
+
+typedef struct _DEBUG_GET_TEXT_COMPLETIONS_IN
+{
+ ULONG Flags;
+ ULONG MatchCountLimit;
+ ULONG64 Reserved[3];
+ // Input text string follows.
+} DEBUG_GET_TEXT_COMPLETIONS_IN, *PDEBUG_GET_TEXT_COMPLETIONS_IN;
+
+#define DEBUG_GET_TEXT_COMPLETIONS_IS_DOT_COMMAND 0x00000001
+#define DEBUG_GET_TEXT_COMPLETIONS_IS_EXTENSION_COMMAND 0x00000002
+#define DEBUG_GET_TEXT_COMPLETIONS_IS_SYMBOL 0x00000004
+
+typedef struct _DEBUG_GET_TEXT_COMPLETIONS_OUT
+{
+ ULONG Flags;
+ // Char index in input string where completions start.
+ ULONG ReplaceIndex;
+ ULONG MatchCount;
+ ULONG Reserved1;
+ ULONG64 Reserved2[2];
+ // Completions follow.
+ // Completion data is zero-terminated strings ended
+ // by a final zero double-terminator.
+} DEBUG_GET_TEXT_COMPLETIONS_OUT, *PDEBUG_GET_TEXT_COMPLETIONS_OUT;
+
+typedef struct _DEBUG_CACHED_SYMBOL_INFO
+{
+ ULONG64 ModBase;
+ ULONG64 Arg1;
+ ULONG64 Arg2;
+ ULONG Id;
+ ULONG Arg3;
+} DEBUG_CACHED_SYMBOL_INFO, *PDEBUG_CACHED_SYMBOL_INFO;
+
+//
+// Request requests.
+//
+
+// InBuffer - Unused.
+// OutBuffer - Unused.
+#define DEBUG_REQUEST_SOURCE_PATH_HAS_SOURCE_SERVER 0
+
+// InBuffer - Unused.
+// OutBuffer - Machine-specific CONTEXT.
+#define DEBUG_REQUEST_TARGET_EXCEPTION_CONTEXT 1
+
+// InBuffer - Unused.
+// OutBuffer - ULONG system ID of thread.
+#define DEBUG_REQUEST_TARGET_EXCEPTION_THREAD 2
+
+// InBuffer - Unused.
+// OutBuffer - EXCEPTION_RECORD64.
+#define DEBUG_REQUEST_TARGET_EXCEPTION_RECORD 3
+
+// InBuffer - Unused.
+// OutBuffer - DEBUG_CREATE_PROCESS_OPTIONS.
+#define DEBUG_REQUEST_GET_ADDITIONAL_CREATE_OPTIONS 4
+
+// InBuffer - DEBUG_CREATE_PROCESS_OPTIONS.
+// OutBuffer - Unused.
+#define DEBUG_REQUEST_SET_ADDITIONAL_CREATE_OPTIONS 5
+
+// InBuffer - Unused.
+// OutBuffer - ULONG[2] major/minor.
+#define DEBUG_REQUEST_GET_WIN32_MAJOR_MINOR_VERSIONS 6
+
+// InBuffer - DEBUG_READ_USER_MINIDUMP_STREAM.
+// OutBuffer - Unused.
+#define DEBUG_REQUEST_READ_USER_MINIDUMP_STREAM 7
+
+// InBuffer - Unused.
+// OutBuffer - Unused.
+#define DEBUG_REQUEST_TARGET_CAN_DETACH 8
+
+// InBuffer - PTSTR.
+// OutBuffer - Unused.
+#define DEBUG_REQUEST_SET_LOCAL_IMPLICIT_COMMAND_LINE 9
+
+// InBuffer - Unused.
+// OutBuffer - Event code stream offset.
+#define DEBUG_REQUEST_GET_CAPTURED_EVENT_CODE_OFFSET 10
+
+// InBuffer - Unused.
+// OutBuffer - Event code stream information.
+#define DEBUG_REQUEST_READ_CAPTURED_EVENT_CODE_STREAM 11
+
+// InBuffer - Input data block.
+// OutBuffer - Processed data block.
+#define DEBUG_REQUEST_EXT_TYPED_DATA_ANSI 12
+
+// InBuffer - Unused.
+// OutBuffer - Returned path.
+#define DEBUG_REQUEST_GET_EXTENSION_SEARCH_PATH_WIDE 13
+
+// InBuffer - DEBUG_GET_TEXT_COMPLETIONS_IN.
+// OutBuffer - DEBUG_GET_TEXT_COMPLETIONS_OUT.
+#define DEBUG_REQUEST_GET_TEXT_COMPLETIONS_WIDE 14
+
+// InBuffer - ULONG64 cookie.
+// OutBuffer - DEBUG_CACHED_SYMBOL_INFO.
+#define DEBUG_REQUEST_GET_CACHED_SYMBOL_INFO 15
+
+// InBuffer - DEBUG_CACHED_SYMBOL_INFO.
+// OutBuffer - ULONG64 cookie.
+#define DEBUG_REQUEST_ADD_CACHED_SYMBOL_INFO 16
+
+// InBuffer - ULONG64 cookie.
+// OutBuffer - Unused.
+#define DEBUG_REQUEST_REMOVE_CACHED_SYMBOL_INFO 17
+
+// InBuffer - DEBUG_GET_TEXT_COMPLETIONS_IN.
+// OutBuffer - DEBUG_GET_TEXT_COMPLETIONS_OUT.
+#define DEBUG_REQUEST_GET_TEXT_COMPLETIONS_ANSI 18
+
+// InBuffer - Unused.
+// OutBuffer - Unused.
+#define DEBUG_REQUEST_CURRENT_OUTPUT_CALLBACKS_ARE_DML_AWARE 19
+
+// InBuffer - ULONG64 offset.
+// OutBuffer - Unwind information.
+#define DEBUG_REQUEST_GET_OFFSET_UNWIND_INFORMATION 20
+
+// InBuffer - Unused
+// OutBuffer - returned DUMP_HEADER32/DUMP_HEADER64 structure.
+#define DEBUG_REQUEST_GET_DUMP_HEADER 21
+
+// InBuffer - DUMP_HEADER32/DUMP_HEADER64 structure.
+// OutBuffer - Unused
+#define DEBUG_REQUEST_SET_DUMP_HEADER 22
+
+//
+// GetSourceFileInformation requests.
+//
+
+// Arg64 - Module base.
+// Arg32 - Unused.
+#define DEBUG_SRCFILE_SYMBOL_TOKEN 0
+
+// Arg64 - Module base.
+// Arg32 - Unused.
+#define DEBUG_SRCFILE_SYMBOL_TOKEN_SOURCE_COMMAND_WIDE 1
+
+//
+// GetSymbolInformation requests.
+//
+
+// Arg64 - Unused.
+// Arg32 - Breakpoint ID.
+// Buffer - ULONG line number.
+// String - File name.
+#define DEBUG_SYMINFO_BREAKPOINT_SOURCE_LINE 0
+
+// Arg64 - Module base.
+// Arg32 - Unused.
+// Buffer - IMAGEHLP_MODULEW64.
+// String - Unused.
+#define DEBUG_SYMINFO_IMAGEHLP_MODULEW64 1
+
+// Arg64 - Offset.
+// Arg32 - Symbol tag.
+// Buffer - Unicode symbol name strings. Could have multiple strings.
+// String - Unused, strings are returned in Buffer as there
+// may be more than one.
+#define DEBUG_SYMINFO_GET_SYMBOL_NAME_BY_OFFSET_AND_TAG_WIDE 2
+
+// Arg64 - Module base.
+// Arg32 - Symbol tag.
+// Buffer - Array of symbol addresses.
+// String - Concatenated symbol strings. Individual symbol
+// strings are zero-terminated and the final string in
+// a symbol is double-zero-terminated.
+#define DEBUG_SYMINFO_GET_MODULE_SYMBOL_NAMES_AND_OFFSETS 3
+
+//
+// GetSystemObjectInformation requests.
+//
+
+// Arg64 - Unused.
+// Arg32 - Debugger thread ID.
+// Buffer - DEBUG_THREAD_BASIC_INFORMATION.
+#define DEBUG_SYSOBJINFO_THREAD_BASIC_INFORMATION 0
+
+// Arg64 - Unused.
+// Arg32 - Debugger thread ID.
+// Buffer - Unicode name string.
+#define DEBUG_SYSOBJINFO_THREAD_NAME_WIDE 1
+
+// Arg64 - Unused.
+// Arg32 - Unused.
+// Buffer - ULONG cookie value.
+#define DEBUG_SYSOBJINFO_CURRENT_PROCESS_COOKIE 2
+
+#define DEBUG_TBINFO_EXIT_STATUS 0x00000001
+#define DEBUG_TBINFO_PRIORITY_CLASS 0x00000002
+#define DEBUG_TBINFO_PRIORITY 0x00000004
+#define DEBUG_TBINFO_TIMES 0x00000008
+#define DEBUG_TBINFO_START_OFFSET 0x00000010
+#define DEBUG_TBINFO_AFFINITY 0x00000020
+#define DEBUG_TBINFO_ALL 0x0000003f
+
+typedef struct _DEBUG_THREAD_BASIC_INFORMATION
+{
+ // Valid members have a DEBUG_TBINFO bit set in Valid.
+ ULONG Valid;
+ ULONG ExitStatus;
+ ULONG PriorityClass;
+ ULONG Priority;
+ ULONG64 CreateTime;
+ ULONG64 ExitTime;
+ ULONG64 KernelTime;
+ ULONG64 UserTime;
+ ULONG64 StartOffset;
+ ULONG64 Affinity;
+} DEBUG_THREAD_BASIC_INFORMATION, *PDEBUG_THREAD_BASIC_INFORMATION;
+
+#undef INTERFACE
+#define INTERFACE IDebugAdvanced2
+DECLARE_INTERFACE_(IDebugAdvanced2, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugAdvanced.
+
+ // Get/SetThreadContext offer control over
+ // the full processor context for a thread.
+ // Higher-level functions, such as the
+ // IDebugRegisters interface, allow similar
+ // access in simpler and more generic ways.
+ // Get/SetThreadContext are useful when
+ // large amounts of thread context must
+ // be changed and processor-specific code
+ // is not a problem.
+ STDMETHOD(GetThreadContext)(
+ THIS_
+ __out_bcount(ContextSize) /* align_is(16) */ PVOID Context,
+ __in ULONG ContextSize
+ ) PURE;
+ STDMETHOD(SetThreadContext)(
+ THIS_
+ __in_bcount(ContextSize) /* align_is(16) */ PVOID Context,
+ __in ULONG ContextSize
+ ) PURE;
+
+ // IDebugAdvanced2.
+
+ //
+ // Generalized open-ended methods for querying
+ // and manipulation. The open-ended nature of
+ // these methods makes it easy to add new requests,
+ // although at a cost in convenience of calling.
+ // Sufficiently common requests may have more specific,
+ // simpler methods elsewhere.
+ //
+
+ STDMETHOD(Request)(
+ THIS_
+ __in ULONG Request,
+ __in_bcount_opt(InBufferSize) PVOID InBuffer,
+ __in ULONG InBufferSize,
+ __out_bcount_opt(OutBufferSize) PVOID OutBuffer,
+ __in ULONG OutBufferSize,
+ __out_opt PULONG OutSize
+ ) PURE;
+
+ STDMETHOD(GetSourceFileInformation)(
+ THIS_
+ __in ULONG Which,
+ __in PSTR SourceFile,
+ __in ULONG64 Arg64,
+ __in ULONG Arg32,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG InfoSize
+ ) PURE;
+ STDMETHOD(FindSourceFileAndToken)(
+ THIS_
+ __in ULONG StartElement,
+ __in ULONG64 ModAddr,
+ __in PCSTR File,
+ __in ULONG Flags,
+ __in_bcount_opt(FileTokenSize) PVOID FileToken,
+ __in ULONG FileTokenSize,
+ __out_opt PULONG FoundElement,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG FoundSize
+ ) PURE;
+
+ STDMETHOD(GetSymbolInformation)(
+ THIS_
+ __in ULONG Which,
+ __in ULONG64 Arg64,
+ __in ULONG Arg32,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG InfoSize,
+ __out_ecount_opt(StringBufferSize) PSTR StringBuffer,
+ __in ULONG StringBufferSize,
+ __out_opt PULONG StringSize
+ ) PURE;
+
+ STDMETHOD(GetSystemObjectInformation)(
+ THIS_
+ __in ULONG Which,
+ __in ULONG64 Arg64,
+ __in ULONG Arg32,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG InfoSize
+ ) PURE;
+};
+
+#undef INTERFACE
+#define INTERFACE IDebugAdvanced3
+DECLARE_INTERFACE_(IDebugAdvanced3, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugAdvanced.
+
+ // Get/SetThreadContext offer control over
+ // the full processor context for a thread.
+ // Higher-level functions, such as the
+ // IDebugRegisters interface, allow similar
+ // access in simpler and more generic ways.
+ // Get/SetThreadContext are useful when
+ // large amounts of thread context must
+ // be changed and processor-specific code
+ // is not a problem.
+ STDMETHOD(GetThreadContext)(
+ THIS_
+ __out_bcount(ContextSize) /* align_is(16) */ PVOID Context,
+ __in ULONG ContextSize
+ ) PURE;
+ STDMETHOD(SetThreadContext)(
+ THIS_
+ __in_bcount(ContextSize) /* align_is(16) */ PVOID Context,
+ __in ULONG ContextSize
+ ) PURE;
+
+ // IDebugAdvanced2.
+
+ //
+ // Generalized open-ended methods for querying
+ // and manipulation. The open-ended nature of
+ // these methods makes it easy to add new requests,
+ // although at a cost in convenience of calling.
+ // Sufficiently common requests may have more specific,
+ // simpler methods elsewhere.
+ //
+
+ STDMETHOD(Request)(
+ THIS_
+ __in ULONG Request,
+ __in_bcount_opt(InBufferSize) PVOID InBuffer,
+ __in ULONG InBufferSize,
+ __out_bcount_opt(OutBufferSize) PVOID OutBuffer,
+ __in ULONG OutBufferSize,
+ __out_opt PULONG OutSize
+ ) PURE;
+
+ STDMETHOD(GetSourceFileInformation)(
+ THIS_
+ __in ULONG Which,
+ __in PSTR SourceFile,
+ __in ULONG64 Arg64,
+ __in ULONG Arg32,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG InfoSize
+ ) PURE;
+ STDMETHOD(FindSourceFileAndToken)(
+ THIS_
+ __in ULONG StartElement,
+ __in ULONG64 ModAddr,
+ __in PCSTR File,
+ __in ULONG Flags,
+ __in_bcount_opt(FileTokenSize) PVOID FileToken,
+ __in ULONG FileTokenSize,
+ __out_opt PULONG FoundElement,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG FoundSize
+ ) PURE;
+
+ STDMETHOD(GetSymbolInformation)(
+ THIS_
+ __in ULONG Which,
+ __in ULONG64 Arg64,
+ __in ULONG Arg32,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG InfoSize,
+ __out_ecount_opt(StringBufferSize) PSTR StringBuffer,
+ __in ULONG StringBufferSize,
+ __out_opt PULONG StringSize
+ ) PURE;
+
+ STDMETHOD(GetSystemObjectInformation)(
+ THIS_
+ __in ULONG Which,
+ __in ULONG64 Arg64,
+ __in ULONG Arg32,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG InfoSize
+ ) PURE;
+
+ // IDebugAdvanced3.
+
+ STDMETHOD(GetSourceFileInformationWide)(
+ THIS_
+ __in ULONG Which,
+ __in PWSTR SourceFile,
+ __in ULONG64 Arg64,
+ __in ULONG Arg32,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG InfoSize
+ ) PURE;
+ STDMETHOD(FindSourceFileAndTokenWide)(
+ THIS_
+ __in ULONG StartElement,
+ __in ULONG64 ModAddr,
+ __in PCWSTR File,
+ __in ULONG Flags,
+ __in_bcount_opt(FileTokenSize) PVOID FileToken,
+ __in ULONG FileTokenSize,
+ __out_opt PULONG FoundElement,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG FoundSize
+ ) PURE;
+
+ STDMETHOD(GetSymbolInformationWide)(
+ THIS_
+ __in ULONG Which,
+ __in ULONG64 Arg64,
+ __in ULONG Arg32,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG InfoSize,
+ __out_ecount_opt(StringBufferSize) PWSTR StringBuffer,
+ __in ULONG StringBufferSize,
+ __out_opt PULONG StringSize
+ ) PURE;
+};
+
+//----------------------------------------------------------------------------
+//
+// IDebugBreakpoint.
+//
+//----------------------------------------------------------------------------
+
+// Types of breakpoints.
+#define DEBUG_BREAKPOINT_CODE 0
+#define DEBUG_BREAKPOINT_DATA 1
+#define DEBUG_BREAKPOINT_TIME 2
+
+// Breakpoint flags.
+// Go-only breakpoints are only active when
+// the engine is in unrestricted execution
+// mode. They do not fire when the engine
+// is stepping.
+#define DEBUG_BREAKPOINT_GO_ONLY 0x00000001
+// A breakpoint is flagged as deferred as long as
+// its offset expression cannot be evaluated.
+// A deferred breakpoint is not active.
+#define DEBUG_BREAKPOINT_DEFERRED 0x00000002
+#define DEBUG_BREAKPOINT_ENABLED 0x00000004
+// The adder-only flag does not affect breakpoint
+// operation. It is just a marker to restrict
+// output and notifications for the breakpoint to
+// the client that added the breakpoint. Breakpoint
+// callbacks for adder-only breaks will only be delivered
+// to the adding client. The breakpoint can not
+// be enumerated and accessed by other clients.
+#define DEBUG_BREAKPOINT_ADDER_ONLY 0x00000008
+// One-shot breakpoints automatically clear themselves
+// the first time they are hit.
+#define DEBUG_BREAKPOINT_ONE_SHOT 0x00000010
+
+// Data breakpoint access types.
+// Different architectures support different
+// sets of these bits.
+#define DEBUG_BREAK_READ 0x00000001
+#define DEBUG_BREAK_WRITE 0x00000002
+#define DEBUG_BREAK_EXECUTE 0x00000004
+#define DEBUG_BREAK_IO 0x00000008
+
+// Structure for querying breakpoint information
+// all at once.
+typedef struct _DEBUG_BREAKPOINT_PARAMETERS
+{
+ ULONG64 Offset;
+ ULONG Id;
+ ULONG BreakType;
+ ULONG ProcType;
+ ULONG Flags;
+ ULONG DataSize;
+ ULONG DataAccessType;
+ ULONG PassCount;
+ ULONG CurrentPassCount;
+ ULONG MatchThread;
+ ULONG CommandSize;
+ ULONG OffsetExpressionSize;
+} DEBUG_BREAKPOINT_PARAMETERS, *PDEBUG_BREAKPOINT_PARAMETERS;
+
+#undef INTERFACE
+#define INTERFACE IDebugBreakpoint
+DECLARE_INTERFACE_(IDebugBreakpoint, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugBreakpoint.
+
+ // Retrieves debugger engine unique ID
+ // for the breakpoint. This ID is
+ // fixed as long as the breakpoint exists
+ // but after that may be reused.
+ STDMETHOD(GetId)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ // Retrieves the type of break and
+ // processor type for the breakpoint.
+ STDMETHOD(GetType)(
+ THIS_
+ __out PULONG BreakType,
+ __out PULONG ProcType
+ ) PURE;
+ // Returns the client that called AddBreakpoint.
+ STDMETHOD(GetAdder)(
+ THIS_
+ __out PDEBUG_CLIENT* Adder
+ ) PURE;
+
+ STDMETHOD(GetFlags)(
+ THIS_
+ __out PULONG Flags
+ ) PURE;
+ // Only certain flags can be changed. Flags
+ // are: GO_ONLY, ENABLE.
+ // Sets the given flags.
+ STDMETHOD(AddFlags)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // Clears the given flags.
+ STDMETHOD(RemoveFlags)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // Sets the flags.
+ STDMETHOD(SetFlags)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+
+ // Controls the offset of the breakpoint. The
+ // interpretation of the offset value depends on
+ // the type of breakpoint and its settings. It
+ // may be a code address, a data address, an
+ // I/O port, etc.
+ STDMETHOD(GetOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(SetOffset)(
+ THIS_
+ __in ULONG64 Offset
+ ) PURE;
+
+ // Data breakpoint methods will fail if the
+ // target platform does not support the
+ // parameters used.
+ // These methods only function for breakpoints
+ // created as data breakpoints.
+ STDMETHOD(GetDataParameters)(
+ THIS_
+ __out PULONG Size,
+ __out PULONG AccessType
+ ) PURE;
+ STDMETHOD(SetDataParameters)(
+ THIS_
+ __in ULONG Size,
+ __in ULONG AccessType
+ ) PURE;
+
+ // Pass count defaults to one.
+ STDMETHOD(GetPassCount)(
+ THIS_
+ __out PULONG Count
+ ) PURE;
+ STDMETHOD(SetPassCount)(
+ THIS_
+ __in ULONG Count
+ ) PURE;
+ // Gets the current number of times
+ // the breakpoint has been hit since
+ // it was last triggered.
+ STDMETHOD(GetCurrentPassCount)(
+ THIS_
+ __out PULONG Count
+ ) PURE;
+
+ // If a match thread is set this breakpoint will
+ // only trigger if it occurs on the match thread.
+ // Otherwise it triggers for all threads.
+ // Thread restrictions are not currently supported
+ // in kernel mode.
+ STDMETHOD(GetMatchThreadId)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(SetMatchThreadId)(
+ THIS_
+ __in ULONG Thread
+ ) PURE;
+
+ // The command for a breakpoint is automatically
+ // executed by the engine before the event
+ // is propagated. If the breakpoint continues
+ // execution the event will begin with a continue
+ // status. If the breakpoint does not continue
+ // the event will begin with a break status.
+ // This allows breakpoint commands to participate
+ // in the normal event status voting.
+ // Breakpoint commands are only executed until
+ // the first command that alters the execution
+ // status, such as g, p and t.
+ STDMETHOD(GetCommand)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG CommandSize
+ ) PURE;
+ STDMETHOD(SetCommand)(
+ THIS_
+ __in PCSTR Command
+ ) PURE;
+
+ // Offset expressions are evaluated immediately
+ // and at module load and unload events. If the
+ // evaluation is successful the breakpoints
+ // offset is updated and the breakpoint is
+ // handled normally. If the expression cannot
+ // be evaluated the breakpoint is deferred.
+ // Currently the only offset expression
+ // supported is a module-relative symbol
+ // of the form <Module>!<Symbol>.
+ STDMETHOD(GetOffsetExpression)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ExpressionSize
+ ) PURE;
+ STDMETHOD(SetOffsetExpression)(
+ THIS_
+ __in PCSTR Expression
+ ) PURE;
+
+ STDMETHOD(GetParameters)(
+ THIS_
+ __out PDEBUG_BREAKPOINT_PARAMETERS Params
+ ) PURE;
+};
+
+#undef INTERFACE
+#define INTERFACE IDebugBreakpoint2
+DECLARE_INTERFACE_(IDebugBreakpoint2, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugBreakpoint.
+
+ // Retrieves debugger engine unique ID
+ // for the breakpoint. This ID is
+ // fixed as long as the breakpoint exists
+ // but after that may be reused.
+ STDMETHOD(GetId)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ // Retrieves the type of break and
+ // processor type for the breakpoint.
+ STDMETHOD(GetType)(
+ THIS_
+ __out PULONG BreakType,
+ __out PULONG ProcType
+ ) PURE;
+ // Returns the client that called AddBreakpoint.
+ STDMETHOD(GetAdder)(
+ THIS_
+ __out PDEBUG_CLIENT* Adder
+ ) PURE;
+
+ STDMETHOD(GetFlags)(
+ THIS_
+ __out PULONG Flags
+ ) PURE;
+ // Only certain flags can be changed. Flags
+ // are: GO_ONLY, ENABLE.
+ // Sets the given flags.
+ STDMETHOD(AddFlags)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // Clears the given flags.
+ STDMETHOD(RemoveFlags)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // Sets the flags.
+ STDMETHOD(SetFlags)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+
+ // Controls the offset of the breakpoint. The
+ // interpretation of the offset value depends on
+ // the type of breakpoint and its settings. It
+ // may be a code address, a data address, an
+ // I/O port, etc.
+ STDMETHOD(GetOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(SetOffset)(
+ THIS_
+ __in ULONG64 Offset
+ ) PURE;
+
+ // Data breakpoint methods will fail if the
+ // target platform does not support the
+ // parameters used.
+ // These methods only function for breakpoints
+ // created as data breakpoints.
+ STDMETHOD(GetDataParameters)(
+ THIS_
+ __out PULONG Size,
+ __out PULONG AccessType
+ ) PURE;
+ STDMETHOD(SetDataParameters)(
+ THIS_
+ __in ULONG Size,
+ __in ULONG AccessType
+ ) PURE;
+
+ // Pass count defaults to one.
+ STDMETHOD(GetPassCount)(
+ THIS_
+ __out PULONG Count
+ ) PURE;
+ STDMETHOD(SetPassCount)(
+ THIS_
+ __in ULONG Count
+ ) PURE;
+ // Gets the current number of times
+ // the breakpoint has been hit since
+ // it was last triggered.
+ STDMETHOD(GetCurrentPassCount)(
+ THIS_
+ __out PULONG Count
+ ) PURE;
+
+ // If a match thread is set this breakpoint will
+ // only trigger if it occurs on the match thread.
+ // Otherwise it triggers for all threads.
+ // Thread restrictions are not currently supported
+ // in kernel mode.
+ STDMETHOD(GetMatchThreadId)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(SetMatchThreadId)(
+ THIS_
+ __in ULONG Thread
+ ) PURE;
+
+ // The command for a breakpoint is automatically
+ // executed by the engine before the event
+ // is propagated. If the breakpoint continues
+ // execution the event will begin with a continue
+ // status. If the breakpoint does not continue
+ // the event will begin with a break status.
+ // This allows breakpoint commands to participate
+ // in the normal event status voting.
+ // Breakpoint commands are only executed until
+ // the first command that alters the execution
+ // status, such as g, p and t.
+ STDMETHOD(GetCommand)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG CommandSize
+ ) PURE;
+ STDMETHOD(SetCommand)(
+ THIS_
+ __in PCSTR Command
+ ) PURE;
+
+ // Offset expressions are evaluated immediately
+ // and at module load and unload events. If the
+ // evaluation is successful the breakpoints
+ // offset is updated and the breakpoint is
+ // handled normally. If the expression cannot
+ // be evaluated the breakpoint is deferred.
+ // Currently the only offset expression
+ // supported is a module-relative symbol
+ // of the form <Module>!<Symbol>.
+ STDMETHOD(GetOffsetExpression)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ExpressionSize
+ ) PURE;
+ STDMETHOD(SetOffsetExpression)(
+ THIS_
+ __in PCSTR Expression
+ ) PURE;
+
+ STDMETHOD(GetParameters)(
+ THIS_
+ __out PDEBUG_BREAKPOINT_PARAMETERS Params
+ ) PURE;
+
+ // IDebugBreakpoint2.
+
+ STDMETHOD(GetCommandWide)(
+ THIS_
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG CommandSize
+ ) PURE;
+ STDMETHOD(SetCommandWide)(
+ THIS_
+ __in PCWSTR Command
+ ) PURE;
+
+ STDMETHOD(GetOffsetExpressionWide)(
+ THIS_
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ExpressionSize
+ ) PURE;
+ STDMETHOD(SetOffsetExpressionWide)(
+ THIS_
+ __in PCWSTR Expression
+ ) PURE;
+};
+
+//----------------------------------------------------------------------------
+//
+// IDebugClient.
+//
+//----------------------------------------------------------------------------
+
+// Kernel attach flags.
+#define DEBUG_ATTACH_KERNEL_CONNECTION 0x00000000
+// Attach to the local machine. If this flag is not set
+// a connection is made to a separate target machine using
+// the given connection options.
+#define DEBUG_ATTACH_LOCAL_KERNEL 0x00000001
+// Attach to an eXDI driver.
+#define DEBUG_ATTACH_EXDI_DRIVER 0x00000002
+
+// GetRunningProcessSystemIdByExecutableName flags.
+// By default the match allows a tail match on
+// just the filename. The match returns the first hit
+// even if multiple matches exist.
+#define DEBUG_GET_PROC_DEFAULT 0x00000000
+// The name must match fully.
+#define DEBUG_GET_PROC_FULL_MATCH 0x00000001
+// The match must be the only match.
+#define DEBUG_GET_PROC_ONLY_MATCH 0x00000002
+// The name is a service name instead of an executable name.
+#define DEBUG_GET_PROC_SERVICE_NAME 0x00000004
+
+// GetRunningProcessDescription flags.
+#define DEBUG_PROC_DESC_DEFAULT 0x00000000
+// Return only filenames, not full paths.
+#define DEBUG_PROC_DESC_NO_PATHS 0x00000001
+// Dont look up service names.
+#define DEBUG_PROC_DESC_NO_SERVICES 0x00000002
+// Dont look up MTS package names.
+#define DEBUG_PROC_DESC_NO_MTS_PACKAGES 0x00000004
+// Dont retrieve the command line.
+#define DEBUG_PROC_DESC_NO_COMMAND_LINE 0x00000008
+// Dont retrieve the session ID.
+#define DEBUG_PROC_DESC_NO_SESSION_ID 0x00000010
+// Dont retrieve the process's user name.
+#define DEBUG_PROC_DESC_NO_USER_NAME 0x00000020
+
+//
+// Attach flags.
+//
+
+// Call DebugActiveProcess when attaching.
+#define DEBUG_ATTACH_DEFAULT 0x00000000
+// When attaching to a process just examine
+// the process state and suspend the threads.
+// DebugActiveProcess is not called so the process
+// is not actually being debugged. This is useful
+// for debugging processes holding locks which
+// interfere with the operation of DebugActiveProcess
+// or in situations where it is not desirable to
+// actually set up as a debugger.
+#define DEBUG_ATTACH_NONINVASIVE 0x00000001
+// Attempt to attach to a process that was abandoned
+// when being debugged. This is only supported in
+// some system versions.
+// This flag also allows multiple debuggers to
+// attach to the same process, which can result
+// in numerous problems unless very carefully
+// managed.
+#define DEBUG_ATTACH_EXISTING 0x00000002
+// When attaching non-invasively, do not suspend
+// threads. It is the callers responsibility
+// to either suspend the threads itself or be
+// aware that the attach state may not reflect
+// the current state of the process if threads
+// are still running.
+#define DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND 0x00000004
+// When doing an invasive attach do not inject
+// a break-in thread to generate the initial break-in
+// event. This can be useful to save resources when
+// an initial break is not necessary or when injecting
+// a thread might affect the debuggee's state. This
+// option is only supported on Windows XP and above.
+#define DEBUG_ATTACH_INVASIVE_NO_INITIAL_BREAK 0x00000008
+// When doing an invasive attach resume all threads at the
+// time of attach. This makes it possible to attach
+// to a process created suspended and cause it to start running.
+#define DEBUG_ATTACH_INVASIVE_RESUME_PROCESS 0x00000010
+// When doing a non-invasive attach the engine must
+// recover information for all debuggee elements. The
+// engine may not have permissions for all elements,
+// for example it may not be able to open all threads,
+// and that would ordinarily block the attach. This
+// flag allows unusable elements to be ignored.
+#define DEBUG_ATTACH_NONINVASIVE_ALLOW_PARTIAL 0x00000020
+
+
+//
+// Process creation flags to merge with Win32 flags.
+//
+
+// On Windows XP this flag prevents the debug
+// heap from being used in the new process.
+#define DEBUG_CREATE_PROCESS_NO_DEBUG_HEAP CREATE_UNICODE_ENVIRONMENT
+// Indicates that the native NT RTL process creation
+// routines should be used instead of Win32. This
+// is only meaningful for special processes that run
+// as NT native processes.
+#define DEBUG_CREATE_PROCESS_THROUGH_RTL STACK_SIZE_PARAM_IS_A_RESERVATION
+
+//
+// Process creation flags specific to the debugger engine.
+//
+
+#define DEBUG_ECREATE_PROCESS_DEFAULT 0x00000000
+#define DEBUG_ECREATE_PROCESS_INHERIT_HANDLES 0x00000001
+#define DEBUG_ECREATE_PROCESS_USE_VERIFIER_FLAGS 0x00000002
+#define DEBUG_ECREATE_PROCESS_USE_IMPLICIT_COMMAND_LINE 0x00000004
+
+typedef struct _DEBUG_CREATE_PROCESS_OPTIONS
+{
+ // Win32 create flags.
+ ULONG CreateFlags;
+ // DEBUG_ECREATE_PROCESS_* flags.
+ ULONG EngCreateFlags;
+ // Application Verifier flags,
+ // if DEBUG_ECREATE_PROCESS_USE_VERIFIER_FLAGS is set.
+ ULONG VerifierFlags;
+ // Must be zero.
+ ULONG Reserved;
+} DEBUG_CREATE_PROCESS_OPTIONS, *PDEBUG_CREATE_PROCESS_OPTIONS;
+
+//
+// Process options.
+//
+
+// Indicates that the debuggee process should be
+// automatically detached when the debugger exits.
+// A debugger can explicitly detach on exit or this
+// flag can be set so that detach occurs regardless
+// of how the debugger exits.
+// This is only supported on some system versions.
+#define DEBUG_PROCESS_DETACH_ON_EXIT 0x00000001
+// Indicates that processes created by the current
+// process should not be debugged.
+// Modifying this flag is only supported on some
+// system versions.
+#define DEBUG_PROCESS_ONLY_THIS_PROCESS 0x00000002
+
+// ConnectSession flags.
+// Default connect.
+#define DEBUG_CONNECT_SESSION_DEFAULT 0x00000000
+// Do not output the debugger version.
+#define DEBUG_CONNECT_SESSION_NO_VERSION 0x00000001
+// Do not announce the connection.
+#define DEBUG_CONNECT_SESSION_NO_ANNOUNCE 0x00000002
+
+// OutputServers flags.
+// Debugger servers from StartSever.
+#define DEBUG_SERVERS_DEBUGGER 0x00000001
+// Process servers from StartProcessServer.
+#define DEBUG_SERVERS_PROCESS 0x00000002
+#define DEBUG_SERVERS_ALL 0x00000003
+
+// EndSession flags.
+// Perform cleanup for the session.
+#define DEBUG_END_PASSIVE 0x00000000
+// Actively terminate the session and then perform cleanup.
+#define DEBUG_END_ACTIVE_TERMINATE 0x00000001
+// If possible, detach from all processes and then perform cleanup.
+#define DEBUG_END_ACTIVE_DETACH 0x00000002
+// Perform whatever cleanup is possible that doesn't require
+// acquiring any locks. This is useful for situations where
+// a thread is currently using the engine but the application
+// needs to exit and still wants to give the engine
+// the opportunity to clean up as much as possible.
+// This may leave the engine in an indeterminate state so
+// further engine calls should not be made.
+// When making a reentrant EndSession call from a remote
+// client it is the callers responsibility to ensure
+// that the server can process the request. It is best
+// to avoid making such calls.
+#define DEBUG_END_REENTRANT 0x00000003
+// Notify a server that a remote client is disconnecting.
+// This isnt required but if it isnt called then
+// no disconnect messages will be generated by the server.
+#define DEBUG_END_DISCONNECT 0x00000004
+
+// 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
+
+// Internal debugger output, used mainly
+// for debugging the debugger. Output
+// may only occur in debug builds.
+// KD protocol output.
+#define DEBUG_IOUTPUT_KD_PROTOCOL 0x80000000
+// Remoting output.
+#define DEBUG_IOUTPUT_REMOTING 0x40000000
+// Breakpoint output.
+#define DEBUG_IOUTPUT_BREAKPOINT 0x20000000
+// Event output.
+#define DEBUG_IOUTPUT_EVENT 0x10000000
+
+// OutputIdentity flags.
+#define DEBUG_OUTPUT_IDENTITY_DEFAULT 0x00000000
+
+#undef INTERFACE
+#define INTERFACE IDebugClient
+DECLARE_INTERFACE_(IDebugClient, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugClient.
+
+ // The following set of methods start
+ // the different kinds of debuggees.
+
+ // Begins a debug session using the kernel
+ // debugging protocol. This method selects
+ // the protocol as the debuggee communication
+ // mechanism but does not initiate the communication
+ // itself.
+ STDMETHOD(AttachKernel)(
+ THIS_
+ __in ULONG Flags,
+ __in_opt PCSTR ConnectOptions
+ ) PURE;
+ STDMETHOD(GetKernelConnectionOptions)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG OptionsSize
+ ) PURE;
+ // Updates the connection options for a live
+ // kernel connection. This can only be used
+ // to modify parameters for the connection, not
+ // to switch to a completely different kind of
+ // connection.
+ // This method is reentrant.
+ STDMETHOD(SetKernelConnectionOptions)(
+ THIS_
+ __in PCSTR Options
+ ) PURE;
+
+ // Starts a process server for remote
+ // user-mode process control.
+ // The local process server is server zero.
+ STDMETHOD(StartProcessServer)(
+ THIS_
+ __in ULONG Flags,
+ __in PCSTR Options,
+ __in_opt __reserved PVOID Reserved
+ ) PURE;
+ STDMETHOD(ConnectProcessServer)(
+ THIS_
+ __in PCSTR RemoteOptions,
+ __out PULONG64 Server
+ ) PURE;
+ STDMETHOD(DisconnectProcessServer)(
+ THIS_
+ __in ULONG64 Server
+ ) PURE;
+
+ // Enumerates and describes processes
+ // accessible through the given process server.
+ STDMETHOD(GetRunningProcessSystemIds)(
+ THIS_
+ __in ULONG64 Server,
+ __out_ecount_opt(Count) PULONG Ids,
+ __in ULONG Count,
+ __out_opt PULONG ActualCount
+ ) PURE;
+ STDMETHOD(GetRunningProcessSystemIdByExecutableName)(
+ THIS_
+ __in ULONG64 Server,
+ __in PCSTR ExeName,
+ __in ULONG Flags,
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(GetRunningProcessDescription)(
+ THIS_
+ __in ULONG64 Server,
+ __in ULONG SystemId,
+ __in ULONG Flags,
+ __out_ecount_opt(ExeNameSize) PSTR ExeName,
+ __in ULONG ExeNameSize,
+ __out_opt PULONG ActualExeNameSize,
+ __out_ecount_opt(DescriptionSize) PSTR Description,
+ __in ULONG DescriptionSize,
+ __out_opt PULONG ActualDescriptionSize
+ ) PURE;
+
+ // Attaches to a running user-mode process.
+ STDMETHOD(AttachProcess)(
+ THIS_
+ __in ULONG64 Server,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+ // Creates a new user-mode process for debugging.
+ // CreateFlags are as given to Win32s CreateProcess.
+ // One of DEBUG_PROCESS or DEBUG_ONLY_THIS_PROCESS
+ // must be specified.
+ STDMETHOD(CreateProcess)(
+ THIS_
+ __in ULONG64 Server,
+ __in PSTR CommandLine,
+ __in ULONG CreateFlags
+ ) PURE;
+ // Creates or attaches to a user-mode process, or both.
+ // If CommandLine is NULL this method operates as
+ // AttachProcess does. If ProcessId is zero it
+ // operates as CreateProcess does. If CommandLine is
+ // non-NULL and ProcessId is non-zero the method first
+ // starts a process with the given information but
+ // in a suspended state. The engine then attaches to
+ // the indicated process. Once the attach is successful
+ // the suspended process is resumed. This provides
+ // synchronization between the new process and the
+ // attachment.
+ STDMETHOD(CreateProcessAndAttach)(
+ THIS_
+ __in ULONG64 Server,
+ __in_opt PSTR CommandLine,
+ __in ULONG CreateFlags,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+ // Gets and sets process control flags.
+ STDMETHOD(GetProcessOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ // Opens any kind of user- or kernel-mode dump file
+ // and begins a debug session with the information
+ // contained within it.
+ STDMETHOD(OpenDumpFile)(
+ THIS_
+ __in PCSTR DumpFile
+ ) PURE;
+ // Writes a dump file from the current session information.
+ // The kind of dump file written is determined by the
+ // kind of session and the type qualifier given.
+ // For example, if the current session is a kernel
+ // debug session (DEBUG_CLASS_KERNEL) and the qualifier
+ // is DEBUG_DUMP_SMALL a small kernel dump will be written.
+ STDMETHOD(WriteDumpFile)(
+ THIS_
+ __in PCSTR DumpFile,
+ __in ULONG Qualifier
+ ) PURE;
+
+ // Indicates that a remote client is ready to
+ // begin participating in the current session.
+ // HistoryLimit gives a character limit on
+ // the amount of output history to be sent.
+ STDMETHOD(ConnectSession)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG HistoryLimit
+ ) PURE;
+ // Indicates that the engine should start accepting
+ // remote connections. Options specifies connection types
+ // and their parameters. Supported strings are:
+ // npipe:Pipe=<Pipe name>
+ // tcp:Port=<IP port>
+ STDMETHOD(StartServer)(
+ THIS_
+ __in PCSTR Options
+ ) PURE;
+ // List the servers running on the given machine.
+ // Uses the line prefix.
+ STDMETHOD(OutputServers)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCSTR Machine,
+ __in ULONG Flags
+ ) PURE;
+
+ // Attempts to terminate all processes in the debuggers list.
+ STDMETHOD(TerminateProcesses)(
+ THIS
+ ) PURE;
+ // Attempts to detach from all processes in the debuggers list.
+ // This requires OS support for debugger detach.
+ STDMETHOD(DetachProcesses)(
+ THIS
+ ) PURE;
+ // Stops the current debug session. If a process
+ // was created or attached an active EndSession can
+ // terminate or detach from it.
+ // If a kernel connection was opened it will be closed but the
+ // target machine is otherwise unaffected.
+ STDMETHOD(EndSession)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // If a process was started and ran to completion
+ // this method can be used to retrieve its exit code.
+ STDMETHOD(GetExitCode)(
+ THIS_
+ __out PULONG Code
+ ) PURE;
+
+ // Client event callbacks are called on the thread
+ // of the client. In order to give thread
+ // execution to the engine for callbacks all
+ // client threads should call DispatchCallbacks
+ // when they are idle. Callbacks are only
+ // received when a thread calls DispatchCallbacks
+ // or WaitForEvent. WaitForEvent can only be
+ // called by the thread that started the debug
+ // session so all other client threads should
+ // call DispatchCallbacks when possible.
+ // DispatchCallbacks returns when ExitDispatch is used
+ // to interrupt dispatch or when the timeout expires.
+ // DispatchCallbacks dispatches callbacks for all
+ // clients associated with the thread calling
+ // DispatchCallbacks.
+ // DispatchCallbacks returns S_FALSE when the
+ // timeout expires.
+ STDMETHOD(DispatchCallbacks)(
+ THIS_
+ __in ULONG Timeout
+ ) PURE;
+ // ExitDispatch can be used to interrupt callback
+ // dispatch when a client thread is needed by the
+ // client. This method is reentrant and can
+ // be called from any thread.
+ STDMETHOD(ExitDispatch)(
+ THIS_
+ __in PDEBUG_CLIENT Client
+ ) PURE;
+
+ // Clients are specific to the thread that
+ // created them. Calls from other threads
+ // fail immediately. The CreateClient method
+ // is a notable exception; it allows creation
+ // of a new client for a new thread.
+ STDMETHOD(CreateClient)(
+ THIS_
+ __out PDEBUG_CLIENT* Client
+ ) PURE;
+
+ STDMETHOD(GetInputCallbacks)(
+ THIS_
+ __out PDEBUG_INPUT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetInputCallbacks)(
+ THIS_
+ __in_opt PDEBUG_INPUT_CALLBACKS Callbacks
+ ) PURE;
+
+ // Output callback interfaces are described separately.
+ STDMETHOD(GetOutputCallbacks)(
+ THIS_
+ __out PDEBUG_OUTPUT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetOutputCallbacks)(
+ THIS_
+ __in_opt PDEBUG_OUTPUT_CALLBACKS Callbacks
+ ) PURE;
+ // Output flags provide control over
+ // the distribution of output among clients.
+ // Output masks select which output streams
+ // should be sent to the output callbacks.
+ // Only Output calls with a mask that
+ // contains one of the output mask bits
+ // will be sent to the output callbacks.
+ // These methods are reentrant.
+ // If such access is not synchronized
+ // disruptions in output may occur.
+ STDMETHOD(GetOutputMask)(
+ THIS_
+ __out PULONG Mask
+ ) PURE;
+ STDMETHOD(SetOutputMask)(
+ THIS_
+ __in ULONG Mask
+ ) PURE;
+ // These methods allow access to another clients
+ // output mask. They are necessary for changing
+ // a clients output mask when it is
+ // waiting for events. These methods are reentrant
+ // and can be called from any thread.
+ STDMETHOD(GetOtherOutputMask)(
+ THIS_
+ __in PDEBUG_CLIENT Client,
+ __out PULONG Mask
+ ) PURE;
+ STDMETHOD(SetOtherOutputMask)(
+ THIS_
+ __in PDEBUG_CLIENT Client,
+ __in ULONG Mask
+ ) PURE;
+ // Control the width of an output line for
+ // commands which produce formatted output.
+ // This setting is just a suggestion.
+ STDMETHOD(GetOutputWidth)(
+ THIS_
+ __out PULONG Columns
+ ) PURE;
+ STDMETHOD(SetOutputWidth)(
+ THIS_
+ __in ULONG Columns
+ ) PURE;
+ // Some of the engines output commands produce
+ // multiple lines of output. A prefix can be
+ // set that the engine will automatically output
+ // for each line in that case, allowing a caller
+ // to control indentation or identifying marks.
+ // This is not a general setting for any output
+ // with a newline in it. Methods which use
+ // the line prefix are marked in their documentation.
+ STDMETHOD(GetOutputLinePrefix)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PrefixSize
+ ) PURE;
+ STDMETHOD(SetOutputLinePrefix)(
+ THIS_
+ __in_opt PCSTR Prefix
+ ) PURE;
+
+ // Returns a string describing the machine
+ // and user this client represents. The
+ // specific content of the string varies
+ // with operating system. If the client is
+ // remotely connected some network information
+ // may also be present.
+ STDMETHOD(GetIdentity)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG IdentitySize
+ ) PURE;
+ // Format is a printf-like format string
+ // with one %s where the identity string should go.
+ STDMETHOD(OutputIdentity)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags,
+ __in PCSTR Format
+ ) PURE;
+
+ // Event callbacks allow a client to
+ // receive notification about changes
+ // during the debug session.
+ STDMETHOD(GetEventCallbacks)(
+ THIS_
+ __out PDEBUG_EVENT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetEventCallbacks)(
+ THIS_
+ __in_opt PDEBUG_EVENT_CALLBACKS Callbacks
+ ) PURE;
+
+ // The engine sometimes merges compatible callback
+ // requests to reduce callback overhead. This is
+ // most noticeable with output as small pieces of
+ // output are collected into larger groups to
+ // reduce the overall number of output callback calls.
+ // A client can use this method to force all pending
+ // callbacks to be delivered. This is rarely necessary.
+ STDMETHOD(FlushCallbacks)(
+ THIS
+ ) PURE;
+};
+
+// Per-dump-format control flags.
+#define DEBUG_FORMAT_DEFAULT 0x00000000
+// When creating a CAB with secondary images do searches
+// for all image files, regardless of whether they're
+// needed for the current session or not.
+#define DEBUG_FORMAT_CAB_SECONDARY_ALL_IMAGES 0x10000000
+// Write dump to a temporary file, then package it
+// into a CAB file and delete the temporary file.
+#define DEBUG_FORMAT_WRITE_CAB 0x20000000
+// When creating a CAB add secondary files such as
+// current symbols and mapped images.
+#define DEBUG_FORMAT_CAB_SECONDARY_FILES 0x40000000
+// Don't overwrite existing files.
+#define DEBUG_FORMAT_NO_OVERWRITE 0x80000000
+
+#define DEBUG_FORMAT_USER_SMALL_FULL_MEMORY 0x00000001
+#define DEBUG_FORMAT_USER_SMALL_HANDLE_DATA 0x00000002
+#define DEBUG_FORMAT_USER_SMALL_UNLOADED_MODULES 0x00000004
+#define DEBUG_FORMAT_USER_SMALL_INDIRECT_MEMORY 0x00000008
+#define DEBUG_FORMAT_USER_SMALL_DATA_SEGMENTS 0x00000010
+#define DEBUG_FORMAT_USER_SMALL_FILTER_MEMORY 0x00000020
+#define DEBUG_FORMAT_USER_SMALL_FILTER_PATHS 0x00000040
+#define DEBUG_FORMAT_USER_SMALL_PROCESS_THREAD_DATA 0x00000080
+#define DEBUG_FORMAT_USER_SMALL_PRIVATE_READ_WRITE_MEMORY 0x00000100
+#define DEBUG_FORMAT_USER_SMALL_NO_OPTIONAL_DATA 0x00000200
+#define DEBUG_FORMAT_USER_SMALL_FULL_MEMORY_INFO 0x00000400
+#define DEBUG_FORMAT_USER_SMALL_THREAD_INFO 0x00000800
+#define DEBUG_FORMAT_USER_SMALL_CODE_SEGMENTS 0x00001000
+#define DEBUG_FORMAT_USER_SMALL_NO_AUXILIARY_STATE 0x00002000
+#define DEBUG_FORMAT_USER_SMALL_FULL_AUXILIARY_STATE 0x00004000
+
+//
+// Dump information file types.
+//
+
+// Base dump file, returned when querying for dump files.
+#define DEBUG_DUMP_FILE_BASE 0xffffffff
+// Single file containing packed page file information.
+#define DEBUG_DUMP_FILE_PAGE_FILE_DUMP 0x00000000
+
+#undef INTERFACE
+#define INTERFACE IDebugClient2
+DECLARE_INTERFACE_(IDebugClient2, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugClient.
+
+ // The following set of methods start
+ // the different kinds of debuggees.
+
+ // Begins a debug session using the kernel
+ // debugging protocol. This method selects
+ // the protocol as the debuggee communication
+ // mechanism but does not initiate the communication
+ // itself.
+ STDMETHOD(AttachKernel)(
+ THIS_
+ __in ULONG Flags,
+ __in_opt PCSTR ConnectOptions
+ ) PURE;
+ STDMETHOD(GetKernelConnectionOptions)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG OptionsSize
+ ) PURE;
+ // Updates the connection options for a live
+ // kernel connection. This can only be used
+ // to modify parameters for the connection, not
+ // to switch to a completely different kind of
+ // connection.
+ // This method is reentrant.
+ STDMETHOD(SetKernelConnectionOptions)(
+ THIS_
+ __in PCSTR Options
+ ) PURE;
+
+ // Starts a process server for remote
+ // user-mode process control.
+ // The local process server is server zero.
+ STDMETHOD(StartProcessServer)(
+ THIS_
+ __in ULONG Flags,
+ __in PCSTR Options,
+ __in_opt __reserved PVOID Reserved
+ ) PURE;
+ STDMETHOD(ConnectProcessServer)(
+ THIS_
+ __in PCSTR RemoteOptions,
+ __out PULONG64 Server
+ ) PURE;
+ STDMETHOD(DisconnectProcessServer)(
+ THIS_
+ __in ULONG64 Server
+ ) PURE;
+
+ // Enumerates and describes processes
+ // accessible through the given process server.
+ STDMETHOD(GetRunningProcessSystemIds)(
+ THIS_
+ __in ULONG64 Server,
+ __out_ecount_opt(Count) PULONG Ids,
+ __in ULONG Count,
+ __out_opt PULONG ActualCount
+ ) PURE;
+ STDMETHOD(GetRunningProcessSystemIdByExecutableName)(
+ THIS_
+ __in ULONG64 Server,
+ __in PCSTR ExeName,
+ __in ULONG Flags,
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(GetRunningProcessDescription)(
+ THIS_
+ __in ULONG64 Server,
+ __in ULONG SystemId,
+ __in ULONG Flags,
+ __out_ecount_opt(ExeNameSize) PSTR ExeName,
+ __in ULONG ExeNameSize,
+ __out_opt PULONG ActualExeNameSize,
+ __out_ecount_opt(DescriptionSize) PSTR Description,
+ __in ULONG DescriptionSize,
+ __out_opt PULONG ActualDescriptionSize
+ ) PURE;
+
+ // Attaches to a running user-mode process.
+ STDMETHOD(AttachProcess)(
+ THIS_
+ __in ULONG64 Server,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+ // Creates a new user-mode process for debugging.
+ // CreateFlags are as given to Win32s CreateProcess.
+ // One of DEBUG_PROCESS or DEBUG_ONLY_THIS_PROCESS
+ // must be specified.
+ STDMETHOD(CreateProcess)(
+ THIS_
+ __in ULONG64 Server,
+ __in PSTR CommandLine,
+ __in ULONG CreateFlags
+ ) PURE;
+ // Creates or attaches to a user-mode process, or both.
+ // If CommandLine is NULL this method operates as
+ // AttachProcess does. If ProcessId is zero it
+ // operates as CreateProcess does. If CommandLine is
+ // non-NULL and ProcessId is non-zero the method first
+ // starts a process with the given information but
+ // in a suspended state. The engine then attaches to
+ // the indicated process. Once the attach is successful
+ // the suspended process is resumed. This provides
+ // synchronization between the new process and the
+ // attachment.
+ STDMETHOD(CreateProcessAndAttach)(
+ THIS_
+ __in ULONG64 Server,
+ __in_opt PSTR CommandLine,
+ __in ULONG CreateFlags,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+ // Gets and sets process control flags.
+ STDMETHOD(GetProcessOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ // Opens any kind of user- or kernel-mode dump file
+ // and begins a debug session with the information
+ // contained within it.
+ STDMETHOD(OpenDumpFile)(
+ THIS_
+ __in PCSTR DumpFile
+ ) PURE;
+ // Writes a dump file from the current session information.
+ // The kind of dump file written is determined by the
+ // kind of session and the type qualifier given.
+ // For example, if the current session is a kernel
+ // debug session (DEBUG_CLASS_KERNEL) and the qualifier
+ // is DEBUG_DUMP_SMALL a small kernel dump will be written.
+ STDMETHOD(WriteDumpFile)(
+ THIS_
+ __in PCSTR DumpFile,
+ __in ULONG Qualifier
+ ) PURE;
+
+ // Indicates that a remote client is ready to
+ // begin participating in the current session.
+ // HistoryLimit gives a character limit on
+ // the amount of output history to be sent.
+ STDMETHOD(ConnectSession)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG HistoryLimit
+ ) PURE;
+ // Indicates that the engine should start accepting
+ // remote connections. Options specifies connection types
+ // and their parameters. Supported strings are:
+ // npipe:Pipe=<Pipe name>
+ // tcp:Port=<IP port>
+ STDMETHOD(StartServer)(
+ THIS_
+ __in PCSTR Options
+ ) PURE;
+ // List the servers running on the given machine.
+ // Uses the line prefix.
+ STDMETHOD(OutputServers)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCSTR Machine,
+ __in ULONG Flags
+ ) PURE;
+
+ // Attempts to terminate all processes in the debuggers list.
+ STDMETHOD(TerminateProcesses)(
+ THIS
+ ) PURE;
+ // Attempts to detach from all processes in the debuggers list.
+ // This requires OS support for debugger detach.
+ STDMETHOD(DetachProcesses)(
+ THIS
+ ) PURE;
+ // Stops the current debug session. If a process
+ // was created or attached an active EndSession can
+ // terminate or detach from it.
+ // If a kernel connection was opened it will be closed but the
+ // target machine is otherwise unaffected.
+ STDMETHOD(EndSession)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // If a process was started and ran to completion
+ // this method can be used to retrieve its exit code.
+ STDMETHOD(GetExitCode)(
+ THIS_
+ __out PULONG Code
+ ) PURE;
+
+ // Client event callbacks are called on the thread
+ // of the client. In order to give thread
+ // execution to the engine for callbacks all
+ // client threads should call DispatchCallbacks
+ // when they are idle. Callbacks are only
+ // received when a thread calls DispatchCallbacks
+ // or WaitForEvent. WaitForEvent can only be
+ // called by the thread that started the debug
+ // session so all other client threads should
+ // call DispatchCallbacks when possible.
+ // DispatchCallbacks returns when ExitDispatch is used
+ // to interrupt dispatch or when the timeout expires.
+ // DispatchCallbacks dispatches callbacks for all
+ // clients associated with the thread calling
+ // DispatchCallbacks.
+ // DispatchCallbacks returns S_FALSE when the
+ // timeout expires.
+ STDMETHOD(DispatchCallbacks)(
+ THIS_
+ __in ULONG Timeout
+ ) PURE;
+ // ExitDispatch can be used to interrupt callback
+ // dispatch when a client thread is needed by the
+ // client. This method is reentrant and can
+ // be called from any thread.
+ STDMETHOD(ExitDispatch)(
+ THIS_
+ __in PDEBUG_CLIENT Client
+ ) PURE;
+
+ // Clients are specific to the thread that
+ // created them. Calls from other threads
+ // fail immediately. The CreateClient method
+ // is a notable exception; it allows creation
+ // of a new client for a new thread.
+ STDMETHOD(CreateClient)(
+ THIS_
+ __out PDEBUG_CLIENT* Client
+ ) PURE;
+
+ STDMETHOD(GetInputCallbacks)(
+ THIS_
+ __out PDEBUG_INPUT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetInputCallbacks)(
+ THIS_
+ __in_opt PDEBUG_INPUT_CALLBACKS Callbacks
+ ) PURE;
+
+ // Output callback interfaces are described separately.
+ STDMETHOD(GetOutputCallbacks)(
+ THIS_
+ __out PDEBUG_OUTPUT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetOutputCallbacks)(
+ THIS_
+ __in_opt PDEBUG_OUTPUT_CALLBACKS Callbacks
+ ) PURE;
+ // Output flags provide control over
+ // the distribution of output among clients.
+ // Output masks select which output streams
+ // should be sent to the output callbacks.
+ // Only Output calls with a mask that
+ // contains one of the output mask bits
+ // will be sent to the output callbacks.
+ // These methods are reentrant.
+ // If such access is not synchronized
+ // disruptions in output may occur.
+ STDMETHOD(GetOutputMask)(
+ THIS_
+ __out PULONG Mask
+ ) PURE;
+ STDMETHOD(SetOutputMask)(
+ THIS_
+ __in ULONG Mask
+ ) PURE;
+ // These methods allow access to another clients
+ // output mask. They are necessary for changing
+ // a clients output mask when it is
+ // waiting for events. These methods are reentrant
+ // and can be called from any thread.
+ STDMETHOD(GetOtherOutputMask)(
+ THIS_
+ __in PDEBUG_CLIENT Client,
+ __out PULONG Mask
+ ) PURE;
+ STDMETHOD(SetOtherOutputMask)(
+ THIS_
+ __in PDEBUG_CLIENT Client,
+ __in ULONG Mask
+ ) PURE;
+ // Control the width of an output line for
+ // commands which produce formatted output.
+ // This setting is just a suggestion.
+ STDMETHOD(GetOutputWidth)(
+ THIS_
+ __out PULONG Columns
+ ) PURE;
+ STDMETHOD(SetOutputWidth)(
+ THIS_
+ __in ULONG Columns
+ ) PURE;
+ // Some of the engines output commands produce
+ // multiple lines of output. A prefix can be
+ // set that the engine will automatically output
+ // for each line in that case, allowing a caller
+ // to control indentation or identifying marks.
+ // This is not a general setting for any output
+ // with a newline in it. Methods which use
+ // the line prefix are marked in their documentation.
+ STDMETHOD(GetOutputLinePrefix)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PrefixSize
+ ) PURE;
+ STDMETHOD(SetOutputLinePrefix)(
+ THIS_
+ __in_opt PCSTR Prefix
+ ) PURE;
+
+ // Returns a string describing the machine
+ // and user this client represents. The
+ // specific content of the string varies
+ // with operating system. If the client is
+ // remotely connected some network information
+ // may also be present.
+ STDMETHOD(GetIdentity)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG IdentitySize
+ ) PURE;
+ // Format is a printf-like format string
+ // with one %s where the identity string should go.
+ STDMETHOD(OutputIdentity)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags,
+ __in PCSTR Format
+ ) PURE;
+
+ // Event callbacks allow a client to
+ // receive notification about changes
+ // during the debug session.
+ STDMETHOD(GetEventCallbacks)(
+ THIS_
+ __out PDEBUG_EVENT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetEventCallbacks)(
+ THIS_
+ __in_opt PDEBUG_EVENT_CALLBACKS Callbacks
+ ) PURE;
+
+ // The engine sometimes merges compatible callback
+ // requests to reduce callback overhead. This is
+ // most noticeable with output as small pieces of
+ // output are collected into larger groups to
+ // reduce the overall number of output callback calls.
+ // A client can use this method to force all pending
+ // callbacks to be delivered. This is rarely necessary.
+ STDMETHOD(FlushCallbacks)(
+ THIS
+ ) PURE;
+
+ // IDebugClient2.
+
+ // Functions similarly to WriteDumpFile with
+ // the addition of the ability to specify
+ // per-dump-format write control flags.
+ // Comment is not supported in all formats.
+ STDMETHOD(WriteDumpFile2)(
+ THIS_
+ __in PCSTR DumpFile,
+ __in ULONG Qualifier,
+ __in ULONG FormatFlags,
+ __in_opt PCSTR Comment
+ ) PURE;
+ // Registers additional files of supporting information
+ // for a dump file open. This method must be called
+ // before OpenDumpFile is called.
+ // The files registered may be opened at the time
+ // this method is called but generally will not
+ // be used until OpenDumpFile is called.
+ STDMETHOD(AddDumpInformationFile)(
+ THIS_
+ __in PCSTR InfoFile,
+ __in ULONG Type
+ ) PURE;
+
+ // Requests that the remote process server shut down.
+ STDMETHOD(EndProcessServer)(
+ THIS_
+ __in ULONG64 Server
+ ) PURE;
+ // Waits for a started process server to
+ // exit. Allows an application running a
+ // process server to monitor the process
+ // server so that it can tell when a remote
+ // client has asked for it to exit.
+ // Returns S_OK if the process server has
+ // shut down and S_FALSE for a timeout.
+ STDMETHOD(WaitForProcessServerEnd)(
+ THIS_
+ __in ULONG Timeout
+ ) PURE;
+
+ // Returns S_OK if the system is configured
+ // to allow kernel debugging.
+ STDMETHOD(IsKernelDebuggerEnabled)(
+ THIS
+ ) PURE;
+
+ // Attempts to terminate the current process.
+ // Exit process events for the process may be generated.
+ STDMETHOD(TerminateCurrentProcess)(
+ THIS
+ ) PURE;
+ // Attempts to detach from the current process.
+ // This requires OS support for debugger detach.
+ STDMETHOD(DetachCurrentProcess)(
+ THIS
+ ) PURE;
+ // Removes the process from the debuggers process
+ // list without making any other changes. The process
+ // will still be marked as being debugged and will
+ // not run. This allows a debugger to be shut down
+ // and a new debugger attached without taking the
+ // process out of the debugged state.
+ // This is only supported on some system versions.
+ STDMETHOD(AbandonCurrentProcess)(
+ THIS
+ ) PURE;
+};
+
+#undef INTERFACE
+#define INTERFACE IDebugClient3
+DECLARE_INTERFACE_(IDebugClient3, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugClient.
+
+ // The following set of methods start
+ // the different kinds of debuggees.
+
+ // Begins a debug session using the kernel
+ // debugging protocol. This method selects
+ // the protocol as the debuggee communication
+ // mechanism but does not initiate the communication
+ // itself.
+ STDMETHOD(AttachKernel)(
+ THIS_
+ __in ULONG Flags,
+ __in_opt PCSTR ConnectOptions
+ ) PURE;
+ STDMETHOD(GetKernelConnectionOptions)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG OptionsSize
+ ) PURE;
+ // Updates the connection options for a live
+ // kernel connection. This can only be used
+ // to modify parameters for the connection, not
+ // to switch to a completely different kind of
+ // connection.
+ // This method is reentrant.
+ STDMETHOD(SetKernelConnectionOptions)(
+ THIS_
+ __in PCSTR Options
+ ) PURE;
+
+ // Starts a process server for remote
+ // user-mode process control.
+ // The local process server is server zero.
+ STDMETHOD(StartProcessServer)(
+ THIS_
+ __in ULONG Flags,
+ __in PCSTR Options,
+ __in_opt __reserved PVOID Reserved
+ ) PURE;
+ STDMETHOD(ConnectProcessServer)(
+ THIS_
+ __in PCSTR RemoteOptions,
+ __out PULONG64 Server
+ ) PURE;
+ STDMETHOD(DisconnectProcessServer)(
+ THIS_
+ __in ULONG64 Server
+ ) PURE;
+
+ // Enumerates and describes processes
+ // accessible through the given process server.
+ STDMETHOD(GetRunningProcessSystemIds)(
+ THIS_
+ __in ULONG64 Server,
+ __out_ecount_opt(Count) PULONG Ids,
+ __in ULONG Count,
+ __out_opt PULONG ActualCount
+ ) PURE;
+ STDMETHOD(GetRunningProcessSystemIdByExecutableName)(
+ THIS_
+ __in ULONG64 Server,
+ __in PCSTR ExeName,
+ __in ULONG Flags,
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(GetRunningProcessDescription)(
+ THIS_
+ __in ULONG64 Server,
+ __in ULONG SystemId,
+ __in ULONG Flags,
+ __out_ecount_opt(ExeNameSize) PSTR ExeName,
+ __in ULONG ExeNameSize,
+ __out_opt PULONG ActualExeNameSize,
+ __out_ecount_opt(DescriptionSize) PSTR Description,
+ __in ULONG DescriptionSize,
+ __out_opt PULONG ActualDescriptionSize
+ ) PURE;
+
+ // Attaches to a running user-mode process.
+ STDMETHOD(AttachProcess)(
+ THIS_
+ __in ULONG64 Server,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+ // Creates a new user-mode process for debugging.
+ // CreateFlags are as given to Win32s CreateProcess.
+ // One of DEBUG_PROCESS or DEBUG_ONLY_THIS_PROCESS
+ // must be specified.
+ STDMETHOD(CreateProcess)(
+ THIS_
+ __in ULONG64 Server,
+ __in PSTR CommandLine,
+ __in ULONG CreateFlags
+ ) PURE;
+ // Creates or attaches to a user-mode process, or both.
+ // If CommandLine is NULL this method operates as
+ // AttachProcess does. If ProcessId is zero it
+ // operates as CreateProcess does. If CommandLine is
+ // non-NULL and ProcessId is non-zero the method first
+ // starts a process with the given information but
+ // in a suspended state. The engine then attaches to
+ // the indicated process. Once the attach is successful
+ // the suspended process is resumed. This provides
+ // synchronization between the new process and the
+ // attachment.
+ STDMETHOD(CreateProcessAndAttach)(
+ THIS_
+ __in ULONG64 Server,
+ __in_opt PSTR CommandLine,
+ __in ULONG CreateFlags,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+ // Gets and sets process control flags.
+ STDMETHOD(GetProcessOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ // Opens any kind of user- or kernel-mode dump file
+ // and begins a debug session with the information
+ // contained within it.
+ STDMETHOD(OpenDumpFile)(
+ THIS_
+ __in PCSTR DumpFile
+ ) PURE;
+ // Writes a dump file from the current session information.
+ // The kind of dump file written is determined by the
+ // kind of session and the type qualifier given.
+ // For example, if the current session is a kernel
+ // debug session (DEBUG_CLASS_KERNEL) and the qualifier
+ // is DEBUG_DUMP_SMALL a small kernel dump will be written.
+ STDMETHOD(WriteDumpFile)(
+ THIS_
+ __in PCSTR DumpFile,
+ __in ULONG Qualifier
+ ) PURE;
+
+ // Indicates that a remote client is ready to
+ // begin participating in the current session.
+ // HistoryLimit gives a character limit on
+ // the amount of output history to be sent.
+ STDMETHOD(ConnectSession)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG HistoryLimit
+ ) PURE;
+ // Indicates that the engine should start accepting
+ // remote connections. Options specifies connection types
+ // and their parameters. Supported strings are:
+ // npipe:Pipe=<Pipe name>
+ // tcp:Port=<IP port>
+ STDMETHOD(StartServer)(
+ THIS_
+ __in PCSTR Options
+ ) PURE;
+ // List the servers running on the given machine.
+ // Uses the line prefix.
+ STDMETHOD(OutputServers)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCSTR Machine,
+ __in ULONG Flags
+ ) PURE;
+
+ // Attempts to terminate all processes in the debuggers list.
+ STDMETHOD(TerminateProcesses)(
+ THIS
+ ) PURE;
+ // Attempts to detach from all processes in the debuggers list.
+ // This requires OS support for debugger detach.
+ STDMETHOD(DetachProcesses)(
+ THIS
+ ) PURE;
+ // Stops the current debug session. If a process
+ // was created or attached an active EndSession can
+ // terminate or detach from it.
+ // If a kernel connection was opened it will be closed but the
+ // target machine is otherwise unaffected.
+ STDMETHOD(EndSession)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // If a process was started and ran to completion
+ // this method can be used to retrieve its exit code.
+ STDMETHOD(GetExitCode)(
+ THIS_
+ __out PULONG Code
+ ) PURE;
+
+ // Client event callbacks are called on the thread
+ // of the client. In order to give thread
+ // execution to the engine for callbacks all
+ // client threads should call DispatchCallbacks
+ // when they are idle. Callbacks are only
+ // received when a thread calls DispatchCallbacks
+ // or WaitForEvent. WaitForEvent can only be
+ // called by the thread that started the debug
+ // session so all other client threads should
+ // call DispatchCallbacks when possible.
+ // DispatchCallbacks returns when ExitDispatch is used
+ // to interrupt dispatch or when the timeout expires.
+ // DispatchCallbacks dispatches callbacks for all
+ // clients associated with the thread calling
+ // DispatchCallbacks.
+ // DispatchCallbacks returns S_FALSE when the
+ // timeout expires.
+ STDMETHOD(DispatchCallbacks)(
+ THIS_
+ __in ULONG Timeout
+ ) PURE;
+ // ExitDispatch can be used to interrupt callback
+ // dispatch when a client thread is needed by the
+ // client. This method is reentrant and can
+ // be called from any thread.
+ STDMETHOD(ExitDispatch)(
+ THIS_
+ __in PDEBUG_CLIENT Client
+ ) PURE;
+
+ // Clients are specific to the thread that
+ // created them. Calls from other threads
+ // fail immediately. The CreateClient method
+ // is a notable exception; it allows creation
+ // of a new client for a new thread.
+ STDMETHOD(CreateClient)(
+ THIS_
+ __out PDEBUG_CLIENT* Client
+ ) PURE;
+
+ STDMETHOD(GetInputCallbacks)(
+ THIS_
+ __out PDEBUG_INPUT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetInputCallbacks)(
+ THIS_
+ __in_opt PDEBUG_INPUT_CALLBACKS Callbacks
+ ) PURE;
+
+ // Output callback interfaces are described separately.
+ STDMETHOD(GetOutputCallbacks)(
+ THIS_
+ __out PDEBUG_OUTPUT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetOutputCallbacks)(
+ THIS_
+ __in_opt PDEBUG_OUTPUT_CALLBACKS Callbacks
+ ) PURE;
+ // Output flags provide control over
+ // the distribution of output among clients.
+ // Output masks select which output streams
+ // should be sent to the output callbacks.
+ // Only Output calls with a mask that
+ // contains one of the output mask bits
+ // will be sent to the output callbacks.
+ // These methods are reentrant.
+ // If such access is not synchronized
+ // disruptions in output may occur.
+ STDMETHOD(GetOutputMask)(
+ THIS_
+ __out PULONG Mask
+ ) PURE;
+ STDMETHOD(SetOutputMask)(
+ THIS_
+ __in ULONG Mask
+ ) PURE;
+ // These methods allow access to another clients
+ // output mask. They are necessary for changing
+ // a clients output mask when it is
+ // waiting for events. These methods are reentrant
+ // and can be called from any thread.
+ STDMETHOD(GetOtherOutputMask)(
+ THIS_
+ __in PDEBUG_CLIENT Client,
+ __out PULONG Mask
+ ) PURE;
+ STDMETHOD(SetOtherOutputMask)(
+ THIS_
+ __in PDEBUG_CLIENT Client,
+ __in ULONG Mask
+ ) PURE;
+ // Control the width of an output line for
+ // commands which produce formatted output.
+ // This setting is just a suggestion.
+ STDMETHOD(GetOutputWidth)(
+ THIS_
+ __out PULONG Columns
+ ) PURE;
+ STDMETHOD(SetOutputWidth)(
+ THIS_
+ __in ULONG Columns
+ ) PURE;
+ // Some of the engines output commands produce
+ // multiple lines of output. A prefix can be
+ // set that the engine will automatically output
+ // for each line in that case, allowing a caller
+ // to control indentation or identifying marks.
+ // This is not a general setting for any output
+ // with a newline in it. Methods which use
+ // the line prefix are marked in their documentation.
+ STDMETHOD(GetOutputLinePrefix)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PrefixSize
+ ) PURE;
+ STDMETHOD(SetOutputLinePrefix)(
+ THIS_
+ __in_opt PCSTR Prefix
+ ) PURE;
+
+ // Returns a string describing the machine
+ // and user this client represents. The
+ // specific content of the string varies
+ // with operating system. If the client is
+ // remotely connected some network information
+ // may also be present.
+ STDMETHOD(GetIdentity)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG IdentitySize
+ ) PURE;
+ // Format is a printf-like format string
+ // with one %s where the identity string should go.
+ STDMETHOD(OutputIdentity)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags,
+ __in PCSTR Format
+ ) PURE;
+
+ // Event callbacks allow a client to
+ // receive notification about changes
+ // during the debug session.
+ STDMETHOD(GetEventCallbacks)(
+ THIS_
+ __out PDEBUG_EVENT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetEventCallbacks)(
+ THIS_
+ __in_opt PDEBUG_EVENT_CALLBACKS Callbacks
+ ) PURE;
+
+ // The engine sometimes merges compatible callback
+ // requests to reduce callback overhead. This is
+ // most noticeable with output as small pieces of
+ // output are collected into larger groups to
+ // reduce the overall number of output callback calls.
+ // A client can use this method to force all pending
+ // callbacks to be delivered. This is rarely necessary.
+ STDMETHOD(FlushCallbacks)(
+ THIS
+ ) PURE;
+
+ // IDebugClient2.
+
+ // Functions similarly to WriteDumpFile with
+ // the addition of the ability to specify
+ // per-dump-format write control flags.
+ // Comment is not supported in all formats.
+ STDMETHOD(WriteDumpFile2)(
+ THIS_
+ __in PCSTR DumpFile,
+ __in ULONG Qualifier,
+ __in ULONG FormatFlags,
+ __in_opt PCSTR Comment
+ ) PURE;
+ // Registers additional files of supporting information
+ // for a dump file open. This method must be called
+ // before OpenDumpFile is called.
+ // The files registered may be opened at the time
+ // this method is called but generally will not
+ // be used until OpenDumpFile is called.
+ STDMETHOD(AddDumpInformationFile)(
+ THIS_
+ __in PCSTR InfoFile,
+ __in ULONG Type
+ ) PURE;
+
+ // Requests that the remote process server shut down.
+ STDMETHOD(EndProcessServer)(
+ THIS_
+ __in ULONG64 Server
+ ) PURE;
+ // Waits for a started process server to
+ // exit. Allows an application running a
+ // process server to monitor the process
+ // server so that it can tell when a remote
+ // client has asked for it to exit.
+ // Returns S_OK if the process server has
+ // shut down and S_FALSE for a timeout.
+ STDMETHOD(WaitForProcessServerEnd)(
+ THIS_
+ __in ULONG Timeout
+ ) PURE;
+
+ // Returns S_OK if the system is configured
+ // to allow kernel debugging.
+ STDMETHOD(IsKernelDebuggerEnabled)(
+ THIS
+ ) PURE;
+
+ // Attempts to terminate the current process.
+ // Exit process events for the process may be generated.
+ STDMETHOD(TerminateCurrentProcess)(
+ THIS
+ ) PURE;
+ // Attempts to detach from the current process.
+ // This requires OS support for debugger detach.
+ STDMETHOD(DetachCurrentProcess)(
+ THIS
+ ) PURE;
+ // Removes the process from the debuggers process
+ // list without making any other changes. The process
+ // will still be marked as being debugged and will
+ // not run. This allows a debugger to be shut down
+ // and a new debugger attached without taking the
+ // process out of the debugged state.
+ // This is only supported on some system versions.
+ STDMETHOD(AbandonCurrentProcess)(
+ THIS
+ ) PURE;
+
+ // IDebugClient3.
+
+ STDMETHOD(GetRunningProcessSystemIdByExecutableNameWide)(
+ THIS_
+ __in ULONG64 Server,
+ __in PCWSTR ExeName,
+ __in ULONG Flags,
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(GetRunningProcessDescriptionWide)(
+ THIS_
+ __in ULONG64 Server,
+ __in ULONG SystemId,
+ __in ULONG Flags,
+ __out_ecount_opt(ExeNameSize) PWSTR ExeName,
+ __in ULONG ExeNameSize,
+ __out_opt PULONG ActualExeNameSize,
+ __out_ecount_opt(DescriptionSize) PWSTR Description,
+ __in ULONG DescriptionSize,
+ __out_opt PULONG ActualDescriptionSize
+ ) PURE;
+
+ STDMETHOD(CreateProcessWide)(
+ THIS_
+ __in ULONG64 Server,
+ __in PWSTR CommandLine,
+ __in ULONG CreateFlags
+ ) PURE;
+ STDMETHOD(CreateProcessAndAttachWide)(
+ THIS_
+ __in ULONG64 Server,
+ __in_opt PWSTR CommandLine,
+ __in ULONG CreateFlags,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+};
+
+//
+// Special indices for GetDumpFile to return
+// alternate filenames.
+//
+
+// Special index that returns the name of the last .dmp file
+// that failed to load (whether directly or from inside a
+// .cab file).
+#define DEBUG_DUMP_FILE_LOAD_FAILED_INDEX 0xffffffff
+// Index that returns last cab file opened, this is needed to
+// get the name of original CAB file since debugger returns the
+// extracted dump file in the GetDumpFile method.
+#define DEBUG_DUMP_FILE_ORIGINAL_CAB_INDEX 0xfffffffe
+
+#undef INTERFACE
+#define INTERFACE IDebugClient4
+DECLARE_INTERFACE_(IDebugClient4, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugClient.
+
+ // The following set of methods start
+ // the different kinds of debuggees.
+
+ // Begins a debug session using the kernel
+ // debugging protocol. This method selects
+ // the protocol as the debuggee communication
+ // mechanism but does not initiate the communication
+ // itself.
+ STDMETHOD(AttachKernel)(
+ THIS_
+ __in ULONG Flags,
+ __in_opt PCSTR ConnectOptions
+ ) PURE;
+ STDMETHOD(GetKernelConnectionOptions)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG OptionsSize
+ ) PURE;
+ // Updates the connection options for a live
+ // kernel connection. This can only be used
+ // to modify parameters for the connection, not
+ // to switch to a completely different kind of
+ // connection.
+ // This method is reentrant.
+ STDMETHOD(SetKernelConnectionOptions)(
+ THIS_
+ __in PCSTR Options
+ ) PURE;
+
+ // Starts a process server for remote
+ // user-mode process control.
+ // The local process server is server zero.
+ STDMETHOD(StartProcessServer)(
+ THIS_
+ __in ULONG Flags,
+ __in PCSTR Options,
+ __in_opt __reserved PVOID Reserved
+ ) PURE;
+ STDMETHOD(ConnectProcessServer)(
+ THIS_
+ __in PCSTR RemoteOptions,
+ __out PULONG64 Server
+ ) PURE;
+ STDMETHOD(DisconnectProcessServer)(
+ THIS_
+ __in ULONG64 Server
+ ) PURE;
+
+ // Enumerates and describes processes
+ // accessible through the given process server.
+ STDMETHOD(GetRunningProcessSystemIds)(
+ THIS_
+ __in ULONG64 Server,
+ __out_ecount_opt(Count) PULONG Ids,
+ __in ULONG Count,
+ __out_opt PULONG ActualCount
+ ) PURE;
+ STDMETHOD(GetRunningProcessSystemIdByExecutableName)(
+ THIS_
+ __in ULONG64 Server,
+ __in PCSTR ExeName,
+ __in ULONG Flags,
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(GetRunningProcessDescription)(
+ THIS_
+ __in ULONG64 Server,
+ __in ULONG SystemId,
+ __in ULONG Flags,
+ __out_ecount_opt(ExeNameSize) PSTR ExeName,
+ __in ULONG ExeNameSize,
+ __out_opt PULONG ActualExeNameSize,
+ __out_ecount_opt(DescriptionSize) PSTR Description,
+ __in ULONG DescriptionSize,
+ __out_opt PULONG ActualDescriptionSize
+ ) PURE;
+
+ // Attaches to a running user-mode process.
+ STDMETHOD(AttachProcess)(
+ THIS_
+ __in ULONG64 Server,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+ // Creates a new user-mode process for debugging.
+ // CreateFlags are as given to Win32s CreateProcess.
+ // One of DEBUG_PROCESS or DEBUG_ONLY_THIS_PROCESS
+ // must be specified.
+ STDMETHOD(CreateProcess)(
+ THIS_
+ __in ULONG64 Server,
+ __in PSTR CommandLine,
+ __in ULONG CreateFlags
+ ) PURE;
+ // Creates or attaches to a user-mode process, or both.
+ // If CommandLine is NULL this method operates as
+ // AttachProcess does. If ProcessId is zero it
+ // operates as CreateProcess does. If CommandLine is
+ // non-NULL and ProcessId is non-zero the method first
+ // starts a process with the given information but
+ // in a suspended state. The engine then attaches to
+ // the indicated process. Once the attach is successful
+ // the suspended process is resumed. This provides
+ // synchronization between the new process and the
+ // attachment.
+ STDMETHOD(CreateProcessAndAttach)(
+ THIS_
+ __in ULONG64 Server,
+ __in_opt PSTR CommandLine,
+ __in ULONG CreateFlags,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+ // Gets and sets process control flags.
+ STDMETHOD(GetProcessOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ // Opens any kind of user- or kernel-mode dump file
+ // and begins a debug session with the information
+ // contained within it.
+ STDMETHOD(OpenDumpFile)(
+ THIS_
+ __in PCSTR DumpFile
+ ) PURE;
+ // Writes a dump file from the current session information.
+ // The kind of dump file written is determined by the
+ // kind of session and the type qualifier given.
+ // For example, if the current session is a kernel
+ // debug session (DEBUG_CLASS_KERNEL) and the qualifier
+ // is DEBUG_DUMP_SMALL a small kernel dump will be written.
+ STDMETHOD(WriteDumpFile)(
+ THIS_
+ __in PCSTR DumpFile,
+ __in ULONG Qualifier
+ ) PURE;
+
+ // Indicates that a remote client is ready to
+ // begin participating in the current session.
+ // HistoryLimit gives a character limit on
+ // the amount of output history to be sent.
+ STDMETHOD(ConnectSession)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG HistoryLimit
+ ) PURE;
+ // Indicates that the engine should start accepting
+ // remote connections. Options specifies connection types
+ // and their parameters. Supported strings are:
+ // npipe:Pipe=<Pipe name>
+ // tcp:Port=<IP port>
+ STDMETHOD(StartServer)(
+ THIS_
+ __in PCSTR Options
+ ) PURE;
+ // List the servers running on the given machine.
+ // Uses the line prefix.
+ STDMETHOD(OutputServers)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCSTR Machine,
+ __in ULONG Flags
+ ) PURE;
+
+ // Attempts to terminate all processes in the debuggers list.
+ STDMETHOD(TerminateProcesses)(
+ THIS
+ ) PURE;
+ // Attempts to detach from all processes in the debuggers list.
+ // This requires OS support for debugger detach.
+ STDMETHOD(DetachProcesses)(
+ THIS
+ ) PURE;
+ // Stops the current debug session. If a process
+ // was created or attached an active EndSession can
+ // terminate or detach from it.
+ // If a kernel connection was opened it will be closed but the
+ // target machine is otherwise unaffected.
+ STDMETHOD(EndSession)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // If a process was started and ran to completion
+ // this method can be used to retrieve its exit code.
+ STDMETHOD(GetExitCode)(
+ THIS_
+ __out PULONG Code
+ ) PURE;
+
+ // Client event callbacks are called on the thread
+ // of the client. In order to give thread
+ // execution to the engine for callbacks all
+ // client threads should call DispatchCallbacks
+ // when they are idle. Callbacks are only
+ // received when a thread calls DispatchCallbacks
+ // or WaitForEvent. WaitForEvent can only be
+ // called by the thread that started the debug
+ // session so all other client threads should
+ // call DispatchCallbacks when possible.
+ // DispatchCallbacks returns when ExitDispatch is used
+ // to interrupt dispatch or when the timeout expires.
+ // DispatchCallbacks dispatches callbacks for all
+ // clients associated with the thread calling
+ // DispatchCallbacks.
+ // DispatchCallbacks returns S_FALSE when the
+ // timeout expires.
+ STDMETHOD(DispatchCallbacks)(
+ THIS_
+ __in ULONG Timeout
+ ) PURE;
+ // ExitDispatch can be used to interrupt callback
+ // dispatch when a client thread is needed by the
+ // client. This method is reentrant and can
+ // be called from any thread.
+ STDMETHOD(ExitDispatch)(
+ THIS_
+ __in PDEBUG_CLIENT Client
+ ) PURE;
+
+ // Clients are specific to the thread that
+ // created them. Calls from other threads
+ // fail immediately. The CreateClient method
+ // is a notable exception; it allows creation
+ // of a new client for a new thread.
+ STDMETHOD(CreateClient)(
+ THIS_
+ __out PDEBUG_CLIENT* Client
+ ) PURE;
+
+ STDMETHOD(GetInputCallbacks)(
+ THIS_
+ __out PDEBUG_INPUT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetInputCallbacks)(
+ THIS_
+ __in_opt PDEBUG_INPUT_CALLBACKS Callbacks
+ ) PURE;
+
+ // Output callback interfaces are described separately.
+ STDMETHOD(GetOutputCallbacks)(
+ THIS_
+ __out PDEBUG_OUTPUT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetOutputCallbacks)(
+ THIS_
+ __in_opt PDEBUG_OUTPUT_CALLBACKS Callbacks
+ ) PURE;
+ // Output flags provide control over
+ // the distribution of output among clients.
+ // Output masks select which output streams
+ // should be sent to the output callbacks.
+ // Only Output calls with a mask that
+ // contains one of the output mask bits
+ // will be sent to the output callbacks.
+ // These methods are reentrant.
+ // If such access is not synchronized
+ // disruptions in output may occur.
+ STDMETHOD(GetOutputMask)(
+ THIS_
+ __out PULONG Mask
+ ) PURE;
+ STDMETHOD(SetOutputMask)(
+ THIS_
+ __in ULONG Mask
+ ) PURE;
+ // These methods allow access to another clients
+ // output mask. They are necessary for changing
+ // a clients output mask when it is
+ // waiting for events. These methods are reentrant
+ // and can be called from any thread.
+ STDMETHOD(GetOtherOutputMask)(
+ THIS_
+ __in PDEBUG_CLIENT Client,
+ __out PULONG Mask
+ ) PURE;
+ STDMETHOD(SetOtherOutputMask)(
+ THIS_
+ __in PDEBUG_CLIENT Client,
+ __in ULONG Mask
+ ) PURE;
+ // Control the width of an output line for
+ // commands which produce formatted output.
+ // This setting is just a suggestion.
+ STDMETHOD(GetOutputWidth)(
+ THIS_
+ __out PULONG Columns
+ ) PURE;
+ STDMETHOD(SetOutputWidth)(
+ THIS_
+ __in ULONG Columns
+ ) PURE;
+ // Some of the engines output commands produce
+ // multiple lines of output. A prefix can be
+ // set that the engine will automatically output
+ // for each line in that case, allowing a caller
+ // to control indentation or identifying marks.
+ // This is not a general setting for any output
+ // with a newline in it. Methods which use
+ // the line prefix are marked in their documentation.
+ STDMETHOD(GetOutputLinePrefix)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PrefixSize
+ ) PURE;
+ STDMETHOD(SetOutputLinePrefix)(
+ THIS_
+ __in_opt PCSTR Prefix
+ ) PURE;
+
+ // Returns a string describing the machine
+ // and user this client represents. The
+ // specific content of the string varies
+ // with operating system. If the client is
+ // remotely connected some network information
+ // may also be present.
+ STDMETHOD(GetIdentity)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG IdentitySize
+ ) PURE;
+ // Format is a printf-like format string
+ // with one %s where the identity string should go.
+ STDMETHOD(OutputIdentity)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags,
+ __in PCSTR Format
+ ) PURE;
+
+ // Event callbacks allow a client to
+ // receive notification about changes
+ // during the debug session.
+ STDMETHOD(GetEventCallbacks)(
+ THIS_
+ __out PDEBUG_EVENT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetEventCallbacks)(
+ THIS_
+ __in_opt PDEBUG_EVENT_CALLBACKS Callbacks
+ ) PURE;
+
+ // The engine sometimes merges compatible callback
+ // requests to reduce callback overhead. This is
+ // most noticeable with output as small pieces of
+ // output are collected into larger groups to
+ // reduce the overall number of output callback calls.
+ // A client can use this method to force all pending
+ // callbacks to be delivered. This is rarely necessary.
+ STDMETHOD(FlushCallbacks)(
+ THIS
+ ) PURE;
+
+ // IDebugClient2.
+
+ // Functions similarly to WriteDumpFile with
+ // the addition of the ability to specify
+ // per-dump-format write control flags.
+ // Comment is not supported in all formats.
+ STDMETHOD(WriteDumpFile2)(
+ THIS_
+ __in PCSTR DumpFile,
+ __in ULONG Qualifier,
+ __in ULONG FormatFlags,
+ __in_opt PCSTR Comment
+ ) PURE;
+ // Registers additional files of supporting information
+ // for a dump file open. This method must be called
+ // before OpenDumpFile is called.
+ // The files registered may be opened at the time
+ // this method is called but generally will not
+ // be used until OpenDumpFile is called.
+ STDMETHOD(AddDumpInformationFile)(
+ THIS_
+ __in PCSTR InfoFile,
+ __in ULONG Type
+ ) PURE;
+
+ // Requests that the remote process server shut down.
+ STDMETHOD(EndProcessServer)(
+ THIS_
+ __in ULONG64 Server
+ ) PURE;
+ // Waits for a started process server to
+ // exit. Allows an application running a
+ // process server to monitor the process
+ // server so that it can tell when a remote
+ // client has asked for it to exit.
+ // Returns S_OK if the process server has
+ // shut down and S_FALSE for a timeout.
+ STDMETHOD(WaitForProcessServerEnd)(
+ THIS_
+ __in ULONG Timeout
+ ) PURE;
+
+ // Returns S_OK if the system is configured
+ // to allow kernel debugging.
+ STDMETHOD(IsKernelDebuggerEnabled)(
+ THIS
+ ) PURE;
+
+ // Attempts to terminate the current process.
+ // Exit process events for the process may be generated.
+ STDMETHOD(TerminateCurrentProcess)(
+ THIS
+ ) PURE;
+ // Attempts to detach from the current process.
+ // This requires OS support for debugger detach.
+ STDMETHOD(DetachCurrentProcess)(
+ THIS
+ ) PURE;
+ // Removes the process from the debuggers process
+ // list without making any other changes. The process
+ // will still be marked as being debugged and will
+ // not run. This allows a debugger to be shut down
+ // and a new debugger attached without taking the
+ // process out of the debugged state.
+ // This is only supported on some system versions.
+ STDMETHOD(AbandonCurrentProcess)(
+ THIS
+ ) PURE;
+
+ // IDebugClient3.
+
+ STDMETHOD(GetRunningProcessSystemIdByExecutableNameWide)(
+ THIS_
+ __in ULONG64 Server,
+ __in PCWSTR ExeName,
+ __in ULONG Flags,
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(GetRunningProcessDescriptionWide)(
+ THIS_
+ __in ULONG64 Server,
+ __in ULONG SystemId,
+ __in ULONG Flags,
+ __out_ecount_opt(ExeNameSize) PWSTR ExeName,
+ __in ULONG ExeNameSize,
+ __out_opt PULONG ActualExeNameSize,
+ __out_ecount_opt(DescriptionSize) PWSTR Description,
+ __in ULONG DescriptionSize,
+ __out_opt PULONG ActualDescriptionSize
+ ) PURE;
+
+ STDMETHOD(CreateProcessWide)(
+ THIS_
+ __in ULONG64 Server,
+ __in PWSTR CommandLine,
+ __in ULONG CreateFlags
+ ) PURE;
+ STDMETHOD(CreateProcessAndAttachWide)(
+ THIS_
+ __in ULONG64 Server,
+ __in_opt PWSTR CommandLine,
+ __in ULONG CreateFlags,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+
+ // IDebugClient4.
+
+ // In the following methods both a filename and a file
+ // handle can be passed in. If a file handle is given
+ // the filename may be omitted, although providing it
+ // allows the debugger to properly report the name when
+ // queried.
+ // File handles cannot be used in remote calls.
+ STDMETHOD(OpenDumpFileWide)(
+ THIS_
+ __in_opt PCWSTR FileName,
+ __in ULONG64 FileHandle
+ ) PURE;
+ STDMETHOD(WriteDumpFileWide)(
+ THIS_
+ __in_opt PCWSTR FileName,
+ __in ULONG64 FileHandle,
+ __in ULONG Qualifier,
+ __in ULONG FormatFlags,
+ __in_opt PCWSTR Comment
+ ) PURE;
+ STDMETHOD(AddDumpInformationFileWide)(
+ THIS_
+ __in_opt PCWSTR FileName,
+ __in ULONG64 FileHandle,
+ __in ULONG Type
+ ) PURE;
+ // These methods can be used to retrieve
+ // file information for all targets that
+ // involve files.
+ STDMETHOD(GetNumberDumpFiles)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetDumpFile)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PULONG64 Handle,
+ __out PULONG Type
+ ) PURE;
+ STDMETHOD(GetDumpFileWide)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PULONG64 Handle,
+ __out PULONG Type
+ ) PURE;
+};
+
+#undef INTERFACE
+#define INTERFACE IDebugClient5
+DECLARE_INTERFACE_(IDebugClient5, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugClient.
+
+ // The following set of methods start
+ // the different kinds of debuggees.
+
+ // Begins a debug session using the kernel
+ // debugging protocol. This method selects
+ // the protocol as the debuggee communication
+ // mechanism but does not initiate the communication
+ // itself.
+ STDMETHOD(AttachKernel)(
+ THIS_
+ __in ULONG Flags,
+ __in_opt PCSTR ConnectOptions
+ ) PURE;
+ STDMETHOD(GetKernelConnectionOptions)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG OptionsSize
+ ) PURE;
+ // Updates the connection options for a live
+ // kernel connection. This can only be used
+ // to modify parameters for the connection, not
+ // to switch to a completely different kind of
+ // connection.
+ // This method is reentrant.
+ STDMETHOD(SetKernelConnectionOptions)(
+ THIS_
+ __in PCSTR Options
+ ) PURE;
+
+ // Starts a process server for remote
+ // user-mode process control.
+ // The local process server is server zero.
+ STDMETHOD(StartProcessServer)(
+ THIS_
+ __in ULONG Flags,
+ __in PCSTR Options,
+ __in_opt __reserved PVOID Reserved
+ ) PURE;
+ STDMETHOD(ConnectProcessServer)(
+ THIS_
+ __in PCSTR RemoteOptions,
+ __out PULONG64 Server
+ ) PURE;
+ STDMETHOD(DisconnectProcessServer)(
+ THIS_
+ __in ULONG64 Server
+ ) PURE;
+
+ // Enumerates and describes processes
+ // accessible through the given process server.
+ STDMETHOD(GetRunningProcessSystemIds)(
+ THIS_
+ __in ULONG64 Server,
+ __out_ecount_opt(Count) PULONG Ids,
+ __in ULONG Count,
+ __out_opt PULONG ActualCount
+ ) PURE;
+ STDMETHOD(GetRunningProcessSystemIdByExecutableName)(
+ THIS_
+ __in ULONG64 Server,
+ __in PCSTR ExeName,
+ __in ULONG Flags,
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(GetRunningProcessDescription)(
+ THIS_
+ __in ULONG64 Server,
+ __in ULONG SystemId,
+ __in ULONG Flags,
+ __out_ecount_opt(ExeNameSize) PSTR ExeName,
+ __in ULONG ExeNameSize,
+ __out_opt PULONG ActualExeNameSize,
+ __out_ecount_opt(DescriptionSize) PSTR Description,
+ __in ULONG DescriptionSize,
+ __out_opt PULONG ActualDescriptionSize
+ ) PURE;
+
+ // Attaches to a running user-mode process.
+ STDMETHOD(AttachProcess)(
+ THIS_
+ __in ULONG64 Server,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+ // Creates a new user-mode process for debugging.
+ // CreateFlags are as given to Win32s CreateProcess.
+ // One of DEBUG_PROCESS or DEBUG_ONLY_THIS_PROCESS
+ // must be specified.
+ STDMETHOD(CreateProcess)(
+ THIS_
+ __in ULONG64 Server,
+ __in PSTR CommandLine,
+ __in ULONG CreateFlags
+ ) PURE;
+ // Creates or attaches to a user-mode process, or both.
+ // If CommandLine is NULL this method operates as
+ // AttachProcess does. If ProcessId is zero it
+ // operates as CreateProcess does. If CommandLine is
+ // non-NULL and ProcessId is non-zero the method first
+ // starts a process with the given information but
+ // in a suspended state. The engine then attaches to
+ // the indicated process. Once the attach is successful
+ // the suspended process is resumed. This provides
+ // synchronization between the new process and the
+ // attachment.
+ STDMETHOD(CreateProcessAndAttach)(
+ THIS_
+ __in ULONG64 Server,
+ __in_opt PSTR CommandLine,
+ __in ULONG CreateFlags,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+ // Gets and sets process control flags.
+ STDMETHOD(GetProcessOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetProcessOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ // Opens any kind of user- or kernel-mode dump file
+ // and begins a debug session with the information
+ // contained within it.
+ STDMETHOD(OpenDumpFile)(
+ THIS_
+ __in PCSTR DumpFile
+ ) PURE;
+ // Writes a dump file from the current session information.
+ // The kind of dump file written is determined by the
+ // kind of session and the type qualifier given.
+ // For example, if the current session is a kernel
+ // debug session (DEBUG_CLASS_KERNEL) and the qualifier
+ // is DEBUG_DUMP_SMALL a small kernel dump will be written.
+ STDMETHOD(WriteDumpFile)(
+ THIS_
+ __in PCSTR DumpFile,
+ __in ULONG Qualifier
+ ) PURE;
+
+ // Indicates that a remote client is ready to
+ // begin participating in the current session.
+ // HistoryLimit gives a character limit on
+ // the amount of output history to be sent.
+ STDMETHOD(ConnectSession)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG HistoryLimit
+ ) PURE;
+ // Indicates that the engine should start accepting
+ // remote connections. Options specifies connection types
+ // and their parameters. Supported strings are:
+ // npipe:Pipe=<Pipe name>
+ // tcp:Port=<IP port>
+ STDMETHOD(StartServer)(
+ THIS_
+ __in PCSTR Options
+ ) PURE;
+ // List the servers running on the given machine.
+ // Uses the line prefix.
+ STDMETHOD(OutputServers)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCSTR Machine,
+ __in ULONG Flags
+ ) PURE;
+
+ // Attempts to terminate all processes in the debuggers list.
+ STDMETHOD(TerminateProcesses)(
+ THIS
+ ) PURE;
+ // Attempts to detach from all processes in the debuggers list.
+ // This requires OS support for debugger detach.
+ STDMETHOD(DetachProcesses)(
+ THIS
+ ) PURE;
+ // Stops the current debug session. If a process
+ // was created or attached an active EndSession can
+ // terminate or detach from it.
+ // If a kernel connection was opened it will be closed but the
+ // target machine is otherwise unaffected.
+ STDMETHOD(EndSession)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // If a process was started and ran to completion
+ // this method can be used to retrieve its exit code.
+ STDMETHOD(GetExitCode)(
+ THIS_
+ __out PULONG Code
+ ) PURE;
+
+ // Client event callbacks are called on the thread
+ // of the client. In order to give thread
+ // execution to the engine for callbacks all
+ // client threads should call DispatchCallbacks
+ // when they are idle. Callbacks are only
+ // received when a thread calls DispatchCallbacks
+ // or WaitForEvent. WaitForEvent can only be
+ // called by the thread that started the debug
+ // session so all other client threads should
+ // call DispatchCallbacks when possible.
+ // DispatchCallbacks returns when ExitDispatch is used
+ // to interrupt dispatch or when the timeout expires.
+ // DispatchCallbacks dispatches callbacks for all
+ // clients associated with the thread calling
+ // DispatchCallbacks.
+ // DispatchCallbacks returns S_FALSE when the
+ // timeout expires.
+ STDMETHOD(DispatchCallbacks)(
+ THIS_
+ __in ULONG Timeout
+ ) PURE;
+ // ExitDispatch can be used to interrupt callback
+ // dispatch when a client thread is needed by the
+ // client. This method is reentrant and can
+ // be called from any thread.
+ STDMETHOD(ExitDispatch)(
+ THIS_
+ __in PDEBUG_CLIENT Client
+ ) PURE;
+
+ // Clients are specific to the thread that
+ // created them. Calls from other threads
+ // fail immediately. The CreateClient method
+ // is a notable exception; it allows creation
+ // of a new client for a new thread.
+ STDMETHOD(CreateClient)(
+ THIS_
+ __out PDEBUG_CLIENT* Client
+ ) PURE;
+
+ STDMETHOD(GetInputCallbacks)(
+ THIS_
+ __out PDEBUG_INPUT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetInputCallbacks)(
+ THIS_
+ __in_opt PDEBUG_INPUT_CALLBACKS Callbacks
+ ) PURE;
+
+ // Output callback interfaces are described separately.
+ STDMETHOD(GetOutputCallbacks)(
+ THIS_
+ __out PDEBUG_OUTPUT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetOutputCallbacks)(
+ THIS_
+ __in_opt PDEBUG_OUTPUT_CALLBACKS Callbacks
+ ) PURE;
+ // Output flags provide control over
+ // the distribution of output among clients.
+ // Output masks select which output streams
+ // should be sent to the output callbacks.
+ // Only Output calls with a mask that
+ // contains one of the output mask bits
+ // will be sent to the output callbacks.
+ // These methods are reentrant.
+ // If such access is not synchronized
+ // disruptions in output may occur.
+ STDMETHOD(GetOutputMask)(
+ THIS_
+ __out PULONG Mask
+ ) PURE;
+ STDMETHOD(SetOutputMask)(
+ THIS_
+ __in ULONG Mask
+ ) PURE;
+ // These methods allow access to another clients
+ // output mask. They are necessary for changing
+ // a clients output mask when it is
+ // waiting for events. These methods are reentrant
+ // and can be called from any thread.
+ STDMETHOD(GetOtherOutputMask)(
+ THIS_
+ __in PDEBUG_CLIENT Client,
+ __out PULONG Mask
+ ) PURE;
+ STDMETHOD(SetOtherOutputMask)(
+ THIS_
+ __in PDEBUG_CLIENT Client,
+ __in ULONG Mask
+ ) PURE;
+ // Control the width of an output line for
+ // commands which produce formatted output.
+ // This setting is just a suggestion.
+ STDMETHOD(GetOutputWidth)(
+ THIS_
+ __out PULONG Columns
+ ) PURE;
+ STDMETHOD(SetOutputWidth)(
+ THIS_
+ __in ULONG Columns
+ ) PURE;
+ // Some of the engines output commands produce
+ // multiple lines of output. A prefix can be
+ // set that the engine will automatically output
+ // for each line in that case, allowing a caller
+ // to control indentation or identifying marks.
+ // This is not a general setting for any output
+ // with a newline in it. Methods which use
+ // the line prefix are marked in their documentation.
+ STDMETHOD(GetOutputLinePrefix)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PrefixSize
+ ) PURE;
+ STDMETHOD(SetOutputLinePrefix)(
+ THIS_
+ __in_opt PCSTR Prefix
+ ) PURE;
+
+ // Returns a string describing the machine
+ // and user this client represents. The
+ // specific content of the string varies
+ // with operating system. If the client is
+ // remotely connected some network information
+ // may also be present.
+ STDMETHOD(GetIdentity)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG IdentitySize
+ ) PURE;
+ // Format is a printf-like format string
+ // with one %s where the identity string should go.
+ STDMETHOD(OutputIdentity)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags,
+ __in PCSTR Format
+ ) PURE;
+
+ // Event callbacks allow a client to
+ // receive notification about changes
+ // during the debug session.
+ STDMETHOD(GetEventCallbacks)(
+ THIS_
+ __out PDEBUG_EVENT_CALLBACKS* Callbacks
+ ) PURE;
+ STDMETHOD(SetEventCallbacks)(
+ THIS_
+ __in_opt PDEBUG_EVENT_CALLBACKS Callbacks
+ ) PURE;
+
+ // The engine sometimes merges compatible callback
+ // requests to reduce callback overhead. This is
+ // most noticeable with output as small pieces of
+ // output are collected into larger groups to
+ // reduce the overall number of output callback calls.
+ // A client can use this method to force all pending
+ // callbacks to be delivered. This is rarely necessary.
+ STDMETHOD(FlushCallbacks)(
+ THIS
+ ) PURE;
+
+ // IDebugClient2.
+
+ // Functions similarly to WriteDumpFile with
+ // the addition of the ability to specify
+ // per-dump-format write control flags.
+ // Comment is not supported in all formats.
+ STDMETHOD(WriteDumpFile2)(
+ THIS_
+ __in PCSTR DumpFile,
+ __in ULONG Qualifier,
+ __in ULONG FormatFlags,
+ __in_opt PCSTR Comment
+ ) PURE;
+ // Registers additional files of supporting information
+ // for a dump file open. This method must be called
+ // before OpenDumpFile is called.
+ // The files registered may be opened at the time
+ // this method is called but generally will not
+ // be used until OpenDumpFile is called.
+ STDMETHOD(AddDumpInformationFile)(
+ THIS_
+ __in PCSTR InfoFile,
+ __in ULONG Type
+ ) PURE;
+
+ // Requests that the remote process server shut down.
+ STDMETHOD(EndProcessServer)(
+ THIS_
+ __in ULONG64 Server
+ ) PURE;
+ // Waits for a started process server to
+ // exit. Allows an application running a
+ // process server to monitor the process
+ // server so that it can tell when a remote
+ // client has asked for it to exit.
+ // Returns S_OK if the process server has
+ // shut down and S_FALSE for a timeout.
+ STDMETHOD(WaitForProcessServerEnd)(
+ THIS_
+ __in ULONG Timeout
+ ) PURE;
+
+ // Returns S_OK if the system is configured
+ // to allow kernel debugging.
+ STDMETHOD(IsKernelDebuggerEnabled)(
+ THIS
+ ) PURE;
+
+ // Attempts to terminate the current process.
+ // Exit process events for the process may be generated.
+ STDMETHOD(TerminateCurrentProcess)(
+ THIS
+ ) PURE;
+ // Attempts to detach from the current process.
+ // This requires OS support for debugger detach.
+ STDMETHOD(DetachCurrentProcess)(
+ THIS
+ ) PURE;
+ // Removes the process from the debuggers process
+ // list without making any other changes. The process
+ // will still be marked as being debugged and will
+ // not run. This allows a debugger to be shut down
+ // and a new debugger attached without taking the
+ // process out of the debugged state.
+ // This is only supported on some system versions.
+ STDMETHOD(AbandonCurrentProcess)(
+ THIS
+ ) PURE;
+
+ // IDebugClient3.
+
+ STDMETHOD(GetRunningProcessSystemIdByExecutableNameWide)(
+ THIS_
+ __in ULONG64 Server,
+ __in PCWSTR ExeName,
+ __in ULONG Flags,
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(GetRunningProcessDescriptionWide)(
+ THIS_
+ __in ULONG64 Server,
+ __in ULONG SystemId,
+ __in ULONG Flags,
+ __out_ecount_opt(ExeNameSize) PWSTR ExeName,
+ __in ULONG ExeNameSize,
+ __out_opt PULONG ActualExeNameSize,
+ __out_ecount_opt(DescriptionSize) PWSTR Description,
+ __in ULONG DescriptionSize,
+ __out_opt PULONG ActualDescriptionSize
+ ) PURE;
+
+ STDMETHOD(CreateProcessWide)(
+ THIS_
+ __in ULONG64 Server,
+ __in PWSTR CommandLine,
+ __in ULONG CreateFlags
+ ) PURE;
+ STDMETHOD(CreateProcessAndAttachWide)(
+ THIS_
+ __in ULONG64 Server,
+ __in_opt PWSTR CommandLine,
+ __in ULONG CreateFlags,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+
+ // IDebugClient4.
+
+ // In the following methods both a filename and a file
+ // handle can be passed in. If a file handle is given
+ // the filename may be omitted, although providing it
+ // allows the debugger to properly report the name when
+ // queried.
+ // File handles cannot be used in remote calls.
+ STDMETHOD(OpenDumpFileWide)(
+ THIS_
+ __in_opt PCWSTR FileName,
+ __in ULONG64 FileHandle
+ ) PURE;
+ STDMETHOD(WriteDumpFileWide)(
+ THIS_
+ __in_opt PCWSTR FileName,
+ __in ULONG64 FileHandle,
+ __in ULONG Qualifier,
+ __in ULONG FormatFlags,
+ __in_opt PCWSTR Comment
+ ) PURE;
+ STDMETHOD(AddDumpInformationFileWide)(
+ THIS_
+ __in_opt PCWSTR FileName,
+ __in ULONG64 FileHandle,
+ __in ULONG Type
+ ) PURE;
+ // These methods can be used to retrieve
+ // file information for all targets that
+ // involve files.
+ STDMETHOD(GetNumberDumpFiles)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetDumpFile)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PULONG64 Handle,
+ __out PULONG Type
+ ) PURE;
+ STDMETHOD(GetDumpFileWide)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PULONG64 Handle,
+ __out PULONG Type
+ ) PURE;
+
+ // IDebugClient5.
+
+ STDMETHOD(AttachKernelWide)(
+ THIS_
+ __in ULONG Flags,
+ __in_opt PCWSTR ConnectOptions
+ ) PURE;
+ STDMETHOD(GetKernelConnectionOptionsWide)(
+ THIS_
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG OptionsSize
+ ) PURE;
+ STDMETHOD(SetKernelConnectionOptionsWide)(
+ THIS_
+ __in PCWSTR Options
+ ) PURE;
+
+ STDMETHOD(StartProcessServerWide)(
+ THIS_
+ __in ULONG Flags,
+ __in PCWSTR Options,
+ __in_opt __reserved PVOID Reserved
+ ) PURE;
+ STDMETHOD(ConnectProcessServerWide)(
+ THIS_
+ __in PCWSTR RemoteOptions,
+ __out PULONG64 Server
+ ) PURE;
+
+ STDMETHOD(StartServerWide)(
+ THIS_
+ __in PCWSTR Options
+ ) PURE;
+ STDMETHOD(OutputServersWide)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCWSTR Machine,
+ __in ULONG Flags
+ ) PURE;
+
+ STDMETHOD(GetOutputCallbacksWide)(
+ THIS_
+ __out PDEBUG_OUTPUT_CALLBACKS_WIDE* Callbacks
+ ) PURE;
+ STDMETHOD(SetOutputCallbacksWide)(
+ THIS_
+ __in PDEBUG_OUTPUT_CALLBACKS_WIDE Callbacks
+ ) PURE;
+ STDMETHOD(GetOutputLinePrefixWide)(
+ THIS_
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PrefixSize
+ ) PURE;
+ STDMETHOD(SetOutputLinePrefixWide)(
+ THIS_
+ __in_opt PCWSTR Prefix
+ ) PURE;
+
+ STDMETHOD(GetIdentityWide)(
+ THIS_
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG IdentitySize
+ ) PURE;
+ STDMETHOD(OutputIdentityWide)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags,
+ __in PCWSTR Format
+ ) PURE;
+
+ STDMETHOD(GetEventCallbacksWide)(
+ THIS_
+ __out PDEBUG_EVENT_CALLBACKS_WIDE* Callbacks
+ ) PURE;
+ STDMETHOD(SetEventCallbacksWide)(
+ THIS_
+ __in PDEBUG_EVENT_CALLBACKS_WIDE Callbacks
+ ) PURE;
+
+ STDMETHOD(CreateProcess2)(
+ THIS_
+ __in ULONG64 Server,
+ __in PSTR CommandLine,
+ __in_bcount(OptionsBufferSize) PVOID OptionsBuffer,
+ __in ULONG OptionsBufferSize,
+ __in_opt PCSTR InitialDirectory,
+ __in_opt PCSTR Environment
+ ) PURE;
+ STDMETHOD(CreateProcess2Wide)(
+ THIS_
+ __in ULONG64 Server,
+ __in PWSTR CommandLine,
+ __in_bcount(OptionsBufferSize) PVOID OptionsBuffer,
+ __in ULONG OptionsBufferSize,
+ __in_opt PCWSTR InitialDirectory,
+ __in_opt PCWSTR Environment
+ ) PURE;
+ STDMETHOD(CreateProcessAndAttach2)(
+ THIS_
+ __in ULONG64 Server,
+ __in_opt PSTR CommandLine,
+ __in_bcount(OptionsBufferSize) PVOID OptionsBuffer,
+ __in ULONG OptionsBufferSize,
+ __in_opt PCSTR InitialDirectory,
+ __in_opt PCSTR Environment,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+ STDMETHOD(CreateProcessAndAttach2Wide)(
+ THIS_
+ __in ULONG64 Server,
+ __in_opt PWSTR CommandLine,
+ __in_bcount(OptionsBufferSize) PVOID OptionsBuffer,
+ __in ULONG OptionsBufferSize,
+ __in_opt PCWSTR InitialDirectory,
+ __in_opt PCWSTR Environment,
+ __in ULONG ProcessId,
+ __in ULONG AttachFlags
+ ) PURE;
+
+ // Helpers for saving and restoring the
+ // current output line prefix.
+ STDMETHOD(PushOutputLinePrefix)(
+ THIS_
+ __in_opt PCSTR NewPrefix,
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(PushOutputLinePrefixWide)(
+ THIS_
+ __in_opt PCWSTR NewPrefix,
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(PopOutputLinePrefix)(
+ THIS_
+ __in ULONG64 Handle
+ ) PURE;
+
+ // Queries to determine if any clients
+ // could potentially respond to the given callback.
+ STDMETHOD(GetNumberInputCallbacks)(
+ THIS_
+ __out PULONG Count
+ ) PURE;
+ STDMETHOD(GetNumberOutputCallbacks)(
+ THIS_
+ __out PULONG Count
+ ) PURE;
+ STDMETHOD(GetNumberEventCallbacks)(
+ THIS_
+ __in ULONG EventFlags,
+ __out PULONG Count
+ ) PURE;
+
+ // Control over locking the session against
+ // undesired quits. The quit lock string
+ // cannot be retrieved from a secure session.
+ STDMETHOD(GetQuitLockString)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG StringSize
+ ) PURE;
+ STDMETHOD(SetQuitLockString)(
+ THIS_
+ __in PCSTR String
+ ) PURE;
+ STDMETHOD(GetQuitLockStringWide)(
+ THIS_
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG StringSize
+ ) PURE;
+ STDMETHOD(SetQuitLockStringWide)(
+ THIS_
+ __in PCWSTR String
+ ) PURE;
+};
+
+//----------------------------------------------------------------------------
+//
+// IDebugControl.
+//
+//----------------------------------------------------------------------------
+
+// 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_MASK 0xf
+
+// This bit is added in DEBUG_CES_EXECUTION_STATUS
+// notifications when the engines execution status
+// is changing due to operations performed during
+// a wait, such as making synchronous callbacks. If
+// the bit is not set the execution status is changing
+// due to a wait being satisfied.
+#define DEBUG_STATUS_INSIDE_WAIT 0x100000000
+// This bit is added in DEBUG_CES_EXECUTION_STATUS
+// notifications when the engines execution status
+// update is coming after a wait has timed-out.
+// It indicates that the execution status change
+// was not due to an actual event.
+#define DEBUG_STATUS_WAIT_TIMEOUT 0x200000000
+
+// Output control flags.
+// Output generated by methods called by this
+// client will be sent only to this clients
+// output callbacks.
+#define DEBUG_OUTCTL_THIS_CLIENT 0x00000000
+// Output will be sent to all clients.
+#define DEBUG_OUTCTL_ALL_CLIENTS 0x00000001
+// Output will be sent to all clients except
+// the client generating the output.
+#define DEBUG_OUTCTL_ALL_OTHER_CLIENTS 0x00000002
+// Output will be discarded immediately and will not
+// be logged or sent to callbacks.
+#define DEBUG_OUTCTL_IGNORE 0x00000003
+// Output will be logged but not sent to callbacks.
+#define DEBUG_OUTCTL_LOG_ONLY 0x00000004
+// All send control bits.
+#define DEBUG_OUTCTL_SEND_MASK 0x00000007
+// Do not place output from this client in
+// the global log file.
+#define DEBUG_OUTCTL_NOT_LOGGED 0x00000008
+// Send output to clients regardless of whether the
+// mask allows it or not.
+#define DEBUG_OUTCTL_OVERRIDE_MASK 0x00000010
+// Text is markup instead of plain text.
+#define DEBUG_OUTCTL_DML 0x00000020
+
+// Special values which mean leave the output settings
+// unchanged.
+#define DEBUG_OUTCTL_AMBIENT_DML 0xfffffffe
+#define DEBUG_OUTCTL_AMBIENT_TEXT 0xffffffff
+
+// Old ambient flag which maps to text.
+#define DEBUG_OUTCTL_AMBIENT DEBUG_OUTCTL_AMBIENT_TEXT
+
+// Interrupt types.
+// Force a break in if the debuggee is running.
+#define DEBUG_INTERRUPT_ACTIVE 0
+// Notify but do not force a break in.
+#define DEBUG_INTERRUPT_PASSIVE 1
+// Try and get the current engine operation to
+// complete so that the engine will be available
+// again. If no wait is active this is the same
+// as a passive interrupt. If a wait is active
+// this will try to cause the wait to fail without
+// breaking in to the debuggee. There is
+// no guarantee that issuing an exit interrupt
+// will cause the engine to become available
+// as not all operations are arbitrarily
+// interruptible.
+#define DEBUG_INTERRUPT_EXIT 2
+
+// OutputCurrentState flags. These flags
+// allow a particular type of information
+// to be displayed but do not guarantee
+// that it will be displayed. Other global
+// settings may override these flags or
+// the particular state may not be available.
+// For example, source line information may
+// not be present so source line information
+// may not be displayed.
+#define DEBUG_CURRENT_DEFAULT 0x0000000f
+#define DEBUG_CURRENT_SYMBOL 0x00000001
+#define DEBUG_CURRENT_DISASM 0x00000002
+#define DEBUG_CURRENT_REGISTERS 0x00000004
+#define DEBUG_CURRENT_SOURCE_LINE 0x00000008
+
+//
+// Disassemble flags.
+//
+
+// Compute the effective address from current register
+// information and display it.
+#define DEBUG_DISASM_EFFECTIVE_ADDRESS 0x00000001
+// If the current disassembly offset has an exact
+// symbol match output the symbol.
+#define DEBUG_DISASM_MATCHING_SYMBOLS 0x00000002
+// Output the source line number for each disassembly offset.
+#define DEBUG_DISASM_SOURCE_LINE_NUMBER 0x00000004
+// Output the source file name (no path) for each disassembly offset.
+#define DEBUG_DISASM_SOURCE_FILE_NAME 0x00000008
+
+// Code interpretation levels for stepping
+// and other operations.
+#define DEBUG_LEVEL_SOURCE 0
+#define DEBUG_LEVEL_ASSEMBLY 1
+
+// Engine control flags.
+#define DEBUG_ENGOPT_IGNORE_DBGHELP_VERSION 0x00000001
+#define DEBUG_ENGOPT_IGNORE_EXTENSION_VERSIONS 0x00000002
+// If neither allow nor disallow is specified
+// the engine will pick one based on what kind
+// of debugging is going on.
+#define DEBUG_ENGOPT_ALLOW_NETWORK_PATHS 0x00000004
+#define DEBUG_ENGOPT_DISALLOW_NETWORK_PATHS 0x00000008
+#define DEBUG_ENGOPT_NETWORK_PATHS (0x00000004 | 0x00000008)
+// Ignore loader-generated first-chance exceptions.
+#define DEBUG_ENGOPT_IGNORE_LOADER_EXCEPTIONS 0x00000010
+// Break in on a debuggees initial event. In user-mode
+// this will break at the initial system breakpoint
+// for every created process. In kernel-mode it
+// will attempt break in on the target at the first
+// WaitForEvent.
+#define DEBUG_ENGOPT_INITIAL_BREAK 0x00000020
+// Break in on the first module load for a debuggee.
+#define DEBUG_ENGOPT_INITIAL_MODULE_BREAK 0x00000040
+// Break in on a debuggees final event. In user-mode
+// this will break on process exit for every process.
+// In kernel-mode it currently does nothing.
+#define DEBUG_ENGOPT_FINAL_BREAK 0x00000080
+// By default Execute will repeat the last command
+// if it is given an empty string. The flags to
+// Execute can override this behavior for a single
+// command or this engine option can be used to
+// change the default globally.
+#define DEBUG_ENGOPT_NO_EXECUTE_REPEAT 0x00000100
+// Disable places in the engine that have fallback
+// code when presented with incomplete information.
+// 1. Fails minidump module loads unless matching
+// executables can be mapped.
+#define DEBUG_ENGOPT_FAIL_INCOMPLETE_INFORMATION 0x00000200
+// Allow the debugger to manipulate page protections
+// in order to insert code breakpoints on pages that
+// do not have write access. This option is not on
+// by default as it allows breakpoints to be set
+// in potentially hazardous memory areas.
+#define DEBUG_ENGOPT_ALLOW_READ_ONLY_BREAKPOINTS 0x00000400
+// When using a software (bp/bu) breakpoint in code
+// that will be executed by multiple threads it is
+// possible for breakpoint management to cause the
+// breakpoint to be missed or for spurious single-step
+// exceptions to be generated. This flag suspends
+// all but the active thread when doing breakpoint
+// management and thereby avoids multithreading
+// problems. Care must be taken when using it, though,
+// as the suspension of threads can cause deadlocks
+// if the suspended threads are holding resources that
+// the active thread needs. Additionally, there
+// are still rare situations where problems may
+// occur, but setting this flag corrects nearly
+// all multithreading issues with software breakpoints.
+// Thread-restricted stepping and execution supersedes
+// this flags effect.
+// This flag is ignored in kernel sessions as there
+// is no way to restrict processor execution.
+#define DEBUG_ENGOPT_SYNCHRONIZE_BREAKPOINTS 0x00000800
+// Disallows executing shell commands through the
+// engine with .shell (!!).
+#define DEBUG_ENGOPT_DISALLOW_SHELL_COMMANDS 0x00001000
+// Turns on "quiet mode", a somewhat less verbose mode
+// of operation supported in the debuggers that were
+// superseded by dbgeng.dll. This equates to the KDQUIET
+// environment variable.
+#define DEBUG_ENGOPT_KD_QUIET_MODE 0x00002000
+// Disables managed code debugging support in the engine.
+// If managed support is already in use this flag has no effect.
+#define DEBUG_ENGOPT_DISABLE_MANAGED_SUPPORT 0x00004000
+// Disables symbol loading for all modules created
+// after this flag is set.
+#define DEBUG_ENGOPT_DISABLE_MODULE_SYMBOL_LOAD 0x00008000
+// Disables execution commands.
+#define DEBUG_ENGOPT_DISABLE_EXECUTION_COMMANDS 0x00010000
+// Disallows mapping of image files from disk for any use.
+// For example, this disallows image mapping for memory
+// content when debugging minidumps.
+// Does not affect existing mappings, only future attempts.
+#define DEBUG_ENGOPT_DISALLOW_IMAGE_FILE_MAPPING 0x00020000
+// Requests that dbgeng run DML-enhanced versions of commands
+// and operations by default.
+#define DEBUG_ENGOPT_PREFER_DML 0x00040000
+#define DEBUG_ENGOPT_ALL 0x0007FFFF
+
+// 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;
+
+// OutputStackTrace flags.
+// Display a small number of arguments for each call.
+// These may or may not be the actual arguments depending
+// on the architecture, particular function and
+// point during the execution of the function.
+// If the current code level is assembly arguments
+// are dumped as hex values. If the code level is
+// source the engine attempts to provide symbolic
+// argument information.
+#define DEBUG_STACK_ARGUMENTS 0x00000001
+// Displays information about the functions
+// frame such as __stdcall arguments, FPO
+// information and whatever else is available.
+#define DEBUG_STACK_FUNCTION_INFO 0x00000002
+// Displays source line information for each
+// frame of the stack trace.
+#define DEBUG_STACK_SOURCE_LINE 0x00000004
+// Show return, previous frame and other relevant address
+// values for each frame.
+#define DEBUG_STACK_FRAME_ADDRESSES 0x00000008
+// Show column names.
+#define DEBUG_STACK_COLUMN_NAMES 0x00000010
+// Show non-volatile register context for each
+// frame. This is only meaningful for some platforms.
+#define DEBUG_STACK_NONVOLATILE_REGISTERS 0x00000020
+// Show frame numbers
+#define DEBUG_STACK_FRAME_NUMBERS 0x00000040
+// Show typed source parameters.
+#define DEBUG_STACK_PARAMETERS 0x00000080
+// Show just return address in stack frame addresses.
+#define DEBUG_STACK_FRAME_ADDRESSES_RA_ONLY 0x00000100
+// Show frame-to-frame memory usage.
+#define DEBUG_STACK_FRAME_MEMORY_USAGE 0x00000200
+// Show typed source parameters one to a line.
+#define DEBUG_STACK_PARAMETERS_NEWLINE 0x00000400
+// Produce stack output enhanced with DML content.
+#define DEBUG_STACK_DML 0x00000800
+
+// 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
+
+// Specific types of kernel debuggees.
+#define DEBUG_KERNEL_CONNECTION 0
+#define DEBUG_KERNEL_LOCAL 1
+#define DEBUG_KERNEL_EXDI_DRIVER 2
+#define DEBUG_KERNEL_IDNA 3
+
+#define DEBUG_KERNEL_SMALL_DUMP DEBUG_DUMP_SMALL
+#define DEBUG_KERNEL_DUMP DEBUG_DUMP_DEFAULT
+#define DEBUG_KERNEL_FULL_DUMP DEBUG_DUMP_FULL
+
+#define DEBUG_KERNEL_TRACE_LOG DEBUG_DUMP_TRACE_LOG
+
+// Specific types of Windows user debuggees.
+#define DEBUG_USER_WINDOWS_PROCESS 0
+#define DEBUG_USER_WINDOWS_PROCESS_SERVER 1
+#define DEBUG_USER_WINDOWS_IDNA 2
+#define DEBUG_USER_WINDOWS_SMALL_DUMP DEBUG_DUMP_SMALL
+#define DEBUG_USER_WINDOWS_DUMP DEBUG_DUMP_DEFAULT
+#define DEBUG_USER_WINDOWS_DUMP_WINDOWS_CE DEBUG_DUMP_WINDOWS_CE
+
+// Extension flags.
+#define DEBUG_EXTENSION_AT_ENGINE 0x00000000
+
+// 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
+
+// Specific event filter types. Some event
+// filters have optional arguments to further
+// qualify their operation.
+#define DEBUG_FILTER_CREATE_THREAD 0x00000000
+#define DEBUG_FILTER_EXIT_THREAD 0x00000001
+#define DEBUG_FILTER_CREATE_PROCESS 0x00000002
+#define DEBUG_FILTER_EXIT_PROCESS 0x00000003
+// Argument is the name of a module to break on.
+#define DEBUG_FILTER_LOAD_MODULE 0x00000004
+// Argument is the base address of a specific module to break on.
+#define DEBUG_FILTER_UNLOAD_MODULE 0x00000005
+#define DEBUG_FILTER_SYSTEM_ERROR 0x00000006
+// Initial breakpoint and initial module load are one-shot
+// events that are triggered at the appropriate points in
+// the beginning of a session. Their commands are executed
+// and then further processing is controlled by the normal
+// exception and load module filters.
+#define DEBUG_FILTER_INITIAL_BREAKPOINT 0x00000007
+#define DEBUG_FILTER_INITIAL_MODULE_LOAD 0x00000008
+// The debug output filter allows the debugger to stop
+// when output is produced so that the code causing
+// output can be tracked down or synchronized with.
+// This filter is not supported for live dual-machine
+// kernel debugging.
+#define DEBUG_FILTER_DEBUGGEE_OUTPUT 0x00000009
+
+// Event filter execution options.
+// Break in always.
+#define DEBUG_FILTER_BREAK 0x00000000
+// Break in on second-chance exceptions. For events
+// that are not exceptions this is the same as BREAK.
+#define DEBUG_FILTER_SECOND_CHANCE_BREAK 0x00000001
+// Output a message about the event but continue.
+#define DEBUG_FILTER_OUTPUT 0x00000002
+// Continue the event.
+#define DEBUG_FILTER_IGNORE 0x00000003
+// Used to remove general exception filters.
+#define DEBUG_FILTER_REMOVE 0x00000004
+
+// Event filter continuation options. These options are
+// only used when DEBUG_STATUS_GO is used to continue
+// execution. If a specific go status such as
+// DEBUG_STATUS_GO_NOT_HANDLED is used it controls
+// the continuation.
+#define DEBUG_FILTER_GO_HANDLED 0x00000000
+#define DEBUG_FILTER_GO_NOT_HANDLED 0x00000001
+
+// Specific event filter settings.
+typedef struct _DEBUG_SPECIFIC_FILTER_PARAMETERS
+{
+ ULONG ExecutionOption;
+ ULONG ContinueOption;
+ ULONG TextSize;
+ ULONG CommandSize;
+ // If ArgumentSize is zero this filter does
+ // not have an argument. An empty argument for
+ // a filter which does have an argument will take
+ // one byte for the terminator.
+ ULONG ArgumentSize;
+} DEBUG_SPECIFIC_FILTER_PARAMETERS, *PDEBUG_SPECIFIC_FILTER_PARAMETERS;
+
+// Exception event filter settings.
+typedef struct _DEBUG_EXCEPTION_FILTER_PARAMETERS
+{
+ ULONG ExecutionOption;
+ ULONG ContinueOption;
+ ULONG TextSize;
+ ULONG CommandSize;
+ ULONG SecondCommandSize;
+ ULONG ExceptionCode;
+} DEBUG_EXCEPTION_FILTER_PARAMETERS, *PDEBUG_EXCEPTION_FILTER_PARAMETERS;
+
+// Wait flags.
+#define DEBUG_WAIT_DEFAULT 0x00000000
+
+// Last event information structures.
+typedef struct _DEBUG_LAST_EVENT_INFO_BREAKPOINT
+{
+ ULONG Id;
+} DEBUG_LAST_EVENT_INFO_BREAKPOINT, *PDEBUG_LAST_EVENT_INFO_BREAKPOINT;
+
+typedef struct _DEBUG_LAST_EVENT_INFO_EXCEPTION
+{
+ EXCEPTION_RECORD64 ExceptionRecord;
+ ULONG FirstChance;
+} DEBUG_LAST_EVENT_INFO_EXCEPTION, *PDEBUG_LAST_EVENT_INFO_EXCEPTION;
+
+typedef struct _DEBUG_LAST_EVENT_INFO_EXIT_THREAD
+{
+ ULONG ExitCode;
+} DEBUG_LAST_EVENT_INFO_EXIT_THREAD, *PDEBUG_LAST_EVENT_INFO_EXIT_THREAD;
+
+typedef struct _DEBUG_LAST_EVENT_INFO_EXIT_PROCESS
+{
+ ULONG ExitCode;
+} DEBUG_LAST_EVENT_INFO_EXIT_PROCESS, *PDEBUG_LAST_EVENT_INFO_EXIT_PROCESS;
+
+typedef struct _DEBUG_LAST_EVENT_INFO_LOAD_MODULE
+{
+ ULONG64 Base;
+} DEBUG_LAST_EVENT_INFO_LOAD_MODULE, *PDEBUG_LAST_EVENT_INFO_LOAD_MODULE;
+
+typedef struct _DEBUG_LAST_EVENT_INFO_UNLOAD_MODULE
+{
+ ULONG64 Base;
+} DEBUG_LAST_EVENT_INFO_UNLOAD_MODULE, *PDEBUG_LAST_EVENT_INFO_UNLOAD_MODULE;
+
+typedef struct _DEBUG_LAST_EVENT_INFO_SYSTEM_ERROR
+{
+ ULONG Error;
+ ULONG Level;
+} DEBUG_LAST_EVENT_INFO_SYSTEM_ERROR, *PDEBUG_LAST_EVENT_INFO_SYSTEM_ERROR;
+
+// DEBUG_VALUE types.
+#define DEBUG_VALUE_INVALID 0
+#define DEBUG_VALUE_INT8 1
+#define DEBUG_VALUE_INT16 2
+#define DEBUG_VALUE_INT32 3
+#define DEBUG_VALUE_INT64 4
+#define DEBUG_VALUE_FLOAT32 5
+#define DEBUG_VALUE_FLOAT64 6
+#define DEBUG_VALUE_FLOAT80 7
+#define DEBUG_VALUE_FLOAT82 8
+#define DEBUG_VALUE_FLOAT128 9
+#define DEBUG_VALUE_VECTOR64 10
+#define DEBUG_VALUE_VECTOR128 11
+// Count of type indices.
+#define DEBUG_VALUE_TYPES 12
+
+#if defined(_MSC_VER)
+#if _MSC_VER >= 800
+#if _MSC_VER >= 1200
+#pragma warning(push)
+#endif
+#pragma warning(disable:4201) /* Nameless struct/union */
+#endif
+#endif
+
+// We want the DEBUG_VALUE structure to have 8-byte alignment
+// and be 32 bytes total. This is tricky because the compiler
+// wants to pad the union of values out to a even 8-byte multiple,
+// pushing the type out too far. We can't use 4-packing because
+// then the 8-byte alignment requirement is lost, so instead
+// we shrink the union to 24 bytes and have a reserved field
+// before the type field. The same amount of space is available
+// and everybody's happy, but the structure is somewhat unusual.
+
+typedef struct _DEBUG_VALUE
+{
+ union
+ {
+ UCHAR I8;
+ USHORT I16;
+ ULONG I32;
+ struct
+ {
+ // Extra NAT indicator for IA64
+ // integer registers. NAT will
+ // always be false for other CPUs.
+ ULONG64 I64;
+ BOOL Nat;
+ };
+ float F32;
+ double F64;
+ UCHAR F80Bytes[10];
+ UCHAR F82Bytes[11];
+ UCHAR F128Bytes[16];
+ // Vector interpretations. The actual number
+ // of valid elements depends on the vector length.
+ UCHAR VI8[16];
+ USHORT VI16[8];
+ ULONG VI32[4];
+ ULONG64 VI64[2];
+ float VF32[4];
+ double VF64[2];
+ struct
+ {
+ ULONG LowPart;
+ ULONG HighPart;
+ } I64Parts32;
+ struct
+ {
+ ULONG64 LowPart;
+ LONG64 HighPart;
+ } F128Parts64;
+ // Allows raw byte access to content. Array
+ // can be indexed for as much data as Type
+ // describes. This array also serves to pad
+ // the structure out to 32 bytes and reserves
+ // space for future members.
+ UCHAR RawBytes[24];
+ };
+ ULONG TailOfRawBytes;
+ ULONG Type;
+} DEBUG_VALUE, *PDEBUG_VALUE;
+
+#if defined(_MSC_VER)
+#if _MSC_VER >= 800
+#if _MSC_VER >= 1200
+#pragma warning(pop)
+#else
+#pragma warning(default:4201) /* Nameless struct/union */
+#endif
+#endif
+#endif
+
+#undef INTERFACE
+#define INTERFACE IDebugControl
+DECLARE_INTERFACE_(IDebugControl, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugControl.
+
+ // Checks for a user interrupt, such a Ctrl-C
+ // or stop button.
+ // This method is reentrant.
+ STDMETHOD(GetInterrupt)(
+ THIS
+ ) PURE;
+ // Registers a user interrupt.
+ // This method is reentrant.
+ STDMETHOD(SetInterrupt)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // Interrupting a user-mode process requires
+ // access to some system resources that the
+ // process may hold itself, preventing the
+ // interrupt from occurring. The engine
+ // will time-out pending interrupt requests
+ // and simulate an interrupt if necessary.
+ // These methods control the interrupt timeout.
+ STDMETHOD(GetInterruptTimeout)(
+ THIS_
+ __out PULONG Seconds
+ ) PURE;
+ STDMETHOD(SetInterruptTimeout)(
+ THIS_
+ __in ULONG Seconds
+ ) PURE;
+
+ STDMETHOD(GetLogFile)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG FileSize,
+ __out PBOOL Append
+ ) PURE;
+ // Opens a log file which collects all
+ // output. Output from every client except
+ // those that explicitly disable logging
+ // goes into the log.
+ // Opening a log file closes any log file
+ // already open.
+ STDMETHOD(OpenLogFile)(
+ THIS_
+ __in PCSTR File,
+ __in BOOL Append
+ ) PURE;
+ STDMETHOD(CloseLogFile)(
+ THIS
+ ) PURE;
+ // Controls what output is logged.
+ STDMETHOD(GetLogMask)(
+ THIS_
+ __out PULONG Mask
+ ) PURE;
+ STDMETHOD(SetLogMask)(
+ THIS_
+ __in ULONG Mask
+ ) PURE;
+
+ // Input requests input from all clients.
+ // The first input that is returned is used
+ // to satisfy the call. Other returned
+ // input is discarded.
+ STDMETHOD(Input)(
+ THIS_
+ __out_ecount(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG InputSize
+ ) PURE;
+ // This method is used by clients to return
+ // input when it is available. It will
+ // return S_OK if the input is used to
+ // satisfy an Input call and S_FALSE if
+ // the input is ignored.
+ // This method is reentrant.
+ STDMETHOD(ReturnInput)(
+ THIS_
+ __in PCSTR Buffer
+ ) PURE;
+
+ // Sends output through clients
+ // output callbacks if the mask is allowed
+ // by the current output control mask and
+ // according to the output distribution
+ // settings.
+ STDMETHODV(Output)(
+ THIS_
+ __in ULONG Mask,
+ __in PCSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(OutputVaList)(
+ THIS_
+ __in ULONG Mask,
+ __in PCSTR Format,
+ __in va_list Args
+ ) PURE;
+ // 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.
+ STDMETHODV(ControlledOutput)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Mask,
+ __in PCSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(ControlledOutputVaList)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Mask,
+ __in PCSTR Format,
+ __in va_list Args
+ ) PURE;
+
+ // Displays the standard command-line prompt
+ // followed by the given output. If Format
+ // is NULL no additional output is produced.
+ // Output is produced under the
+ // DEBUG_OUTPUT_PROMPT mask.
+ // This method only outputs the prompt; it
+ // does not get input.
+ STDMETHODV(OutputPrompt)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_opt PCSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(OutputPromptVaList)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_opt PCSTR Format,
+ __in va_list Args
+ ) PURE;
+ // Gets the text that would be displayed by OutputPrompt.
+ STDMETHOD(GetPromptText)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG TextSize
+ ) PURE;
+ // Outputs information about the current
+ // debuggee state such as a register
+ // summary, disassembly at the current PC,
+ // closest symbol and others.
+ // Uses the line prefix.
+ STDMETHOD(OutputCurrentState)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags
+ ) PURE;
+
+ // Outputs the debugger and extension version
+ // information. This method is reentrant.
+ // Uses the line prefix.
+ STDMETHOD(OutputVersionInformation)(
+ THIS_
+ __in ULONG OutputControl
+ ) PURE;
+
+ // In user-mode debugging sessions the
+ // engine will set an event when
+ // exceptions are continued. This can
+ // be used to synchronize other processes
+ // with the debuggers handling of events.
+ // For example, this is used to support
+ // the e argument to ntsd.
+ STDMETHOD(GetNotifyEventHandle)(
+ THIS_
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(SetNotifyEventHandle)(
+ THIS_
+ __in ULONG64 Handle
+ ) PURE;
+
+ STDMETHOD(Assemble)(
+ THIS_
+ __in ULONG64 Offset,
+ __in PCSTR Instr,
+ __out PULONG64 EndOffset
+ ) PURE;
+ STDMETHOD(Disassemble)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DisassemblySize,
+ __out PULONG64 EndOffset
+ ) PURE;
+ // Returns the value of the effective address
+ // computed for the last Disassemble, if there
+ // was one.
+ STDMETHOD(GetDisassembleEffectiveOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Uses the line prefix if necessary.
+ STDMETHOD(OutputDisassembly)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out PULONG64 EndOffset
+ ) PURE;
+ // Produces multiple lines of disassembly output.
+ // There will be PreviousLines of disassembly before
+ // the given offset if a valid disassembly exists.
+ // In all, there will be TotalLines of output produced.
+ // The first and last line offsets are returned
+ // specially and all lines offsets can be retrieved
+ // through LineOffsets. LineOffsets will contain
+ // offsets for each line where disassembly started.
+ // When disassembly of a single instruction takes
+ // multiple lines the initial offset will be followed
+ // by DEBUG_INVALID_OFFSET.
+ // Uses the line prefix.
+ STDMETHOD(OutputDisassemblyLines)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG PreviousLines,
+ __in ULONG TotalLines,
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out_opt PULONG OffsetLine,
+ __out_opt PULONG64 StartOffset,
+ __out_opt PULONG64 EndOffset,
+ __out_ecount_opt(TotalLines) PULONG64 LineOffsets
+ ) PURE;
+ // Returns the offset of the start of
+ // the instruction thats the given
+ // delta away from the instruction
+ // at the initial offset.
+ // This routine does not check for
+ // validity of the instruction or
+ // the memory containing it.
+ STDMETHOD(GetNearInstruction)(
+ THIS_
+ __in ULONG64 Offset,
+ __in LONG Delta,
+ __out PULONG64 NearOffset
+ ) PURE;
+
+ // Offsets can be passed in as zero to use the current
+ // thread state.
+ STDMETHOD(GetStackTrace)(
+ THIS_
+ __in ULONG64 FrameOffset,
+ __in ULONG64 StackOffset,
+ __in ULONG64 InstructionOffset,
+ __out_ecount(FramesSize) PDEBUG_STACK_FRAME Frames,
+ __in ULONG FramesSize,
+ __out_opt PULONG FramesFilled
+ ) PURE;
+ // Does a simple stack trace to determine
+ // what the current return address is.
+ STDMETHOD(GetReturnOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // If Frames is NULL OutputStackTrace will
+ // use GetStackTrace to get FramesSize frames
+ // and then output them. The current register
+ // values for frame, stack and instruction offsets
+ // are used.
+ // Uses the line prefix.
+ STDMETHOD(OutputStackTrace)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_ecount_opt(FramesSize) PDEBUG_STACK_FRAME Frames,
+ __in ULONG FramesSize,
+ __in ULONG Flags
+ ) PURE;
+
+ // Returns information about the debuggee such
+ // as user vs. kernel, dump vs. live, etc.
+ STDMETHOD(GetDebuggeeType)(
+ THIS_
+ __out PULONG Class,
+ __out PULONG Qualifier
+ ) PURE;
+ // Returns the type of physical processors in
+ // the machine.
+ // Returns one of the IMAGE_FILE_MACHINE values.
+ STDMETHOD(GetActualProcessorType)(
+ THIS_
+ __out PULONG Type
+ ) PURE;
+ // Returns the type of processor used in the
+ // current processor context.
+ STDMETHOD(GetExecutingProcessorType)(
+ THIS_
+ __out PULONG Type
+ ) PURE;
+ // Query all the possible processor types that
+ // may be encountered during this debug session.
+ STDMETHOD(GetNumberPossibleExecutingProcessorTypes)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetPossibleExecutingProcessorTypes)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PULONG Types
+ ) PURE;
+ // Get the number of actual processors in
+ // the machine.
+ STDMETHOD(GetNumberProcessors)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ // PlatformId is one of the VER_PLATFORM values.
+ // Major and minor are as given in the NT
+ // kernel debugger protocol.
+ // ServicePackString and ServicePackNumber indicate the
+ // system service pack level. ServicePackNumber is not
+ // available in some sessions where the service pack level
+ // is only expressed as a string. The service pack information
+ // will be empty if the system does not have a service pack
+ // applied.
+ // The build string is string information identifying the
+ // particular build of the system. The build string is
+ // empty if the system has no particular identifying
+ // information.
+ STDMETHOD(GetSystemVersion)(
+ THIS_
+ __out PULONG PlatformId,
+ __out PULONG Major,
+ __out PULONG Minor,
+ __out_ecount_opt(ServicePackStringSize) PSTR ServicePackString,
+ __in ULONG ServicePackStringSize,
+ __out_opt PULONG ServicePackStringUsed,
+ __out PULONG ServicePackNumber,
+ __out_ecount_opt(BuildStringSize) PSTR BuildString,
+ __in ULONG BuildStringSize,
+ __out_opt PULONG BuildStringUsed
+ ) PURE;
+ // Returns the page size for the currently executing
+ // processor context. The page size may vary between
+ // processor types.
+ STDMETHOD(GetPageSize)(
+ THIS_
+ __out PULONG Size
+ ) PURE;
+ // Returns S_OK if the current processor context uses
+ // 64-bit addresses, otherwise S_FALSE.
+ STDMETHOD(IsPointer64Bit)(
+ THIS
+ ) PURE;
+ // Reads the bugcheck data area and returns the
+ // current contents. This method only works
+ // in kernel debugging sessions.
+ STDMETHOD(ReadBugCheckData)(
+ THIS_
+ __out PULONG Code,
+ __out PULONG64 Arg1,
+ __out PULONG64 Arg2,
+ __out PULONG64 Arg3,
+ __out PULONG64 Arg4
+ ) PURE;
+
+ // Query all the processor types supported by
+ // the engine. This is a complete list and is
+ // not related to the machine running the engine
+ // or the debuggee.
+ STDMETHOD(GetNumberSupportedProcessorTypes)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetSupportedProcessorTypes)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PULONG Types
+ ) PURE;
+ // Returns a full, descriptive name and an
+ // abbreviated name for a processor type.
+ STDMETHOD(GetProcessorTypeNames)(
+ THIS_
+ __in ULONG Type,
+ __out_ecount_opt(FullNameBufferSize) PSTR FullNameBuffer,
+ __in ULONG FullNameBufferSize,
+ __out_opt PULONG FullNameSize,
+ __out_ecount_opt(AbbrevNameBufferSize) PSTR AbbrevNameBuffer,
+ __in ULONG AbbrevNameBufferSize,
+ __out_opt PULONG AbbrevNameSize
+ ) PURE;
+
+ // Gets and sets the type of processor to
+ // use when doing things like setting
+ // breakpoints, accessing registers,
+ // getting stack traces and so on.
+ STDMETHOD(GetEffectiveProcessorType)(
+ THIS_
+ __out PULONG Type
+ ) PURE;
+ STDMETHOD(SetEffectiveProcessorType)(
+ THIS_
+ __in ULONG Type
+ ) PURE;
+
+ // Returns information about whether and how
+ // the debuggee is running. Status will
+ // be GO if the debuggee is running and
+ // BREAK if it isnt.
+ // If no debuggee exists the status is
+ // NO_DEBUGGEE.
+ // This method is reentrant.
+ STDMETHOD(GetExecutionStatus)(
+ THIS_
+ __out PULONG Status
+ ) PURE;
+ // Changes the execution status of the
+ // engine from stopped to running.
+ // Status must be one of the go or step
+ // status values.
+ STDMETHOD(SetExecutionStatus)(
+ THIS_
+ __in ULONG Status
+ ) PURE;
+
+ // Controls what code interpretation level the debugger
+ // runs at. The debugger checks the code level when
+ // deciding whether to step by a source line or
+ // assembly instruction along with other related operations.
+ STDMETHOD(GetCodeLevel)(
+ THIS_
+ __out PULONG Level
+ ) PURE;
+ STDMETHOD(SetCodeLevel)(
+ THIS_
+ __in ULONG Level
+ ) PURE;
+
+ // Gets and sets engine control flags.
+ // These methods are reentrant.
+ STDMETHOD(GetEngineOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddEngineOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveEngineOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetEngineOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ // Gets and sets control values for
+ // handling system error events.
+ // If the system error level is less
+ // than or equal to the given levels
+ // the error may be displayed and
+ // the default break for the event
+ // may be set.
+ STDMETHOD(GetSystemErrorControl)(
+ THIS_
+ __out PULONG OutputLevel,
+ __out PULONG BreakLevel
+ ) PURE;
+ STDMETHOD(SetSystemErrorControl)(
+ THIS_
+ __in ULONG OutputLevel,
+ __in ULONG BreakLevel
+ ) PURE;
+
+ // The command processor supports simple
+ // string replacement macros in Evaluate and
+ // Execute. There are currently ten macro
+ // slots available. Slots 0-9 map to
+ // the command invocations $u0-$u9.
+ STDMETHOD(GetTextMacro)(
+ THIS_
+ __in ULONG Slot,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG MacroSize
+ ) PURE;
+ STDMETHOD(SetTextMacro)(
+ THIS_
+ __in ULONG Slot,
+ __in PCSTR Macro
+ ) PURE;
+
+ // Controls the default number radix used
+ // in expressions and commands.
+ STDMETHOD(GetRadix)(
+ THIS_
+ __out PULONG Radix
+ ) PURE;
+ STDMETHOD(SetRadix)(
+ THIS_
+ __in ULONG Radix
+ ) PURE;
+
+ // Evaluates the given expression string and
+ // returns the resulting value.
+ // If DesiredType is DEBUG_VALUE_INVALID then
+ // the natural type is used.
+ // RemainderIndex, if provided, is set to the index
+ // of the first character in the input string that was
+ // not used when evaluating the expression.
+ STDMETHOD(Evaluate)(
+ THIS_
+ __in PCSTR Expression,
+ __in ULONG DesiredType,
+ __out PDEBUG_VALUE Value,
+ __out_opt PULONG RemainderIndex
+ ) PURE;
+ // Attempts to convert the input value to a value
+ // of the requested type in the output value.
+ // Conversions can fail if no conversion exists.
+ // Successful conversions may be lossy.
+ STDMETHOD(CoerceValue)(
+ THIS_
+ __in PDEBUG_VALUE In,
+ __in ULONG OutType,
+ __out PDEBUG_VALUE Out
+ ) PURE;
+ STDMETHOD(CoerceValues)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount(Count) PDEBUG_VALUE In,
+ __in_ecount(Count) PULONG OutTypes,
+ __out_ecount(Count) PDEBUG_VALUE Out
+ ) PURE;
+
+ // 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.
+ STDMETHOD(Execute)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCSTR Command,
+ __in ULONG Flags
+ ) PURE;
+ // Executes the given command file by
+ // reading a line at a time and processing
+ // it with Execute.
+ STDMETHOD(ExecuteCommandFile)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCSTR CommandFile,
+ __in ULONG Flags
+ ) PURE;
+
+ // Breakpoint interfaces are described
+ // elsewhere in this section.
+ STDMETHOD(GetNumberBreakpoints)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ // It is possible for this retrieval function to
+ // fail even with an index within the number of
+ // existing breakpoints if the breakpoint is
+ // a private breakpoint.
+ STDMETHOD(GetBreakpointByIndex)(
+ THIS_
+ __in ULONG Index,
+ __out PDEBUG_BREAKPOINT* Bp
+ ) PURE;
+ STDMETHOD(GetBreakpointById)(
+ THIS_
+ __in ULONG Id,
+ __out PDEBUG_BREAKPOINT* Bp
+ ) PURE;
+ // If Ids is non-NULL the Count breakpoints
+ // referred to in the Ids array are returned,
+ // otherwise breakpoints from index Start to
+ // Start + Count 1 are returned.
+ STDMETHOD(GetBreakpointParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Ids,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_BREAKPOINT_PARAMETERS Params
+ ) PURE;
+ // Breakpoints are created empty and disabled.
+ // When their parameters have been set they
+ // should be enabled by setting the ENABLE flag.
+ // If DesiredId is DEBUG_ANY_ID then the
+ // engine picks an unused ID. If DesiredId
+ // is any other number the engine attempts
+ // to use the given ID for the breakpoint.
+ // If another breakpoint exists with that ID
+ // the call will fail.
+ STDMETHOD(AddBreakpoint)(
+ THIS_
+ __in ULONG Type,
+ __in ULONG DesiredId,
+ __out PDEBUG_BREAKPOINT* Bp
+ ) PURE;
+ // Breakpoint interface is invalid after this call.
+ STDMETHOD(RemoveBreakpoint)(
+ THIS_
+ __in PDEBUG_BREAKPOINT Bp
+ ) PURE;
+
+ // Control and use extension DLLs.
+ STDMETHOD(AddExtension)(
+ THIS_
+ __in PCSTR Path,
+ __in ULONG Flags,
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(RemoveExtension)(
+ THIS_
+ __in ULONG64 Handle
+ ) PURE;
+ STDMETHOD(GetExtensionByPath)(
+ THIS_
+ __in PCSTR Path,
+ __out PULONG64 Handle
+ ) PURE;
+ // If Handle is zero the extension
+ // chain is walked searching for the
+ // function.
+ STDMETHOD(CallExtension)(
+ THIS_
+ __in ULONG64 Handle,
+ __in PCSTR Function,
+ __in_opt PCSTR Arguments
+ ) PURE;
+ // GetExtensionFunction works like
+ // GetProcAddress on extension DLLs
+ // to allow raw function-call-level
+ // interaction with extension DLLs.
+ // Such functions do not need to
+ // follow the standard extension prototype
+ // if they are not going to be called
+ // through the text extension interface.
+ // _EFN_ is automatically prepended to
+ // the name string given.
+ // This function cannot be called remotely.
+ STDMETHOD(GetExtensionFunction)(
+ THIS_
+ __in ULONG64 Handle,
+ __in PCSTR FuncName,
+ __out FARPROC* Function
+ ) PURE;
+ // These methods return alternate
+ // extension interfaces in order to allow
+ // interface-style extension DLLs to mix in
+ // older extension calls.
+ // Structure sizes must be initialized before
+ // the call.
+ // These methods cannot be called remotely.
+ STDMETHOD(GetWindbgExtensionApis32)(
+ THIS_
+ __inout PWINDBG_EXTENSION_APIS32 Api
+ ) PURE;
+ STDMETHOD(GetWindbgExtensionApis64)(
+ THIS_
+ __inout PWINDBG_EXTENSION_APIS64 Api
+ ) PURE;
+
+ // The engine provides a simple mechanism
+ // to filter common events. Arbitrarily complicated
+ // filtering can be done by registering event callbacks
+ // but simple event filtering only requires
+ // setting the options of one of the predefined
+ // event filters.
+ // Simple event filters are either for specific
+ // events and therefore have an enumerant or
+ // they are for an exception and are based on
+ // the exceptions code. Exception filters
+ // are further divided into exceptions specially
+ // handled by the engine, which is a fixed set,
+ // and arbitrary exceptions.
+ // All three groups of filters are indexed together
+ // with the specific filters first, then the specific
+ // exception filters and finally the arbitrary
+ // exception filters.
+ // The first specific exception is the default
+ // exception. If an exception event occurs for
+ // an exception without settings the default
+ // exception settings are used.
+ STDMETHOD(GetNumberEventFilters)(
+ THIS_
+ __out PULONG SpecificEvents,
+ __out PULONG SpecificExceptions,
+ __out PULONG ArbitraryExceptions
+ ) PURE;
+ // Some filters have descriptive text associated with them.
+ STDMETHOD(GetEventFilterText)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG TextSize
+ ) PURE;
+ // All filters support executing a command when the
+ // event occurs.
+ STDMETHOD(GetEventFilterCommand)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG CommandSize
+ ) PURE;
+ STDMETHOD(SetEventFilterCommand)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Command
+ ) PURE;
+ STDMETHOD(GetSpecificFilterParameters)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PDEBUG_SPECIFIC_FILTER_PARAMETERS Params
+ ) PURE;
+ STDMETHOD(SetSpecificFilterParameters)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __in_ecount(Count) PDEBUG_SPECIFIC_FILTER_PARAMETERS Params
+ ) PURE;
+ // Some specific filters have arguments to further
+ // qualify their operation.
+ STDMETHOD(GetSpecificFilterArgument)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ArgumentSize
+ ) PURE;
+ STDMETHOD(SetSpecificFilterArgument)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Argument
+ ) PURE;
+ // If Codes is non-NULL Start is ignored.
+ STDMETHOD(GetExceptionFilterParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Codes,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_EXCEPTION_FILTER_PARAMETERS Params
+ ) PURE;
+ // The codes in the parameter data control the application
+ // of the parameter data. If a code is not already in
+ // the set of filters it is added. If the ExecutionOption
+ // for a code is REMOVE then the filter is removed.
+ // Specific exception filters cannot be removed.
+ STDMETHOD(SetExceptionFilterParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount(Count) PDEBUG_EXCEPTION_FILTER_PARAMETERS Params
+ ) PURE;
+ // Exception filters support an additional command for
+ // second-chance events.
+ STDMETHOD(GetExceptionFilterSecondCommand)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG CommandSize
+ ) PURE;
+ STDMETHOD(SetExceptionFilterSecondCommand)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Command
+ ) PURE;
+
+ // Yields processing to the engine until
+ // an event occurs. This method may
+ // only be called by the thread that started
+ // the debug session.
+ // When an event occurs the engine carries
+ // out all event processing such as calling
+ // callbacks.
+ // If the callbacks indicate that execution should
+ // break the wait will return, otherwise it
+ // goes back to waiting for a new event.
+ // If the timeout expires, S_FALSE is returned.
+ // The timeout is not currently supported for
+ // kernel debugging.
+ STDMETHOD(WaitForEvent)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG Timeout
+ ) PURE;
+
+ // 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.
+ STDMETHOD(GetLastEventInformation)(
+ THIS_
+ __out PULONG Type,
+ __out PULONG ProcessId,
+ __out PULONG ThreadId,
+ __out_bcount_opt(ExtraInformationSize) PVOID ExtraInformation,
+ __in ULONG ExtraInformationSize,
+ __out_opt PULONG ExtraInformationUsed,
+ __out_ecount_opt(DescriptionSize) PSTR Description,
+ __in ULONG DescriptionSize,
+ __out_opt PULONG DescriptionUsed
+ ) PURE;
+};
+
+// OutputTextReplacements flags.
+#define DEBUG_OUT_TEXT_REPL_DEFAULT 0x00000000
+
+#undef INTERFACE
+#define INTERFACE IDebugControl2
+DECLARE_INTERFACE_(IDebugControl2, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugControl.
+
+ // Checks for a user interrupt, such a Ctrl-C
+ // or stop button.
+ // This method is reentrant.
+ STDMETHOD(GetInterrupt)(
+ THIS
+ ) PURE;
+ // Registers a user interrupt.
+ // This method is reentrant.
+ STDMETHOD(SetInterrupt)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // Interrupting a user-mode process requires
+ // access to some system resources that the
+ // process may hold itself, preventing the
+ // interrupt from occurring. The engine
+ // will time-out pending interrupt requests
+ // and simulate an interrupt if necessary.
+ // These methods control the interrupt timeout.
+ STDMETHOD(GetInterruptTimeout)(
+ THIS_
+ __out PULONG Seconds
+ ) PURE;
+ STDMETHOD(SetInterruptTimeout)(
+ THIS_
+ __in ULONG Seconds
+ ) PURE;
+
+ STDMETHOD(GetLogFile)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG FileSize,
+ __out PBOOL Append
+ ) PURE;
+ // Opens a log file which collects all
+ // output. Output from every client except
+ // those that explicitly disable logging
+ // goes into the log.
+ // Opening a log file closes any log file
+ // already open.
+ STDMETHOD(OpenLogFile)(
+ THIS_
+ __in PCSTR File,
+ __in BOOL Append
+ ) PURE;
+ STDMETHOD(CloseLogFile)(
+ THIS
+ ) PURE;
+ // Controls what output is logged.
+ STDMETHOD(GetLogMask)(
+ THIS_
+ __out PULONG Mask
+ ) PURE;
+ STDMETHOD(SetLogMask)(
+ THIS_
+ __in ULONG Mask
+ ) PURE;
+
+ // Input requests input from all clients.
+ // The first input that is returned is used
+ // to satisfy the call. Other returned
+ // input is discarded.
+ STDMETHOD(Input)(
+ THIS_
+ __out_ecount(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG InputSize
+ ) PURE;
+ // This method is used by clients to return
+ // input when it is available. It will
+ // return S_OK if the input is used to
+ // satisfy an Input call and S_FALSE if
+ // the input is ignored.
+ // This method is reentrant.
+ STDMETHOD(ReturnInput)(
+ THIS_
+ __in PCSTR Buffer
+ ) PURE;
+
+ // Sends output through clients
+ // output callbacks if the mask is allowed
+ // by the current output control mask and
+ // according to the output distribution
+ // settings.
+ STDMETHODV(Output)(
+ THIS_
+ __in ULONG Mask,
+ __in PCSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(OutputVaList)(
+ THIS_
+ __in ULONG Mask,
+ __in PCSTR Format,
+ __in va_list Args
+ ) PURE;
+ // 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.
+ STDMETHODV(ControlledOutput)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Mask,
+ __in PCSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(ControlledOutputVaList)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Mask,
+ __in PCSTR Format,
+ __in va_list Args
+ ) PURE;
+
+ // Displays the standard command-line prompt
+ // followed by the given output. If Format
+ // is NULL no additional output is produced.
+ // Output is produced under the
+ // DEBUG_OUTPUT_PROMPT mask.
+ // This method only outputs the prompt; it
+ // does not get input.
+ STDMETHODV(OutputPrompt)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_opt PCSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(OutputPromptVaList)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_opt PCSTR Format,
+ __in va_list Args
+ ) PURE;
+ // Gets the text that would be displayed by OutputPrompt.
+ STDMETHOD(GetPromptText)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG TextSize
+ ) PURE;
+ // Outputs information about the current
+ // debuggee state such as a register
+ // summary, disassembly at the current PC,
+ // closest symbol and others.
+ // Uses the line prefix.
+ STDMETHOD(OutputCurrentState)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags
+ ) PURE;
+
+ // Outputs the debugger and extension version
+ // information. This method is reentrant.
+ // Uses the line prefix.
+ STDMETHOD(OutputVersionInformation)(
+ THIS_
+ __in ULONG OutputControl
+ ) PURE;
+
+ // In user-mode debugging sessions the
+ // engine will set an event when
+ // exceptions are continued. This can
+ // be used to synchronize other processes
+ // with the debuggers handling of events.
+ // For example, this is used to support
+ // the e argument to ntsd.
+ STDMETHOD(GetNotifyEventHandle)(
+ THIS_
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(SetNotifyEventHandle)(
+ THIS_
+ __in ULONG64 Handle
+ ) PURE;
+
+ STDMETHOD(Assemble)(
+ THIS_
+ __in ULONG64 Offset,
+ __in PCSTR Instr,
+ __out PULONG64 EndOffset
+ ) PURE;
+ STDMETHOD(Disassemble)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DisassemblySize,
+ __out PULONG64 EndOffset
+ ) PURE;
+ // Returns the value of the effective address
+ // computed for the last Disassemble, if there
+ // was one.
+ STDMETHOD(GetDisassembleEffectiveOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Uses the line prefix if necessary.
+ STDMETHOD(OutputDisassembly)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out PULONG64 EndOffset
+ ) PURE;
+ // Produces multiple lines of disassembly output.
+ // There will be PreviousLines of disassembly before
+ // the given offset if a valid disassembly exists.
+ // In all, there will be TotalLines of output produced.
+ // The first and last line offsets are returned
+ // specially and all lines offsets can be retrieved
+ // through LineOffsets. LineOffsets will contain
+ // offsets for each line where disassembly started.
+ // When disassembly of a single instruction takes
+ // multiple lines the initial offset will be followed
+ // by DEBUG_INVALID_OFFSET.
+ // Uses the line prefix.
+ STDMETHOD(OutputDisassemblyLines)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG PreviousLines,
+ __in ULONG TotalLines,
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out_opt PULONG OffsetLine,
+ __out_opt PULONG64 StartOffset,
+ __out_opt PULONG64 EndOffset,
+ __out_ecount_opt(TotalLines) PULONG64 LineOffsets
+ ) PURE;
+ // Returns the offset of the start of
+ // the instruction thats the given
+ // delta away from the instruction
+ // at the initial offset.
+ // This routine does not check for
+ // validity of the instruction or
+ // the memory containing it.
+ STDMETHOD(GetNearInstruction)(
+ THIS_
+ __in ULONG64 Offset,
+ __in LONG Delta,
+ __out PULONG64 NearOffset
+ ) PURE;
+
+ // Offsets can be passed in as zero to use the current
+ // thread state.
+ STDMETHOD(GetStackTrace)(
+ THIS_
+ __in ULONG64 FrameOffset,
+ __in ULONG64 StackOffset,
+ __in ULONG64 InstructionOffset,
+ __out_ecount(FramesSize) PDEBUG_STACK_FRAME Frames,
+ __in ULONG FramesSize,
+ __out_opt PULONG FramesFilled
+ ) PURE;
+ // Does a simple stack trace to determine
+ // what the current return address is.
+ STDMETHOD(GetReturnOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // If Frames is NULL OutputStackTrace will
+ // use GetStackTrace to get FramesSize frames
+ // and then output them. The current register
+ // values for frame, stack and instruction offsets
+ // are used.
+ // Uses the line prefix.
+ STDMETHOD(OutputStackTrace)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_ecount_opt(FramesSize) PDEBUG_STACK_FRAME Frames,
+ __in ULONG FramesSize,
+ __in ULONG Flags
+ ) PURE;
+
+ // Returns information about the debuggee such
+ // as user vs. kernel, dump vs. live, etc.
+ STDMETHOD(GetDebuggeeType)(
+ THIS_
+ __out PULONG Class,
+ __out PULONG Qualifier
+ ) PURE;
+ // Returns the type of physical processors in
+ // the machine.
+ // Returns one of the IMAGE_FILE_MACHINE values.
+ STDMETHOD(GetActualProcessorType)(
+ THIS_
+ __out PULONG Type
+ ) PURE;
+ // Returns the type of processor used in the
+ // current processor context.
+ STDMETHOD(GetExecutingProcessorType)(
+ THIS_
+ __out PULONG Type
+ ) PURE;
+ // Query all the possible processor types that
+ // may be encountered during this debug session.
+ STDMETHOD(GetNumberPossibleExecutingProcessorTypes)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetPossibleExecutingProcessorTypes)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PULONG Types
+ ) PURE;
+ // Get the number of actual processors in
+ // the machine.
+ STDMETHOD(GetNumberProcessors)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ // PlatformId is one of the VER_PLATFORM values.
+ // Major and minor are as given in the NT
+ // kernel debugger protocol.
+ // ServicePackString and ServicePackNumber indicate the
+ // system service pack level. ServicePackNumber is not
+ // available in some sessions where the service pack level
+ // is only expressed as a string. The service pack information
+ // will be empty if the system does not have a service pack
+ // applied.
+ // The build string is string information identifying the
+ // particular build of the system. The build string is
+ // empty if the system has no particular identifying
+ // information.
+ STDMETHOD(GetSystemVersion)(
+ THIS_
+ __out PULONG PlatformId,
+ __out PULONG Major,
+ __out PULONG Minor,
+ __out_ecount_opt(ServicePackStringSize) PSTR ServicePackString,
+ __in ULONG ServicePackStringSize,
+ __out_opt PULONG ServicePackStringUsed,
+ __out PULONG ServicePackNumber,
+ __out_ecount_opt(BuildStringSize) PSTR BuildString,
+ __in ULONG BuildStringSize,
+ __out_opt PULONG BuildStringUsed
+ ) PURE;
+ // Returns the page size for the currently executing
+ // processor context. The page size may vary between
+ // processor types.
+ STDMETHOD(GetPageSize)(
+ THIS_
+ __out PULONG Size
+ ) PURE;
+ // Returns S_OK if the current processor context uses
+ // 64-bit addresses, otherwise S_FALSE.
+ STDMETHOD(IsPointer64Bit)(
+ THIS
+ ) PURE;
+ // Reads the bugcheck data area and returns the
+ // current contents. This method only works
+ // in kernel debugging sessions.
+ STDMETHOD(ReadBugCheckData)(
+ THIS_
+ __out PULONG Code,
+ __out PULONG64 Arg1,
+ __out PULONG64 Arg2,
+ __out PULONG64 Arg3,
+ __out PULONG64 Arg4
+ ) PURE;
+
+ // Query all the processor types supported by
+ // the engine. This is a complete list and is
+ // not related to the machine running the engine
+ // or the debuggee.
+ STDMETHOD(GetNumberSupportedProcessorTypes)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetSupportedProcessorTypes)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PULONG Types
+ ) PURE;
+ // Returns a full, descriptive name and an
+ // abbreviated name for a processor type.
+ STDMETHOD(GetProcessorTypeNames)(
+ THIS_
+ __in ULONG Type,
+ __out_ecount_opt(FullNameBufferSize) PSTR FullNameBuffer,
+ __in ULONG FullNameBufferSize,
+ __out_opt PULONG FullNameSize,
+ __out_ecount_opt(AbbrevNameBufferSize) PSTR AbbrevNameBuffer,
+ __in ULONG AbbrevNameBufferSize,
+ __out_opt PULONG AbbrevNameSize
+ ) PURE;
+
+ // Gets and sets the type of processor to
+ // use when doing things like setting
+ // breakpoints, accessing registers,
+ // getting stack traces and so on.
+ STDMETHOD(GetEffectiveProcessorType)(
+ THIS_
+ __out PULONG Type
+ ) PURE;
+ STDMETHOD(SetEffectiveProcessorType)(
+ THIS_
+ __in ULONG Type
+ ) PURE;
+
+ // Returns information about whether and how
+ // the debuggee is running. Status will
+ // be GO if the debuggee is running and
+ // BREAK if it isnt.
+ // If no debuggee exists the status is
+ // NO_DEBUGGEE.
+ // This method is reentrant.
+ STDMETHOD(GetExecutionStatus)(
+ THIS_
+ __out PULONG Status
+ ) PURE;
+ // Changes the execution status of the
+ // engine from stopped to running.
+ // Status must be one of the go or step
+ // status values.
+ STDMETHOD(SetExecutionStatus)(
+ THIS_
+ __in ULONG Status
+ ) PURE;
+
+ // Controls what code interpretation level the debugger
+ // runs at. The debugger checks the code level when
+ // deciding whether to step by a source line or
+ // assembly instruction along with other related operations.
+ STDMETHOD(GetCodeLevel)(
+ THIS_
+ __out PULONG Level
+ ) PURE;
+ STDMETHOD(SetCodeLevel)(
+ THIS_
+ __in ULONG Level
+ ) PURE;
+
+ // Gets and sets engine control flags.
+ // These methods are reentrant.
+ STDMETHOD(GetEngineOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddEngineOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveEngineOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetEngineOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ // Gets and sets control values for
+ // handling system error events.
+ // If the system error level is less
+ // than or equal to the given levels
+ // the error may be displayed and
+ // the default break for the event
+ // may be set.
+ STDMETHOD(GetSystemErrorControl)(
+ THIS_
+ __out PULONG OutputLevel,
+ __out PULONG BreakLevel
+ ) PURE;
+ STDMETHOD(SetSystemErrorControl)(
+ THIS_
+ __in ULONG OutputLevel,
+ __in ULONG BreakLevel
+ ) PURE;
+
+ // The command processor supports simple
+ // string replacement macros in Evaluate and
+ // Execute. There are currently ten macro
+ // slots available. Slots 0-9 map to
+ // the command invocations $u0-$u9.
+ STDMETHOD(GetTextMacro)(
+ THIS_
+ __in ULONG Slot,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG MacroSize
+ ) PURE;
+ STDMETHOD(SetTextMacro)(
+ THIS_
+ __in ULONG Slot,
+ __in PCSTR Macro
+ ) PURE;
+
+ // Controls the default number radix used
+ // in expressions and commands.
+ STDMETHOD(GetRadix)(
+ THIS_
+ __out PULONG Radix
+ ) PURE;
+ STDMETHOD(SetRadix)(
+ THIS_
+ __in ULONG Radix
+ ) PURE;
+
+ // Evaluates the given expression string and
+ // returns the resulting value.
+ // If DesiredType is DEBUG_VALUE_INVALID then
+ // the natural type is used.
+ // RemainderIndex, if provided, is set to the index
+ // of the first character in the input string that was
+ // not used when evaluating the expression.
+ STDMETHOD(Evaluate)(
+ THIS_
+ __in PCSTR Expression,
+ __in ULONG DesiredType,
+ __out PDEBUG_VALUE Value,
+ __out_opt PULONG RemainderIndex
+ ) PURE;
+ // Attempts to convert the input value to a value
+ // of the requested type in the output value.
+ // Conversions can fail if no conversion exists.
+ // Successful conversions may be lossy.
+ STDMETHOD(CoerceValue)(
+ THIS_
+ __in PDEBUG_VALUE In,
+ __in ULONG OutType,
+ __out PDEBUG_VALUE Out
+ ) PURE;
+ STDMETHOD(CoerceValues)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount(Count) PDEBUG_VALUE In,
+ __in_ecount(Count) PULONG OutTypes,
+ __out_ecount(Count) PDEBUG_VALUE Out
+ ) PURE;
+
+ // 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.
+ STDMETHOD(Execute)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCSTR Command,
+ __in ULONG Flags
+ ) PURE;
+ // Executes the given command file by
+ // reading a line at a time and processing
+ // it with Execute.
+ STDMETHOD(ExecuteCommandFile)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCSTR CommandFile,
+ __in ULONG Flags
+ ) PURE;
+
+ // Breakpoint interfaces are described
+ // elsewhere in this section.
+ STDMETHOD(GetNumberBreakpoints)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ // It is possible for this retrieval function to
+ // fail even with an index within the number of
+ // existing breakpoints if the breakpoint is
+ // a private breakpoint.
+ STDMETHOD(GetBreakpointByIndex)(
+ THIS_
+ __in ULONG Index,
+ __out PDEBUG_BREAKPOINT* Bp
+ ) PURE;
+ STDMETHOD(GetBreakpointById)(
+ THIS_
+ __in ULONG Id,
+ __out PDEBUG_BREAKPOINT* Bp
+ ) PURE;
+ // If Ids is non-NULL the Count breakpoints
+ // referred to in the Ids array are returned,
+ // otherwise breakpoints from index Start to
+ // Start + Count 1 are returned.
+ STDMETHOD(GetBreakpointParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Ids,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_BREAKPOINT_PARAMETERS Params
+ ) PURE;
+ // Breakpoints are created empty and disabled.
+ // When their parameters have been set they
+ // should be enabled by setting the ENABLE flag.
+ // If DesiredId is DEBUG_ANY_ID then the
+ // engine picks an unused ID. If DesiredId
+ // is any other number the engine attempts
+ // to use the given ID for the breakpoint.
+ // If another breakpoint exists with that ID
+ // the call will fail.
+ STDMETHOD(AddBreakpoint)(
+ THIS_
+ __in ULONG Type,
+ __in ULONG DesiredId,
+ __out PDEBUG_BREAKPOINT* Bp
+ ) PURE;
+ // Breakpoint interface is invalid after this call.
+ STDMETHOD(RemoveBreakpoint)(
+ THIS_
+ __in PDEBUG_BREAKPOINT Bp
+ ) PURE;
+
+ // Control and use extension DLLs.
+ STDMETHOD(AddExtension)(
+ THIS_
+ __in PCSTR Path,
+ __in ULONG Flags,
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(RemoveExtension)(
+ THIS_
+ __in ULONG64 Handle
+ ) PURE;
+ STDMETHOD(GetExtensionByPath)(
+ THIS_
+ __in PCSTR Path,
+ __out PULONG64 Handle
+ ) PURE;
+ // If Handle is zero the extension
+ // chain is walked searching for the
+ // function.
+ STDMETHOD(CallExtension)(
+ THIS_
+ __in ULONG64 Handle,
+ __in PCSTR Function,
+ __in_opt PCSTR Arguments
+ ) PURE;
+ // GetExtensionFunction works like
+ // GetProcAddress on extension DLLs
+ // to allow raw function-call-level
+ // interaction with extension DLLs.
+ // Such functions do not need to
+ // follow the standard extension prototype
+ // if they are not going to be called
+ // through the text extension interface.
+ // This function cannot be called remotely.
+ STDMETHOD(GetExtensionFunction)(
+ THIS_
+ __in ULONG64 Handle,
+ __in PCSTR FuncName,
+ __out FARPROC* Function
+ ) PURE;
+ // These methods return alternate
+ // extension interfaces in order to allow
+ // interface-style extension DLLs to mix in
+ // older extension calls.
+ // Structure sizes must be initialized before
+ // the call.
+ // These methods cannot be called remotely.
+ STDMETHOD(GetWindbgExtensionApis32)(
+ THIS_
+ __inout PWINDBG_EXTENSION_APIS32 Api
+ ) PURE;
+ STDMETHOD(GetWindbgExtensionApis64)(
+ THIS_
+ __inout PWINDBG_EXTENSION_APIS64 Api
+ ) PURE;
+
+ // The engine provides a simple mechanism
+ // to filter common events. Arbitrarily complicated
+ // filtering can be done by registering event callbacks
+ // but simple event filtering only requires
+ // setting the options of one of the predefined
+ // event filters.
+ // Simple event filters are either for specific
+ // events and therefore have an enumerant or
+ // they are for an exception and are based on
+ // the exceptions code. Exception filters
+ // are further divided into exceptions specially
+ // handled by the engine, which is a fixed set,
+ // and arbitrary exceptions.
+ // All three groups of filters are indexed together
+ // with the specific filters first, then the specific
+ // exception filters and finally the arbitrary
+ // exception filters.
+ // The first specific exception is the default
+ // exception. If an exception event occurs for
+ // an exception without settings the default
+ // exception settings are used.
+ STDMETHOD(GetNumberEventFilters)(
+ THIS_
+ __out PULONG SpecificEvents,
+ __out PULONG SpecificExceptions,
+ __out PULONG ArbitraryExceptions
+ ) PURE;
+ // Some filters have descriptive text associated with them.
+ STDMETHOD(GetEventFilterText)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG TextSize
+ ) PURE;
+ // All filters support executing a command when the
+ // event occurs.
+ STDMETHOD(GetEventFilterCommand)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG CommandSize
+ ) PURE;
+ STDMETHOD(SetEventFilterCommand)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Command
+ ) PURE;
+ STDMETHOD(GetSpecificFilterParameters)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PDEBUG_SPECIFIC_FILTER_PARAMETERS Params
+ ) PURE;
+ STDMETHOD(SetSpecificFilterParameters)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __in_ecount(Count) PDEBUG_SPECIFIC_FILTER_PARAMETERS Params
+ ) PURE;
+ // Some specific filters have arguments to further
+ // qualify their operation.
+ STDMETHOD(GetSpecificFilterArgument)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ArgumentSize
+ ) PURE;
+ STDMETHOD(SetSpecificFilterArgument)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Argument
+ ) PURE;
+ // If Codes is non-NULL Start is ignored.
+ STDMETHOD(GetExceptionFilterParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Codes,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_EXCEPTION_FILTER_PARAMETERS Params
+ ) PURE;
+ // The codes in the parameter data control the application
+ // of the parameter data. If a code is not already in
+ // the set of filters it is added. If the ExecutionOption
+ // for a code is REMOVE then the filter is removed.
+ // Specific exception filters cannot be removed.
+ STDMETHOD(SetExceptionFilterParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount(Count) PDEBUG_EXCEPTION_FILTER_PARAMETERS Params
+ ) PURE;
+ // Exception filters support an additional command for
+ // second-chance events.
+ STDMETHOD(GetExceptionFilterSecondCommand)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG CommandSize
+ ) PURE;
+ STDMETHOD(SetExceptionFilterSecondCommand)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Command
+ ) PURE;
+
+ // Yields processing to the engine until
+ // an event occurs. This method may
+ // only be called by the thread that started
+ // the debug session.
+ // When an event occurs the engine carries
+ // out all event processing such as calling
+ // callbacks.
+ // If the callbacks indicate that execution should
+ // break the wait will return, otherwise it
+ // goes back to waiting for a new event.
+ // If the timeout expires, S_FALSE is returned.
+ // The timeout is not currently supported for
+ // kernel debugging.
+ STDMETHOD(WaitForEvent)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG Timeout
+ ) PURE;
+
+ // 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.
+ STDMETHOD(GetLastEventInformation)(
+ THIS_
+ __out PULONG Type,
+ __out PULONG ProcessId,
+ __out PULONG ThreadId,
+ __out_bcount_opt(ExtraInformationSize) PVOID ExtraInformation,
+ __in ULONG ExtraInformationSize,
+ __out_opt PULONG ExtraInformationUsed,
+ __out_ecount_opt(DescriptionSize) PSTR Description,
+ __in ULONG DescriptionSize,
+ __out_opt PULONG DescriptionUsed
+ ) PURE;
+
+ // IDebugControl2.
+
+ STDMETHOD(GetCurrentTimeDate)(
+ THIS_
+ __out PULONG TimeDate
+ ) PURE;
+ // Retrieves the number of seconds since the
+ // machine started running.
+ STDMETHOD(GetCurrentSystemUpTime)(
+ THIS_
+ __out PULONG UpTime
+ ) PURE;
+
+ // If the current session is a dump session,
+ // retrieves any extended format information.
+ STDMETHOD(GetDumpFormatFlags)(
+ THIS_
+ __out PULONG FormatFlags
+ ) PURE;
+
+ // The debugger has been enhanced to allow
+ // arbitrary text replacements in addition
+ // to the simple $u0-$u9 text macros.
+ // Text replacement takes a given source
+ // text in commands and converts it to the
+ // given destination text. Replacements
+ // are named by their source text so that
+ // only one replacement for a source text
+ // string can exist.
+ STDMETHOD(GetNumberTextReplacements)(
+ THIS_
+ __out PULONG NumRepl
+ ) PURE;
+ // If SrcText is non-NULL the replacement
+ // is looked up by source text, otherwise
+ // Index is used to get the Nth replacement.
+ STDMETHOD(GetTextReplacement)(
+ THIS_
+ __in_opt PCSTR SrcText,
+ __in ULONG Index,
+ __out_ecount_opt(SrcBufferSize) PSTR SrcBuffer,
+ __in ULONG SrcBufferSize,
+ __out_opt PULONG SrcSize,
+ __out_ecount_opt(DstBufferSize) PSTR DstBuffer,
+ __in ULONG DstBufferSize,
+ __out_opt PULONG DstSize
+ ) PURE;
+ // Setting the destination text to
+ // NULL removes the alias.
+ STDMETHOD(SetTextReplacement)(
+ THIS_
+ __in PCSTR SrcText,
+ __in_opt PCSTR DstText
+ ) PURE;
+ STDMETHOD(RemoveTextReplacements)(
+ THIS
+ ) PURE;
+ // Outputs the complete list of current
+ // replacements.
+ STDMETHOD(OutputTextReplacements)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags
+ ) PURE;
+};
+
+//
+// Assembly/disassembly options.
+//
+// The specific effects of these flags varies depending
+// on the particular instruction set.
+//
+
+#define DEBUG_ASMOPT_DEFAULT 0x00000000
+// Display additional information in disassembly.
+#define DEBUG_ASMOPT_VERBOSE 0x00000001
+// Do not display raw code bytes in disassembly.
+#define DEBUG_ASMOPT_NO_CODE_BYTES 0x00000002
+// Do not take the output width into account when
+// formatting disassembly.
+#define DEBUG_ASMOPT_IGNORE_OUTPUT_WIDTH 0x00000004
+// Display source file line number before each line if available.
+#define DEBUG_ASMOPT_SOURCE_LINE_NUMBER 0x00000008
+
+//
+// Expression syntax options.
+//
+
+// MASM-style expression evaluation.
+#define DEBUG_EXPR_MASM 0x00000000
+// C++-style expression evaluation.
+#define DEBUG_EXPR_CPLUSPLUS 0x00000001
+
+//
+// Event index description information.
+//
+
+#define DEBUG_EINDEX_NAME 0x00000000
+
+//
+// SetNextEventIndex relation options.
+//
+
+// Value increases forward from the first index.
+#define DEBUG_EINDEX_FROM_START 0x00000000
+// Value increases backwards from the last index.
+#define DEBUG_EINDEX_FROM_END 0x00000001
+// Value is a signed delta from the current index.
+#define DEBUG_EINDEX_FROM_CURRENT 0x00000002
+
+#undef INTERFACE
+#define INTERFACE IDebugControl3
+DECLARE_INTERFACE_(IDebugControl3, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugControl.
+
+ // Checks for a user interrupt, such a Ctrl-C
+ // or stop button.
+ // This method is reentrant.
+ STDMETHOD(GetInterrupt)(
+ THIS
+ ) PURE;
+ // Registers a user interrupt.
+ // This method is reentrant.
+ STDMETHOD(SetInterrupt)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // Interrupting a user-mode process requires
+ // access to some system resources that the
+ // process may hold itself, preventing the
+ // interrupt from occurring. The engine
+ // will time-out pending interrupt requests
+ // and simulate an interrupt if necessary.
+ // These methods control the interrupt timeout.
+ STDMETHOD(GetInterruptTimeout)(
+ THIS_
+ __out PULONG Seconds
+ ) PURE;
+ STDMETHOD(SetInterruptTimeout)(
+ THIS_
+ __in ULONG Seconds
+ ) PURE;
+
+ STDMETHOD(GetLogFile)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG FileSize,
+ __out PBOOL Append
+ ) PURE;
+ // Opens a log file which collects all
+ // output. Output from every client except
+ // those that explicitly disable logging
+ // goes into the log.
+ // Opening a log file closes any log file
+ // already open.
+ STDMETHOD(OpenLogFile)(
+ THIS_
+ __in PCSTR File,
+ __in BOOL Append
+ ) PURE;
+ STDMETHOD(CloseLogFile)(
+ THIS
+ ) PURE;
+ // Controls what output is logged.
+ STDMETHOD(GetLogMask)(
+ THIS_
+ __out PULONG Mask
+ ) PURE;
+ STDMETHOD(SetLogMask)(
+ THIS_
+ __in ULONG Mask
+ ) PURE;
+
+ // Input requests input from all clients.
+ // The first input that is returned is used
+ // to satisfy the call. Other returned
+ // input is discarded.
+ STDMETHOD(Input)(
+ THIS_
+ __out_ecount(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG InputSize
+ ) PURE;
+ // This method is used by clients to return
+ // input when it is available. It will
+ // return S_OK if the input is used to
+ // satisfy an Input call and S_FALSE if
+ // the input is ignored.
+ // This method is reentrant.
+ STDMETHOD(ReturnInput)(
+ THIS_
+ __in PCSTR Buffer
+ ) PURE;
+
+ // Sends output through clients
+ // output callbacks if the mask is allowed
+ // by the current output control mask and
+ // according to the output distribution
+ // settings.
+ STDMETHODV(Output)(
+ THIS_
+ __in ULONG Mask,
+ __in PCSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(OutputVaList)(
+ THIS_
+ __in ULONG Mask,
+ __in PCSTR Format,
+ __in va_list Args
+ ) PURE;
+ // 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.
+ STDMETHODV(ControlledOutput)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Mask,
+ __in PCSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(ControlledOutputVaList)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Mask,
+ __in PCSTR Format,
+ __in va_list Args
+ ) PURE;
+
+ // Displays the standard command-line prompt
+ // followed by the given output. If Format
+ // is NULL no additional output is produced.
+ // Output is produced under the
+ // DEBUG_OUTPUT_PROMPT mask.
+ // This method only outputs the prompt; it
+ // does not get input.
+ STDMETHODV(OutputPrompt)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_opt PCSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(OutputPromptVaList)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_opt PCSTR Format,
+ __in va_list Args
+ ) PURE;
+ // Gets the text that would be displayed by OutputPrompt.
+ STDMETHOD(GetPromptText)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG TextSize
+ ) PURE;
+ // Outputs information about the current
+ // debuggee state such as a register
+ // summary, disassembly at the current PC,
+ // closest symbol and others.
+ // Uses the line prefix.
+ STDMETHOD(OutputCurrentState)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags
+ ) PURE;
+
+ // Outputs the debugger and extension version
+ // information. This method is reentrant.
+ // Uses the line prefix.
+ STDMETHOD(OutputVersionInformation)(
+ THIS_
+ __in ULONG OutputControl
+ ) PURE;
+
+ // In user-mode debugging sessions the
+ // engine will set an event when
+ // exceptions are continued. This can
+ // be used to synchronize other processes
+ // with the debuggers handling of events.
+ // For example, this is used to support
+ // the e argument to ntsd.
+ STDMETHOD(GetNotifyEventHandle)(
+ THIS_
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(SetNotifyEventHandle)(
+ THIS_
+ __in ULONG64 Handle
+ ) PURE;
+
+ STDMETHOD(Assemble)(
+ THIS_
+ __in ULONG64 Offset,
+ __in PCSTR Instr,
+ __out PULONG64 EndOffset
+ ) PURE;
+ STDMETHOD(Disassemble)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DisassemblySize,
+ __out PULONG64 EndOffset
+ ) PURE;
+ // Returns the value of the effective address
+ // computed for the last Disassemble, if there
+ // was one.
+ STDMETHOD(GetDisassembleEffectiveOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Uses the line prefix if necessary.
+ STDMETHOD(OutputDisassembly)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out PULONG64 EndOffset
+ ) PURE;
+ // Produces multiple lines of disassembly output.
+ // There will be PreviousLines of disassembly before
+ // the given offset if a valid disassembly exists.
+ // In all, there will be TotalLines of output produced.
+ // The first and last line offsets are returned
+ // specially and all lines offsets can be retrieved
+ // through LineOffsets. LineOffsets will contain
+ // offsets for each line where disassembly started.
+ // When disassembly of a single instruction takes
+ // multiple lines the initial offset will be followed
+ // by DEBUG_INVALID_OFFSET.
+ // Uses the line prefix.
+ STDMETHOD(OutputDisassemblyLines)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG PreviousLines,
+ __in ULONG TotalLines,
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out_opt PULONG OffsetLine,
+ __out_opt PULONG64 StartOffset,
+ __out_opt PULONG64 EndOffset,
+ __out_ecount_opt(TotalLines) PULONG64 LineOffsets
+ ) PURE;
+ // Returns the offset of the start of
+ // the instruction thats the given
+ // delta away from the instruction
+ // at the initial offset.
+ // This routine does not check for
+ // validity of the instruction or
+ // the memory containing it.
+ STDMETHOD(GetNearInstruction)(
+ THIS_
+ __in ULONG64 Offset,
+ __in LONG Delta,
+ __out PULONG64 NearOffset
+ ) PURE;
+
+ // Offsets can be passed in as zero to use the current
+ // thread state.
+ STDMETHOD(GetStackTrace)(
+ THIS_
+ __in ULONG64 FrameOffset,
+ __in ULONG64 StackOffset,
+ __in ULONG64 InstructionOffset,
+ __out_ecount(FramesSize) PDEBUG_STACK_FRAME Frames,
+ __in ULONG FramesSize,
+ __out_opt PULONG FramesFilled
+ ) PURE;
+ // Does a simple stack trace to determine
+ // what the current return address is.
+ STDMETHOD(GetReturnOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // If Frames is NULL OutputStackTrace will
+ // use GetStackTrace to get FramesSize frames
+ // and then output them. The current register
+ // values for frame, stack and instruction offsets
+ // are used.
+ // Uses the line prefix.
+ STDMETHOD(OutputStackTrace)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_ecount_opt(FramesSize) PDEBUG_STACK_FRAME Frames,
+ __in ULONG FramesSize,
+ __in ULONG Flags
+ ) PURE;
+
+ // Returns information about the debuggee such
+ // as user vs. kernel, dump vs. live, etc.
+ STDMETHOD(GetDebuggeeType)(
+ THIS_
+ __out PULONG Class,
+ __out PULONG Qualifier
+ ) PURE;
+ // Returns the type of physical processors in
+ // the machine.
+ // Returns one of the IMAGE_FILE_MACHINE values.
+ STDMETHOD(GetActualProcessorType)(
+ THIS_
+ __out PULONG Type
+ ) PURE;
+ // Returns the type of processor used in the
+ // current processor context.
+ STDMETHOD(GetExecutingProcessorType)(
+ THIS_
+ __out PULONG Type
+ ) PURE;
+ // Query all the possible processor types that
+ // may be encountered during this debug session.
+ STDMETHOD(GetNumberPossibleExecutingProcessorTypes)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetPossibleExecutingProcessorTypes)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PULONG Types
+ ) PURE;
+ // Get the number of actual processors in
+ // the machine.
+ STDMETHOD(GetNumberProcessors)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ // PlatformId is one of the VER_PLATFORM values.
+ // Major and minor are as given in the NT
+ // kernel debugger protocol.
+ // ServicePackString and ServicePackNumber indicate the
+ // system service pack level. ServicePackNumber is not
+ // available in some sessions where the service pack level
+ // is only expressed as a string. The service pack information
+ // will be empty if the system does not have a service pack
+ // applied.
+ // The build string is string information identifying the
+ // particular build of the system. The build string is
+ // empty if the system has no particular identifying
+ // information.
+ STDMETHOD(GetSystemVersion)(
+ THIS_
+ __out PULONG PlatformId,
+ __out PULONG Major,
+ __out PULONG Minor,
+ __out_ecount_opt(ServicePackStringSize) PSTR ServicePackString,
+ __in ULONG ServicePackStringSize,
+ __out_opt PULONG ServicePackStringUsed,
+ __out PULONG ServicePackNumber,
+ __out_ecount_opt(BuildStringSize) PSTR BuildString,
+ __in ULONG BuildStringSize,
+ __out_opt PULONG BuildStringUsed
+ ) PURE;
+ // Returns the page size for the currently executing
+ // processor context. The page size may vary between
+ // processor types.
+ STDMETHOD(GetPageSize)(
+ THIS_
+ __out PULONG Size
+ ) PURE;
+ // Returns S_OK if the current processor context uses
+ // 64-bit addresses, otherwise S_FALSE.
+ STDMETHOD(IsPointer64Bit)(
+ THIS
+ ) PURE;
+ // Reads the bugcheck data area and returns the
+ // current contents. This method only works
+ // in kernel debugging sessions.
+ STDMETHOD(ReadBugCheckData)(
+ THIS_
+ __out PULONG Code,
+ __out PULONG64 Arg1,
+ __out PULONG64 Arg2,
+ __out PULONG64 Arg3,
+ __out PULONG64 Arg4
+ ) PURE;
+
+ // Query all the processor types supported by
+ // the engine. This is a complete list and is
+ // not related to the machine running the engine
+ // or the debuggee.
+ STDMETHOD(GetNumberSupportedProcessorTypes)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetSupportedProcessorTypes)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PULONG Types
+ ) PURE;
+ // Returns a full, descriptive name and an
+ // abbreviated name for a processor type.
+ STDMETHOD(GetProcessorTypeNames)(
+ THIS_
+ __in ULONG Type,
+ __out_ecount_opt(FullNameBufferSize) PSTR FullNameBuffer,
+ __in ULONG FullNameBufferSize,
+ __out_opt PULONG FullNameSize,
+ __out_ecount_opt(AbbrevNameBufferSize) PSTR AbbrevNameBuffer,
+ __in ULONG AbbrevNameBufferSize,
+ __out_opt PULONG AbbrevNameSize
+ ) PURE;
+
+ // Gets and sets the type of processor to
+ // use when doing things like setting
+ // breakpoints, accessing registers,
+ // getting stack traces and so on.
+ STDMETHOD(GetEffectiveProcessorType)(
+ THIS_
+ __out PULONG Type
+ ) PURE;
+ STDMETHOD(SetEffectiveProcessorType)(
+ THIS_
+ __in ULONG Type
+ ) PURE;
+
+ // Returns information about whether and how
+ // the debuggee is running. Status will
+ // be GO if the debuggee is running and
+ // BREAK if it isnt.
+ // If no debuggee exists the status is
+ // NO_DEBUGGEE.
+ // This method is reentrant.
+ STDMETHOD(GetExecutionStatus)(
+ THIS_
+ __out PULONG Status
+ ) PURE;
+ // Changes the execution status of the
+ // engine from stopped to running.
+ // Status must be one of the go or step
+ // status values.
+ STDMETHOD(SetExecutionStatus)(
+ THIS_
+ __in ULONG Status
+ ) PURE;
+
+ // Controls what code interpretation level the debugger
+ // runs at. The debugger checks the code level when
+ // deciding whether to step by a source line or
+ // assembly instruction along with other related operations.
+ STDMETHOD(GetCodeLevel)(
+ THIS_
+ __out PULONG Level
+ ) PURE;
+ STDMETHOD(SetCodeLevel)(
+ THIS_
+ __in ULONG Level
+ ) PURE;
+
+ // Gets and sets engine control flags.
+ // These methods are reentrant.
+ STDMETHOD(GetEngineOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddEngineOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveEngineOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetEngineOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ // Gets and sets control values for
+ // handling system error events.
+ // If the system error level is less
+ // than or equal to the given levels
+ // the error may be displayed and
+ // the default break for the event
+ // may be set.
+ STDMETHOD(GetSystemErrorControl)(
+ THIS_
+ __out PULONG OutputLevel,
+ __out PULONG BreakLevel
+ ) PURE;
+ STDMETHOD(SetSystemErrorControl)(
+ THIS_
+ __in ULONG OutputLevel,
+ __in ULONG BreakLevel
+ ) PURE;
+
+ // The command processor supports simple
+ // string replacement macros in Evaluate and
+ // Execute. There are currently ten macro
+ // slots available. Slots 0-9 map to
+ // the command invocations $u0-$u9.
+ STDMETHOD(GetTextMacro)(
+ THIS_
+ __in ULONG Slot,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG MacroSize
+ ) PURE;
+ STDMETHOD(SetTextMacro)(
+ THIS_
+ __in ULONG Slot,
+ __in PCSTR Macro
+ ) PURE;
+
+ // Controls the default number radix used
+ // in expressions and commands.
+ STDMETHOD(GetRadix)(
+ THIS_
+ __out PULONG Radix
+ ) PURE;
+ STDMETHOD(SetRadix)(
+ THIS_
+ __in ULONG Radix
+ ) PURE;
+
+ // Evaluates the given expression string and
+ // returns the resulting value.
+ // If DesiredType is DEBUG_VALUE_INVALID then
+ // the natural type is used.
+ // RemainderIndex, if provided, is set to the index
+ // of the first character in the input string that was
+ // not used when evaluating the expression.
+ STDMETHOD(Evaluate)(
+ THIS_
+ __in PCSTR Expression,
+ __in ULONG DesiredType,
+ __out PDEBUG_VALUE Value,
+ __out_opt PULONG RemainderIndex
+ ) PURE;
+ // Attempts to convert the input value to a value
+ // of the requested type in the output value.
+ // Conversions can fail if no conversion exists.
+ // Successful conversions may be lossy.
+ STDMETHOD(CoerceValue)(
+ THIS_
+ __in PDEBUG_VALUE In,
+ __in ULONG OutType,
+ __out PDEBUG_VALUE Out
+ ) PURE;
+ STDMETHOD(CoerceValues)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount(Count) PDEBUG_VALUE In,
+ __in_ecount(Count) PULONG OutTypes,
+ __out_ecount(Count) PDEBUG_VALUE Out
+ ) PURE;
+
+ // 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.
+ STDMETHOD(Execute)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCSTR Command,
+ __in ULONG Flags
+ ) PURE;
+ // Executes the given command file by
+ // reading a line at a time and processing
+ // it with Execute.
+ STDMETHOD(ExecuteCommandFile)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCSTR CommandFile,
+ __in ULONG Flags
+ ) PURE;
+
+ // Breakpoint interfaces are described
+ // elsewhere in this section.
+ STDMETHOD(GetNumberBreakpoints)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ // It is possible for this retrieval function to
+ // fail even with an index within the number of
+ // existing breakpoints if the breakpoint is
+ // a private breakpoint.
+ STDMETHOD(GetBreakpointByIndex)(
+ THIS_
+ __in ULONG Index,
+ __out PDEBUG_BREAKPOINT* Bp
+ ) PURE;
+ STDMETHOD(GetBreakpointById)(
+ THIS_
+ __in ULONG Id,
+ __out PDEBUG_BREAKPOINT* Bp
+ ) PURE;
+ // If Ids is non-NULL the Count breakpoints
+ // referred to in the Ids array are returned,
+ // otherwise breakpoints from index Start to
+ // Start + Count 1 are returned.
+ STDMETHOD(GetBreakpointParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Ids,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_BREAKPOINT_PARAMETERS Params
+ ) PURE;
+ // Breakpoints are created empty and disabled.
+ // When their parameters have been set they
+ // should be enabled by setting the ENABLE flag.
+ // If DesiredId is DEBUG_ANY_ID then the
+ // engine picks an unused ID. If DesiredId
+ // is any other number the engine attempts
+ // to use the given ID for the breakpoint.
+ // If another breakpoint exists with that ID
+ // the call will fail.
+ STDMETHOD(AddBreakpoint)(
+ THIS_
+ __in ULONG Type,
+ __in ULONG DesiredId,
+ __out PDEBUG_BREAKPOINT* Bp
+ ) PURE;
+ // Breakpoint interface is invalid after this call.
+ STDMETHOD(RemoveBreakpoint)(
+ THIS_
+ __in PDEBUG_BREAKPOINT Bp
+ ) PURE;
+
+ // Control and use extension DLLs.
+ STDMETHOD(AddExtension)(
+ THIS_
+ __in PCSTR Path,
+ __in ULONG Flags,
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(RemoveExtension)(
+ THIS_
+ __in ULONG64 Handle
+ ) PURE;
+ STDMETHOD(GetExtensionByPath)(
+ THIS_
+ __in PCSTR Path,
+ __out PULONG64 Handle
+ ) PURE;
+ // If Handle is zero the extension
+ // chain is walked searching for the
+ // function.
+ STDMETHOD(CallExtension)(
+ THIS_
+ __in ULONG64 Handle,
+ __in PCSTR Function,
+ __in_opt PCSTR Arguments
+ ) PURE;
+ // GetExtensionFunction works like
+ // GetProcAddress on extension DLLs
+ // to allow raw function-call-level
+ // interaction with extension DLLs.
+ // Such functions do not need to
+ // follow the standard extension prototype
+ // if they are not going to be called
+ // through the text extension interface.
+ // This function cannot be called remotely.
+ STDMETHOD(GetExtensionFunction)(
+ THIS_
+ __in ULONG64 Handle,
+ __in PCSTR FuncName,
+ __out FARPROC* Function
+ ) PURE;
+ // These methods return alternate
+ // extension interfaces in order to allow
+ // interface-style extension DLLs to mix in
+ // older extension calls.
+ // Structure sizes must be initialized before
+ // the call.
+ // These methods cannot be called remotely.
+ STDMETHOD(GetWindbgExtensionApis32)(
+ THIS_
+ __inout PWINDBG_EXTENSION_APIS32 Api
+ ) PURE;
+ STDMETHOD(GetWindbgExtensionApis64)(
+ THIS_
+ __inout PWINDBG_EXTENSION_APIS64 Api
+ ) PURE;
+
+ // The engine provides a simple mechanism
+ // to filter common events. Arbitrarily complicated
+ // filtering can be done by registering event callbacks
+ // but simple event filtering only requires
+ // setting the options of one of the predefined
+ // event filters.
+ // Simple event filters are either for specific
+ // events and therefore have an enumerant or
+ // they are for an exception and are based on
+ // the exceptions code. Exception filters
+ // are further divided into exceptions specially
+ // handled by the engine, which is a fixed set,
+ // and arbitrary exceptions.
+ // All three groups of filters are indexed together
+ // with the specific filters first, then the specific
+ // exception filters and finally the arbitrary
+ // exception filters.
+ // The first specific exception is the default
+ // exception. If an exception event occurs for
+ // an exception without settings the default
+ // exception settings are used.
+ STDMETHOD(GetNumberEventFilters)(
+ THIS_
+ __out PULONG SpecificEvents,
+ __out PULONG SpecificExceptions,
+ __out PULONG ArbitraryExceptions
+ ) PURE;
+ // Some filters have descriptive text associated with them.
+ STDMETHOD(GetEventFilterText)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG TextSize
+ ) PURE;
+ // All filters support executing a command when the
+ // event occurs.
+ STDMETHOD(GetEventFilterCommand)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG CommandSize
+ ) PURE;
+ STDMETHOD(SetEventFilterCommand)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Command
+ ) PURE;
+ STDMETHOD(GetSpecificFilterParameters)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PDEBUG_SPECIFIC_FILTER_PARAMETERS Params
+ ) PURE;
+ STDMETHOD(SetSpecificFilterParameters)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __in_ecount(Count) PDEBUG_SPECIFIC_FILTER_PARAMETERS Params
+ ) PURE;
+ // Some specific filters have arguments to further
+ // qualify their operation.
+ STDMETHOD(GetSpecificFilterArgument)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ArgumentSize
+ ) PURE;
+ STDMETHOD(SetSpecificFilterArgument)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Argument
+ ) PURE;
+ // If Codes is non-NULL Start is ignored.
+ STDMETHOD(GetExceptionFilterParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Codes,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_EXCEPTION_FILTER_PARAMETERS Params
+ ) PURE;
+ // The codes in the parameter data control the application
+ // of the parameter data. If a code is not already in
+ // the set of filters it is added. If the ExecutionOption
+ // for a code is REMOVE then the filter is removed.
+ // Specific exception filters cannot be removed.
+ STDMETHOD(SetExceptionFilterParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount(Count) PDEBUG_EXCEPTION_FILTER_PARAMETERS Params
+ ) PURE;
+ // Exception filters support an additional command for
+ // second-chance events.
+ STDMETHOD(GetExceptionFilterSecondCommand)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG CommandSize
+ ) PURE;
+ STDMETHOD(SetExceptionFilterSecondCommand)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Command
+ ) PURE;
+
+ // Yields processing to the engine until
+ // an event occurs. This method may
+ // only be called by the thread that started
+ // the debug session.
+ // When an event occurs the engine carries
+ // out all event processing such as calling
+ // callbacks.
+ // If the callbacks indicate that execution should
+ // break the wait will return, otherwise it
+ // goes back to waiting for a new event.
+ // If the timeout expires, S_FALSE is returned.
+ // The timeout is not currently supported for
+ // kernel debugging.
+ STDMETHOD(WaitForEvent)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG Timeout
+ ) PURE;
+
+ // 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.
+ STDMETHOD(GetLastEventInformation)(
+ THIS_
+ __out PULONG Type,
+ __out PULONG ProcessId,
+ __out PULONG ThreadId,
+ __out_bcount_opt(ExtraInformationSize) PVOID ExtraInformation,
+ __in ULONG ExtraInformationSize,
+ __out_opt PULONG ExtraInformationUsed,
+ __out_ecount_opt(DescriptionSize) PSTR Description,
+ __in ULONG DescriptionSize,
+ __out_opt PULONG DescriptionUsed
+ ) PURE;
+
+ // IDebugControl2.
+
+ STDMETHOD(GetCurrentTimeDate)(
+ THIS_
+ __out PULONG TimeDate
+ ) PURE;
+ // Retrieves the number of seconds since the
+ // machine started running.
+ STDMETHOD(GetCurrentSystemUpTime)(
+ THIS_
+ __out PULONG UpTime
+ ) PURE;
+
+ // If the current session is a dump session,
+ // retrieves any extended format information.
+ STDMETHOD(GetDumpFormatFlags)(
+ THIS_
+ __out PULONG FormatFlags
+ ) PURE;
+
+ // The debugger has been enhanced to allow
+ // arbitrary text replacements in addition
+ // to the simple $u0-$u9 text macros.
+ // Text replacement takes a given source
+ // text in commands and converts it to the
+ // given destination text. Replacements
+ // are named by their source text so that
+ // only one replacement for a source text
+ // string can exist.
+ STDMETHOD(GetNumberTextReplacements)(
+ THIS_
+ __out PULONG NumRepl
+ ) PURE;
+ // If SrcText is non-NULL the replacement
+ // is looked up by source text, otherwise
+ // Index is used to get the Nth replacement.
+ STDMETHOD(GetTextReplacement)(
+ THIS_
+ __in_opt PCSTR SrcText,
+ __in ULONG Index,
+ __out_ecount_opt(SrcBufferSize) PSTR SrcBuffer,
+ __in ULONG SrcBufferSize,
+ __out_opt PULONG SrcSize,
+ __out_ecount_opt(DstBufferSize) PSTR DstBuffer,
+ __in ULONG DstBufferSize,
+ __out_opt PULONG DstSize
+ ) PURE;
+ // Setting the destination text to
+ // NULL removes the alias.
+ STDMETHOD(SetTextReplacement)(
+ THIS_
+ __in PCSTR SrcText,
+ __in_opt PCSTR DstText
+ ) PURE;
+ STDMETHOD(RemoveTextReplacements)(
+ THIS
+ ) PURE;
+ // Outputs the complete list of current
+ // replacements.
+ STDMETHOD(OutputTextReplacements)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags
+ ) PURE;
+
+ // IDebugControl3.
+
+ // Control options for assembly and disassembly.
+ STDMETHOD(GetAssemblyOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddAssemblyOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveAssemblyOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetAssemblyOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ // Control the expression syntax.
+ STDMETHOD(GetExpressionSyntax)(
+ THIS_
+ __out PULONG Flags
+ ) PURE;
+ STDMETHOD(SetExpressionSyntax)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // Look up a syntax by its abbreviated
+ // name and set it.
+ STDMETHOD(SetExpressionSyntaxByName)(
+ THIS_
+ __in PCSTR AbbrevName
+ ) PURE;
+ STDMETHOD(GetNumberExpressionSyntaxes)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetExpressionSyntaxNames)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(FullNameBufferSize) PSTR FullNameBuffer,
+ __in ULONG FullNameBufferSize,
+ __out_opt PULONG FullNameSize,
+ __out_ecount_opt(AbbrevNameBufferSize) PSTR AbbrevNameBuffer,
+ __in ULONG AbbrevNameBufferSize,
+ __out_opt PULONG AbbrevNameSize
+ ) PURE;
+
+ //
+ // Some debug sessions have only a single
+ // possible event, such as a snapshot dump
+ // file; some have dynamic events, such as
+ // a live debug session; and others may have
+ // multiple events, such as a dump file that
+ // contains snapshots from different points
+ // in time. The following methods allow
+ // discovery and selection of the available
+ // events for a session.
+ // Sessions with one or more static events
+ // will be able to report all of the events
+ // when queried. Sessions with dynamic events
+ // will only report a single event representing
+ // the current event.
+ // Switching events constitutes execution and
+ // changing the current event will alter the
+ // execution status to a running state, after
+ // which WaitForEvent must be used to process
+ // the selected event.
+ //
+
+ // GetNumberEvents returns S_OK if this is the
+ // complete set of events possible, such as for
+ // a static session; or S_FALSE if other events
+ // may be possible, such as for a dynamic session.
+ STDMETHOD(GetNumberEvents)(
+ THIS_
+ __out PULONG Events
+ ) PURE;
+ // Sessions may have descriptive information for
+ // the various events available. The amount of
+ // information varies according to the specific
+ // session and data.
+ STDMETHOD(GetEventIndexDescription)(
+ THIS_
+ __in ULONG Index,
+ __in ULONG Which,
+ __in_opt PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DescSize
+ ) PURE;
+ STDMETHOD(GetCurrentEventIndex)(
+ THIS_
+ __out PULONG Index
+ ) PURE;
+ // SetNextEventIndex works like seek in that
+ // it can set an absolute or relative index.
+ // SetNextEventIndex works similarly to SetExecutionStatus
+ // by putting the session into a running state, after
+ // which the caller must call WaitForEvent. The
+ // current event index only changes when WaitForEvent
+ // is called.
+ STDMETHOD(SetNextEventIndex)(
+ THIS_
+ __in ULONG Relation,
+ __in ULONG Value,
+ __out PULONG NextIndex
+ ) PURE;
+};
+
+//
+// Log file flags.
+//
+
+#define DEBUG_LOG_DEFAULT 0x00000000
+#define DEBUG_LOG_APPEND 0x00000001
+#define DEBUG_LOG_UNICODE 0x00000002
+#define DEBUG_LOG_DML 0x00000004
+
+//
+// System version strings.
+//
+
+#define DEBUG_SYSVERSTR_SERVICE_PACK 0x00000000
+#define DEBUG_SYSVERSTR_BUILD 0x00000001
+
+//
+// GetManagedStatus flags and strings.
+//
+
+#define DEBUG_MANAGED_DISABLED 0x00000000
+#define DEBUG_MANAGED_ALLOWED 0x00000001
+#define DEBUG_MANAGED_DLL_LOADED 0x00000002
+
+#define DEBUG_MANSTR_NONE 0x00000000
+#define DEBUG_MANSTR_LOADED_SUPPORT_DLL 0x00000001
+#define DEBUG_MANSTR_LOAD_STATUS 0x00000002
+
+//
+// ResetManagedStatus flags.
+//
+
+// Reset state to default engine startup state with
+// no support loaded.
+#define DEBUG_MANRESET_DEFAULT 0x00000000
+// Force managed support DLL load attempt.
+#define DEBUG_MANRESET_LOAD_DLL 0x00000001
+
+#undef INTERFACE
+#define INTERFACE IDebugControl4
+DECLARE_INTERFACE_(IDebugControl4, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugControl.
+
+ // Checks for a user interrupt, such a Ctrl-C
+ // or stop button.
+ // This method is reentrant.
+ STDMETHOD(GetInterrupt)(
+ THIS
+ ) PURE;
+ // Registers a user interrupt.
+ // This method is reentrant.
+ STDMETHOD(SetInterrupt)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // Interrupting a user-mode process requires
+ // access to some system resources that the
+ // process may hold itself, preventing the
+ // interrupt from occurring. The engine
+ // will time-out pending interrupt requests
+ // and simulate an interrupt if necessary.
+ // These methods control the interrupt timeout.
+ STDMETHOD(GetInterruptTimeout)(
+ THIS_
+ __out PULONG Seconds
+ ) PURE;
+ STDMETHOD(SetInterruptTimeout)(
+ THIS_
+ __in ULONG Seconds
+ ) PURE;
+
+ STDMETHOD(GetLogFile)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG FileSize,
+ __out PBOOL Append
+ ) PURE;
+ // Opens a log file which collects all
+ // output. Output from every client except
+ // those that explicitly disable logging
+ // goes into the log.
+ // Opening a log file closes any log file
+ // already open.
+ STDMETHOD(OpenLogFile)(
+ THIS_
+ __in PCSTR File,
+ __in BOOL Append
+ ) PURE;
+ STDMETHOD(CloseLogFile)(
+ THIS
+ ) PURE;
+ // Controls what output is logged.
+ STDMETHOD(GetLogMask)(
+ THIS_
+ __out PULONG Mask
+ ) PURE;
+ STDMETHOD(SetLogMask)(
+ THIS_
+ __in ULONG Mask
+ ) PURE;
+
+ // Input requests input from all clients.
+ // The first input that is returned is used
+ // to satisfy the call. Other returned
+ // input is discarded.
+ STDMETHOD(Input)(
+ THIS_
+ __out_ecount(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG InputSize
+ ) PURE;
+ // This method is used by clients to return
+ // input when it is available. It will
+ // return S_OK if the input is used to
+ // satisfy an Input call and S_FALSE if
+ // the input is ignored.
+ // This method is reentrant.
+ STDMETHOD(ReturnInput)(
+ THIS_
+ __in PCSTR Buffer
+ ) PURE;
+
+ // Sends output through clients
+ // output callbacks if the mask is allowed
+ // by the current output control mask and
+ // according to the output distribution
+ // settings.
+ STDMETHODV(Output)(
+ THIS_
+ __in ULONG Mask,
+ __in PCSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(OutputVaList)(
+ THIS_
+ __in ULONG Mask,
+ __in PCSTR Format,
+ __in va_list Args
+ ) PURE;
+ // 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.
+ STDMETHODV(ControlledOutput)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Mask,
+ __in PCSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(ControlledOutputVaList)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Mask,
+ __in PCSTR Format,
+ __in va_list Args
+ ) PURE;
+
+ // Displays the standard command-line prompt
+ // followed by the given output. If Format
+ // is NULL no additional output is produced.
+ // Output is produced under the
+ // DEBUG_OUTPUT_PROMPT mask.
+ // This method only outputs the prompt; it
+ // does not get input.
+ STDMETHODV(OutputPrompt)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_opt PCSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(OutputPromptVaList)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_opt PCSTR Format,
+ __in va_list Args
+ ) PURE;
+ // Gets the text that would be displayed by OutputPrompt.
+ STDMETHOD(GetPromptText)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG TextSize
+ ) PURE;
+ // Outputs information about the current
+ // debuggee state such as a register
+ // summary, disassembly at the current PC,
+ // closest symbol and others.
+ // Uses the line prefix.
+ STDMETHOD(OutputCurrentState)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags
+ ) PURE;
+
+ // Outputs the debugger and extension version
+ // information. This method is reentrant.
+ // Uses the line prefix.
+ STDMETHOD(OutputVersionInformation)(
+ THIS_
+ __in ULONG OutputControl
+ ) PURE;
+
+ // In user-mode debugging sessions the
+ // engine will set an event when
+ // exceptions are continued. This can
+ // be used to synchronize other processes
+ // with the debuggers handling of events.
+ // For example, this is used to support
+ // the e argument to ntsd.
+ STDMETHOD(GetNotifyEventHandle)(
+ THIS_
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(SetNotifyEventHandle)(
+ THIS_
+ __in ULONG64 Handle
+ ) PURE;
+
+ STDMETHOD(Assemble)(
+ THIS_
+ __in ULONG64 Offset,
+ __in PCSTR Instr,
+ __out PULONG64 EndOffset
+ ) PURE;
+ STDMETHOD(Disassemble)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DisassemblySize,
+ __out PULONG64 EndOffset
+ ) PURE;
+ // Returns the value of the effective address
+ // computed for the last Disassemble, if there
+ // was one.
+ STDMETHOD(GetDisassembleEffectiveOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Uses the line prefix if necessary.
+ STDMETHOD(OutputDisassembly)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out PULONG64 EndOffset
+ ) PURE;
+ // Produces multiple lines of disassembly output.
+ // There will be PreviousLines of disassembly before
+ // the given offset if a valid disassembly exists.
+ // In all, there will be TotalLines of output produced.
+ // The first and last line offsets are returned
+ // specially and all lines offsets can be retrieved
+ // through LineOffsets. LineOffsets will contain
+ // offsets for each line where disassembly started.
+ // When disassembly of a single instruction takes
+ // multiple lines the initial offset will be followed
+ // by DEBUG_INVALID_OFFSET.
+ // Uses the line prefix.
+ STDMETHOD(OutputDisassemblyLines)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG PreviousLines,
+ __in ULONG TotalLines,
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out_opt PULONG OffsetLine,
+ __out_opt PULONG64 StartOffset,
+ __out_opt PULONG64 EndOffset,
+ __out_ecount_opt(TotalLines) PULONG64 LineOffsets
+ ) PURE;
+ // Returns the offset of the start of
+ // the instruction thats the given
+ // delta away from the instruction
+ // at the initial offset.
+ // This routine does not check for
+ // validity of the instruction or
+ // the memory containing it.
+ STDMETHOD(GetNearInstruction)(
+ THIS_
+ __in ULONG64 Offset,
+ __in LONG Delta,
+ __out PULONG64 NearOffset
+ ) PURE;
+
+ // Offsets can be passed in as zero to use the current
+ // thread state.
+ STDMETHOD(GetStackTrace)(
+ THIS_
+ __in ULONG64 FrameOffset,
+ __in ULONG64 StackOffset,
+ __in ULONG64 InstructionOffset,
+ __out_ecount(FramesSize) PDEBUG_STACK_FRAME Frames,
+ __in ULONG FramesSize,
+ __out_opt PULONG FramesFilled
+ ) PURE;
+ // Does a simple stack trace to determine
+ // what the current return address is.
+ STDMETHOD(GetReturnOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // If Frames is NULL OutputStackTrace will
+ // use GetStackTrace to get FramesSize frames
+ // and then output them. The current register
+ // values for frame, stack and instruction offsets
+ // are used.
+ // Uses the line prefix.
+ STDMETHOD(OutputStackTrace)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_ecount_opt(FramesSize) PDEBUG_STACK_FRAME Frames,
+ __in ULONG FramesSize,
+ __in ULONG Flags
+ ) PURE;
+
+ // Returns information about the debuggee such
+ // as user vs. kernel, dump vs. live, etc.
+ STDMETHOD(GetDebuggeeType)(
+ THIS_
+ __out PULONG Class,
+ __out PULONG Qualifier
+ ) PURE;
+ // Returns the type of physical processors in
+ // the machine.
+ // Returns one of the IMAGE_FILE_MACHINE values.
+ STDMETHOD(GetActualProcessorType)(
+ THIS_
+ __out PULONG Type
+ ) PURE;
+ // Returns the type of processor used in the
+ // current processor context.
+ STDMETHOD(GetExecutingProcessorType)(
+ THIS_
+ __out PULONG Type
+ ) PURE;
+ // Query all the possible processor types that
+ // may be encountered during this debug session.
+ STDMETHOD(GetNumberPossibleExecutingProcessorTypes)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetPossibleExecutingProcessorTypes)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PULONG Types
+ ) PURE;
+ // Get the number of actual processors in
+ // the machine.
+ STDMETHOD(GetNumberProcessors)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ // PlatformId is one of the VER_PLATFORM values.
+ // Major and minor are as given in the NT
+ // kernel debugger protocol.
+ // ServicePackString and ServicePackNumber indicate the
+ // system service pack level. ServicePackNumber is not
+ // available in some sessions where the service pack level
+ // is only expressed as a string. The service pack information
+ // will be empty if the system does not have a service pack
+ // applied.
+ // The build string is string information identifying the
+ // particular build of the system. The build string is
+ // empty if the system has no particular identifying
+ // information.
+ STDMETHOD(GetSystemVersion)(
+ THIS_
+ __out PULONG PlatformId,
+ __out PULONG Major,
+ __out PULONG Minor,
+ __out_ecount_opt(ServicePackStringSize) PSTR ServicePackString,
+ __in ULONG ServicePackStringSize,
+ __out_opt PULONG ServicePackStringUsed,
+ __out PULONG ServicePackNumber,
+ __out_ecount_opt(BuildStringSize) PSTR BuildString,
+ __in ULONG BuildStringSize,
+ __out_opt PULONG BuildStringUsed
+ ) PURE;
+ // Returns the page size for the currently executing
+ // processor context. The page size may vary between
+ // processor types.
+ STDMETHOD(GetPageSize)(
+ THIS_
+ __out PULONG Size
+ ) PURE;
+ // Returns S_OK if the current processor context uses
+ // 64-bit addresses, otherwise S_FALSE.
+ STDMETHOD(IsPointer64Bit)(
+ THIS
+ ) PURE;
+ // Reads the bugcheck data area and returns the
+ // current contents. This method only works
+ // in kernel debugging sessions.
+ STDMETHOD(ReadBugCheckData)(
+ THIS_
+ __out PULONG Code,
+ __out PULONG64 Arg1,
+ __out PULONG64 Arg2,
+ __out PULONG64 Arg3,
+ __out PULONG64 Arg4
+ ) PURE;
+
+ // Query all the processor types supported by
+ // the engine. This is a complete list and is
+ // not related to the machine running the engine
+ // or the debuggee.
+ STDMETHOD(GetNumberSupportedProcessorTypes)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetSupportedProcessorTypes)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PULONG Types
+ ) PURE;
+ // Returns a full, descriptive name and an
+ // abbreviated name for a processor type.
+ STDMETHOD(GetProcessorTypeNames)(
+ THIS_
+ __in ULONG Type,
+ __out_ecount_opt(FullNameBufferSize) PSTR FullNameBuffer,
+ __in ULONG FullNameBufferSize,
+ __out_opt PULONG FullNameSize,
+ __out_ecount_opt(AbbrevNameBufferSize) PSTR AbbrevNameBuffer,
+ __in ULONG AbbrevNameBufferSize,
+ __out_opt PULONG AbbrevNameSize
+ ) PURE;
+
+ // Gets and sets the type of processor to
+ // use when doing things like setting
+ // breakpoints, accessing registers,
+ // getting stack traces and so on.
+ STDMETHOD(GetEffectiveProcessorType)(
+ THIS_
+ __out PULONG Type
+ ) PURE;
+ STDMETHOD(SetEffectiveProcessorType)(
+ THIS_
+ __in ULONG Type
+ ) PURE;
+
+ // Returns information about whether and how
+ // the debuggee is running. Status will
+ // be GO if the debuggee is running and
+ // BREAK if it isnt.
+ // If no debuggee exists the status is
+ // NO_DEBUGGEE.
+ // This method is reentrant.
+ STDMETHOD(GetExecutionStatus)(
+ THIS_
+ __out PULONG Status
+ ) PURE;
+ // Changes the execution status of the
+ // engine from stopped to running.
+ // Status must be one of the go or step
+ // status values.
+ STDMETHOD(SetExecutionStatus)(
+ THIS_
+ __in ULONG Status
+ ) PURE;
+
+ // Controls what code interpretation level the debugger
+ // runs at. The debugger checks the code level when
+ // deciding whether to step by a source line or
+ // assembly instruction along with other related operations.
+ STDMETHOD(GetCodeLevel)(
+ THIS_
+ __out PULONG Level
+ ) PURE;
+ STDMETHOD(SetCodeLevel)(
+ THIS_
+ __in ULONG Level
+ ) PURE;
+
+ // Gets and sets engine control flags.
+ // These methods are reentrant.
+ STDMETHOD(GetEngineOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddEngineOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveEngineOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetEngineOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ // Gets and sets control values for
+ // handling system error events.
+ // If the system error level is less
+ // than or equal to the given levels
+ // the error may be displayed and
+ // the default break for the event
+ // may be set.
+ STDMETHOD(GetSystemErrorControl)(
+ THIS_
+ __out PULONG OutputLevel,
+ __out PULONG BreakLevel
+ ) PURE;
+ STDMETHOD(SetSystemErrorControl)(
+ THIS_
+ __in ULONG OutputLevel,
+ __in ULONG BreakLevel
+ ) PURE;
+
+ // The command processor supports simple
+ // string replacement macros in Evaluate and
+ // Execute. There are currently ten macro
+ // slots available. Slots 0-9 map to
+ // the command invocations $u0-$u9.
+ STDMETHOD(GetTextMacro)(
+ THIS_
+ __in ULONG Slot,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG MacroSize
+ ) PURE;
+ STDMETHOD(SetTextMacro)(
+ THIS_
+ __in ULONG Slot,
+ __in PCSTR Macro
+ ) PURE;
+
+ // Controls the default number radix used
+ // in expressions and commands.
+ STDMETHOD(GetRadix)(
+ THIS_
+ __out PULONG Radix
+ ) PURE;
+ STDMETHOD(SetRadix)(
+ THIS_
+ __in ULONG Radix
+ ) PURE;
+
+ // Evaluates the given expression string and
+ // returns the resulting value.
+ // If DesiredType is DEBUG_VALUE_INVALID then
+ // the natural type is used.
+ // RemainderIndex, if provided, is set to the index
+ // of the first character in the input string that was
+ // not used when evaluating the expression.
+ STDMETHOD(Evaluate)(
+ THIS_
+ __in PCSTR Expression,
+ __in ULONG DesiredType,
+ __out PDEBUG_VALUE Value,
+ __out_opt PULONG RemainderIndex
+ ) PURE;
+ // Attempts to convert the input value to a value
+ // of the requested type in the output value.
+ // Conversions can fail if no conversion exists.
+ // Successful conversions may be lossy.
+ STDMETHOD(CoerceValue)(
+ THIS_
+ __in PDEBUG_VALUE In,
+ __in ULONG OutType,
+ __out PDEBUG_VALUE Out
+ ) PURE;
+ STDMETHOD(CoerceValues)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount(Count) PDEBUG_VALUE In,
+ __in_ecount(Count) PULONG OutTypes,
+ __out_ecount(Count) PDEBUG_VALUE Out
+ ) PURE;
+
+ // 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.
+ STDMETHOD(Execute)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCSTR Command,
+ __in ULONG Flags
+ ) PURE;
+ // Executes the given command file by
+ // reading a line at a time and processing
+ // it with Execute.
+ STDMETHOD(ExecuteCommandFile)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCSTR CommandFile,
+ __in ULONG Flags
+ ) PURE;
+
+ // Breakpoint interfaces are described
+ // elsewhere in this section.
+ STDMETHOD(GetNumberBreakpoints)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ // It is possible for this retrieval function to
+ // fail even with an index within the number of
+ // existing breakpoints if the breakpoint is
+ // a private breakpoint.
+ STDMETHOD(GetBreakpointByIndex)(
+ THIS_
+ __in ULONG Index,
+ __out PDEBUG_BREAKPOINT* Bp
+ ) PURE;
+ STDMETHOD(GetBreakpointById)(
+ THIS_
+ __in ULONG Id,
+ __out PDEBUG_BREAKPOINT* Bp
+ ) PURE;
+ // If Ids is non-NULL the Count breakpoints
+ // referred to in the Ids array are returned,
+ // otherwise breakpoints from index Start to
+ // Start + Count 1 are returned.
+ STDMETHOD(GetBreakpointParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Ids,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_BREAKPOINT_PARAMETERS Params
+ ) PURE;
+ // Breakpoints are created empty and disabled.
+ // When their parameters have been set they
+ // should be enabled by setting the ENABLE flag.
+ // If DesiredId is DEBUG_ANY_ID then the
+ // engine picks an unused ID. If DesiredId
+ // is any other number the engine attempts
+ // to use the given ID for the breakpoint.
+ // If another breakpoint exists with that ID
+ // the call will fail.
+ STDMETHOD(AddBreakpoint)(
+ THIS_
+ __in ULONG Type,
+ __in ULONG DesiredId,
+ __out PDEBUG_BREAKPOINT* Bp
+ ) PURE;
+ // Breakpoint interface is invalid after this call.
+ STDMETHOD(RemoveBreakpoint)(
+ THIS_
+ __in PDEBUG_BREAKPOINT Bp
+ ) PURE;
+
+ // Control and use extension DLLs.
+ STDMETHOD(AddExtension)(
+ THIS_
+ __in PCSTR Path,
+ __in ULONG Flags,
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(RemoveExtension)(
+ THIS_
+ __in ULONG64 Handle
+ ) PURE;
+ STDMETHOD(GetExtensionByPath)(
+ THIS_
+ __in PCSTR Path,
+ __out PULONG64 Handle
+ ) PURE;
+ // If Handle is zero the extension
+ // chain is walked searching for the
+ // function.
+ STDMETHOD(CallExtension)(
+ THIS_
+ __in ULONG64 Handle,
+ __in PCSTR Function,
+ __in_opt PCSTR Arguments
+ ) PURE;
+ // GetExtensionFunction works like
+ // GetProcAddress on extension DLLs
+ // to allow raw function-call-level
+ // interaction with extension DLLs.
+ // Such functions do not need to
+ // follow the standard extension prototype
+ // if they are not going to be called
+ // through the text extension interface.
+ // This function cannot be called remotely.
+ STDMETHOD(GetExtensionFunction)(
+ THIS_
+ __in ULONG64 Handle,
+ __in PCSTR FuncName,
+ __out FARPROC* Function
+ ) PURE;
+ // These methods return alternate
+ // extension interfaces in order to allow
+ // interface-style extension DLLs to mix in
+ // older extension calls.
+ // Structure sizes must be initialized before
+ // the call.
+ // These methods cannot be called remotely.
+ STDMETHOD(GetWindbgExtensionApis32)(
+ THIS_
+ __inout PWINDBG_EXTENSION_APIS32 Api
+ ) PURE;
+ STDMETHOD(GetWindbgExtensionApis64)(
+ THIS_
+ __inout PWINDBG_EXTENSION_APIS64 Api
+ ) PURE;
+
+ // The engine provides a simple mechanism
+ // to filter common events. Arbitrarily complicated
+ // filtering can be done by registering event callbacks
+ // but simple event filtering only requires
+ // setting the options of one of the predefined
+ // event filters.
+ // Simple event filters are either for specific
+ // events and therefore have an enumerant or
+ // they are for an exception and are based on
+ // the exceptions code. Exception filters
+ // are further divided into exceptions specially
+ // handled by the engine, which is a fixed set,
+ // and arbitrary exceptions.
+ // All three groups of filters are indexed together
+ // with the specific filters first, then the specific
+ // exception filters and finally the arbitrary
+ // exception filters.
+ // The first specific exception is the default
+ // exception. If an exception event occurs for
+ // an exception without settings the default
+ // exception settings are used.
+ STDMETHOD(GetNumberEventFilters)(
+ THIS_
+ __out PULONG SpecificEvents,
+ __out PULONG SpecificExceptions,
+ __out PULONG ArbitraryExceptions
+ ) PURE;
+ // Some filters have descriptive text associated with them.
+ STDMETHOD(GetEventFilterText)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG TextSize
+ ) PURE;
+ // All filters support executing a command when the
+ // event occurs.
+ STDMETHOD(GetEventFilterCommand)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG CommandSize
+ ) PURE;
+ STDMETHOD(SetEventFilterCommand)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Command
+ ) PURE;
+ STDMETHOD(GetSpecificFilterParameters)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PDEBUG_SPECIFIC_FILTER_PARAMETERS Params
+ ) PURE;
+ STDMETHOD(SetSpecificFilterParameters)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __in_ecount(Count) PDEBUG_SPECIFIC_FILTER_PARAMETERS Params
+ ) PURE;
+ // Some specific filters have arguments to further
+ // qualify their operation.
+ STDMETHOD(GetSpecificFilterArgument)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ArgumentSize
+ ) PURE;
+ STDMETHOD(SetSpecificFilterArgument)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Argument
+ ) PURE;
+ // If Codes is non-NULL Start is ignored.
+ STDMETHOD(GetExceptionFilterParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Codes,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_EXCEPTION_FILTER_PARAMETERS Params
+ ) PURE;
+ // The codes in the parameter data control the application
+ // of the parameter data. If a code is not already in
+ // the set of filters it is added. If the ExecutionOption
+ // for a code is REMOVE then the filter is removed.
+ // Specific exception filters cannot be removed.
+ STDMETHOD(SetExceptionFilterParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount(Count) PDEBUG_EXCEPTION_FILTER_PARAMETERS Params
+ ) PURE;
+ // Exception filters support an additional command for
+ // second-chance events.
+ STDMETHOD(GetExceptionFilterSecondCommand)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG CommandSize
+ ) PURE;
+ STDMETHOD(SetExceptionFilterSecondCommand)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Command
+ ) PURE;
+
+ // Yields processing to the engine until
+ // an event occurs. This method may
+ // only be called by the thread that started
+ // the debug session.
+ // When an event occurs the engine carries
+ // out all event processing such as calling
+ // callbacks.
+ // If the callbacks indicate that execution should
+ // break the wait will return, otherwise it
+ // goes back to waiting for a new event.
+ // If the timeout expires, S_FALSE is returned.
+ // The timeout is not currently supported for
+ // kernel debugging.
+ STDMETHOD(WaitForEvent)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG Timeout
+ ) PURE;
+
+ // 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.
+ STDMETHOD(GetLastEventInformation)(
+ THIS_
+ __out PULONG Type,
+ __out PULONG ProcessId,
+ __out PULONG ThreadId,
+ __out_bcount_opt(ExtraInformationSize) PVOID ExtraInformation,
+ __in ULONG ExtraInformationSize,
+ __out_opt PULONG ExtraInformationUsed,
+ __out_ecount_opt(DescriptionSize) PSTR Description,
+ __in ULONG DescriptionSize,
+ __out_opt PULONG DescriptionUsed
+ ) PURE;
+
+ // IDebugControl2.
+
+ STDMETHOD(GetCurrentTimeDate)(
+ THIS_
+ __out PULONG TimeDate
+ ) PURE;
+ // Retrieves the number of seconds since the
+ // machine started running.
+ STDMETHOD(GetCurrentSystemUpTime)(
+ THIS_
+ __out PULONG UpTime
+ ) PURE;
+
+ // If the current session is a dump session,
+ // retrieves any extended format information.
+ STDMETHOD(GetDumpFormatFlags)(
+ THIS_
+ __out PULONG FormatFlags
+ ) PURE;
+
+ // The debugger has been enhanced to allow
+ // arbitrary text replacements in addition
+ // to the simple $u0-$u9 text macros.
+ // Text replacement takes a given source
+ // text in commands and converts it to the
+ // given destination text. Replacements
+ // are named by their source text so that
+ // only one replacement for a source text
+ // string can exist.
+ STDMETHOD(GetNumberTextReplacements)(
+ THIS_
+ __out PULONG NumRepl
+ ) PURE;
+ // If SrcText is non-NULL the replacement
+ // is looked up by source text, otherwise
+ // Index is used to get the Nth replacement.
+ STDMETHOD(GetTextReplacement)(
+ THIS_
+ __in_opt PCSTR SrcText,
+ __in ULONG Index,
+ __out_ecount_opt(SrcBufferSize) PSTR SrcBuffer,
+ __in ULONG SrcBufferSize,
+ __out_opt PULONG SrcSize,
+ __out_ecount_opt(DstBufferSize) PSTR DstBuffer,
+ __in ULONG DstBufferSize,
+ __out_opt PULONG DstSize
+ ) PURE;
+ // Setting the destination text to
+ // NULL removes the alias.
+ STDMETHOD(SetTextReplacement)(
+ THIS_
+ __in PCSTR SrcText,
+ __in_opt PCSTR DstText
+ ) PURE;
+ STDMETHOD(RemoveTextReplacements)(
+ THIS
+ ) PURE;
+ // Outputs the complete list of current
+ // replacements.
+ STDMETHOD(OutputTextReplacements)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags
+ ) PURE;
+
+ // IDebugControl3.
+
+ // Control options for assembly and disassembly.
+ STDMETHOD(GetAssemblyOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddAssemblyOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveAssemblyOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetAssemblyOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ // Control the expression syntax.
+ STDMETHOD(GetExpressionSyntax)(
+ THIS_
+ __out PULONG Flags
+ ) PURE;
+ STDMETHOD(SetExpressionSyntax)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+ // Look up a syntax by its abbreviated
+ // name and set it.
+ STDMETHOD(SetExpressionSyntaxByName)(
+ THIS_
+ __in PCSTR AbbrevName
+ ) PURE;
+ STDMETHOD(GetNumberExpressionSyntaxes)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetExpressionSyntaxNames)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(FullNameBufferSize) PSTR FullNameBuffer,
+ __in ULONG FullNameBufferSize,
+ __out_opt PULONG FullNameSize,
+ __out_ecount_opt(AbbrevNameBufferSize) PSTR AbbrevNameBuffer,
+ __in ULONG AbbrevNameBufferSize,
+ __out_opt PULONG AbbrevNameSize
+ ) PURE;
+
+ //
+ // Some debug sessions have only a single
+ // possible event, such as a snapshot dump
+ // file; some have dynamic events, such as
+ // a live debug session; and others may have
+ // multiple events, such as a dump file that
+ // contains snapshots from different points
+ // in time. The following methods allow
+ // discovery and selection of the available
+ // events for a session.
+ // Sessions with one or more static events
+ // will be able to report all of the events
+ // when queried. Sessions with dynamic events
+ // will only report a single event representing
+ // the current event.
+ // Switching events constitutes execution and
+ // changing the current event will alter the
+ // execution status to a running state, after
+ // which WaitForEvent must be used to process
+ // the selected event.
+ //
+
+ // GetNumberEvents returns S_OK if this is the
+ // complete set of events possible, such as for
+ // a static session; or S_FALSE if other events
+ // may be possible, such as for a dynamic session.
+ STDMETHOD(GetNumberEvents)(
+ THIS_
+ __out PULONG Events
+ ) PURE;
+ // Sessions may have descriptive information for
+ // the various events available. The amount of
+ // information varies according to the specific
+ // session and data.
+ STDMETHOD(GetEventIndexDescription)(
+ THIS_
+ __in ULONG Index,
+ __in ULONG Which,
+ __in_opt PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DescSize
+ ) PURE;
+ STDMETHOD(GetCurrentEventIndex)(
+ THIS_
+ __out PULONG Index
+ ) PURE;
+ // SetNextEventIndex works like seek in that
+ // it can set an absolute or relative index.
+ // SetNextEventIndex works similarly to SetExecutionStatus
+ // by putting the session into a running state, after
+ // which the caller must call WaitForEvent. The
+ // current event index only changes when WaitForEvent
+ // is called.
+ STDMETHOD(SetNextEventIndex)(
+ THIS_
+ __in ULONG Relation,
+ __in ULONG Value,
+ __out PULONG NextIndex
+ ) PURE;
+
+ // IDebugControl4.
+
+ STDMETHOD(GetLogFileWide)(
+ THIS_
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG FileSize,
+ __out PBOOL Append
+ ) PURE;
+ STDMETHOD(OpenLogFileWide)(
+ THIS_
+ __in PCWSTR File,
+ __in BOOL Append
+ ) PURE;
+
+ STDMETHOD(InputWide)(
+ THIS_
+ __out_ecount(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG InputSize
+ ) PURE;
+ STDMETHOD(ReturnInputWide)(
+ THIS_
+ __in PCWSTR Buffer
+ ) PURE;
+
+ STDMETHODV(OutputWide)(
+ THIS_
+ __in ULONG Mask,
+ __in PCWSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(OutputVaListWide)(
+ THIS_
+ __in ULONG Mask,
+ __in PCWSTR Format,
+ __in va_list Args
+ ) PURE;
+ STDMETHODV(ControlledOutputWide)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Mask,
+ __in PCWSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(ControlledOutputVaListWide)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Mask,
+ __in PCWSTR Format,
+ __in va_list Args
+ ) PURE;
+
+ STDMETHODV(OutputPromptWide)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_opt PCWSTR Format,
+ ...
+ ) PURE;
+ STDMETHOD(OutputPromptVaListWide)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_opt PCWSTR Format,
+ __in va_list Args
+ ) PURE;
+ STDMETHOD(GetPromptTextWide)(
+ THIS_
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG TextSize
+ ) PURE;
+
+ STDMETHOD(AssembleWide)(
+ THIS_
+ __in ULONG64 Offset,
+ __in PCWSTR Instr,
+ __out PULONG64 EndOffset
+ ) PURE;
+ STDMETHOD(DisassembleWide)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DisassemblySize,
+ __out PULONG64 EndOffset
+ ) PURE;
+
+ STDMETHOD(GetProcessorTypeNamesWide)(
+ THIS_
+ __in ULONG Type,
+ __out_ecount_opt(FullNameBufferSize) PWSTR FullNameBuffer,
+ __in ULONG FullNameBufferSize,
+ __out_opt PULONG FullNameSize,
+ __out_ecount_opt(AbbrevNameBufferSize) PWSTR AbbrevNameBuffer,
+ __in ULONG AbbrevNameBufferSize,
+ __out_opt PULONG AbbrevNameSize
+ ) PURE;
+
+ STDMETHOD(GetTextMacroWide)(
+ THIS_
+ __in ULONG Slot,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG MacroSize
+ ) PURE;
+ STDMETHOD(SetTextMacroWide)(
+ THIS_
+ __in ULONG Slot,
+ __in PCWSTR Macro
+ ) PURE;
+
+ STDMETHOD(EvaluateWide)(
+ THIS_
+ __in PCWSTR Expression,
+ __in ULONG DesiredType,
+ __out PDEBUG_VALUE Value,
+ __out_opt PULONG RemainderIndex
+ ) PURE;
+
+ STDMETHOD(ExecuteWide)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCWSTR Command,
+ __in ULONG Flags
+ ) PURE;
+ STDMETHOD(ExecuteCommandFileWide)(
+ THIS_
+ __in ULONG OutputControl,
+ __in PCWSTR CommandFile,
+ __in ULONG Flags
+ ) PURE;
+
+ STDMETHOD(GetBreakpointByIndex2)(
+ THIS_
+ __in ULONG Index,
+ __out PDEBUG_BREAKPOINT2* Bp
+ ) PURE;
+ STDMETHOD(GetBreakpointById2)(
+ THIS_
+ __in ULONG Id,
+ __out PDEBUG_BREAKPOINT2* Bp
+ ) PURE;
+ STDMETHOD(AddBreakpoint2)(
+ THIS_
+ __in ULONG Type,
+ __in ULONG DesiredId,
+ __out PDEBUG_BREAKPOINT2* Bp
+ ) PURE;
+ STDMETHOD(RemoveBreakpoint2)(
+ THIS_
+ __in PDEBUG_BREAKPOINT2 Bp
+ ) PURE;
+
+ STDMETHOD(AddExtensionWide)(
+ THIS_
+ __in PCWSTR Path,
+ __in ULONG Flags,
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(GetExtensionByPathWide)(
+ THIS_
+ __in PCWSTR Path,
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(CallExtensionWide)(
+ THIS_
+ __in ULONG64 Handle,
+ __in PCWSTR Function,
+ __in_opt PCWSTR Arguments
+ ) PURE;
+ STDMETHOD(GetExtensionFunctionWide)(
+ THIS_
+ __in ULONG64 Handle,
+ __in PCWSTR FuncName,
+ __out FARPROC* Function
+ ) PURE;
+
+ STDMETHOD(GetEventFilterTextWide)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG TextSize
+ ) PURE;
+ STDMETHOD(GetEventFilterCommandWide)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG CommandSize
+ ) PURE;
+ STDMETHOD(SetEventFilterCommandWide)(
+ THIS_
+ __in ULONG Index,
+ __in PCWSTR Command
+ ) PURE;
+ STDMETHOD(GetSpecificFilterArgumentWide)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ArgumentSize
+ ) PURE;
+ STDMETHOD(SetSpecificFilterArgumentWide)(
+ THIS_
+ __in ULONG Index,
+ __in PCWSTR Argument
+ ) PURE;
+ STDMETHOD(GetExceptionFilterSecondCommandWide)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG CommandSize
+ ) PURE;
+ STDMETHOD(SetExceptionFilterSecondCommandWide)(
+ THIS_
+ __in ULONG Index,
+ __in PCWSTR Command
+ ) PURE;
+
+ STDMETHOD(GetLastEventInformationWide)(
+ THIS_
+ __out PULONG Type,
+ __out PULONG ProcessId,
+ __out PULONG ThreadId,
+ __out_bcount_opt(ExtraInformationSize) PVOID ExtraInformation,
+ __in ULONG ExtraInformationSize,
+ __out_opt PULONG ExtraInformationUsed,
+ __out_ecount_opt(DescriptionSize) PWSTR Description,
+ __in ULONG DescriptionSize,
+ __out_opt PULONG DescriptionUsed
+ ) PURE;
+
+ STDMETHOD(GetTextReplacementWide)(
+ THIS_
+ __in_opt PCWSTR SrcText,
+ __in ULONG Index,
+ __out_ecount_opt(SrcBufferSize) PWSTR SrcBuffer,
+ __in ULONG SrcBufferSize,
+ __out_opt PULONG SrcSize,
+ __out_ecount_opt(DstBufferSize) PWSTR DstBuffer,
+ __in ULONG DstBufferSize,
+ __out_opt PULONG DstSize
+ ) PURE;
+ STDMETHOD(SetTextReplacementWide)(
+ THIS_
+ __in PCWSTR SrcText,
+ __in_opt PCWSTR DstText
+ ) PURE;
+
+ STDMETHOD(SetExpressionSyntaxByNameWide)(
+ THIS_
+ __in PCWSTR AbbrevName
+ ) PURE;
+ STDMETHOD(GetExpressionSyntaxNamesWide)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(FullNameBufferSize) PWSTR FullNameBuffer,
+ __in ULONG FullNameBufferSize,
+ __out_opt PULONG FullNameSize,
+ __out_ecount_opt(AbbrevNameBufferSize) PWSTR AbbrevNameBuffer,
+ __in ULONG AbbrevNameBufferSize,
+ __out_opt PULONG AbbrevNameSize
+ ) PURE;
+
+ STDMETHOD(GetEventIndexDescriptionWide)(
+ THIS_
+ __in ULONG Index,
+ __in ULONG Which,
+ __in_opt PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DescSize
+ ) PURE;
+
+ STDMETHOD(GetLogFile2)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG FileSize,
+ __out PULONG Flags
+ ) PURE;
+ STDMETHOD(OpenLogFile2)(
+ THIS_
+ __in PCSTR File,
+ __in ULONG Flags
+ ) PURE;
+ STDMETHOD(GetLogFile2Wide)(
+ THIS_
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG FileSize,
+ __out PULONG Flags
+ ) PURE;
+ STDMETHOD(OpenLogFile2Wide)(
+ THIS_
+ __in PCWSTR File,
+ __in ULONG Flags
+ ) PURE;
+
+ // GetSystemVersion always returns the kd
+ // major/minor version numbers, which are
+ // different than the Win32 version numbers.
+ // GetSystemVersionValues can be used
+ // to determine the Win32 version values.
+ STDMETHOD(GetSystemVersionValues)(
+ THIS_
+ __out PULONG PlatformId,
+ __out PULONG Win32Major,
+ __out PULONG Win32Minor,
+ __out_opt PULONG KdMajor,
+ __out_opt PULONG KdMinor
+ ) PURE;
+ // Strings are selected with DEBUG_SYSVERSTR_*.
+ STDMETHOD(GetSystemVersionString)(
+ THIS_
+ __in ULONG Which,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG StringSize
+ ) PURE;
+ STDMETHOD(GetSystemVersionStringWide)(
+ THIS_
+ __in ULONG Which,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG StringSize
+ ) PURE;
+
+ // 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.
+ STDMETHOD(GetContextStackTrace)(
+ THIS_
+ __in_bcount_opt(StartContextSize) PVOID StartContext,
+ __in ULONG StartContextSize,
+ __out_ecount_opt(FramesSize) PDEBUG_STACK_FRAME Frames,
+ __in ULONG FramesSize,
+ __out_bcount_opt(FrameContextsSize) PVOID FrameContexts,
+ __in ULONG FrameContextsSize,
+ __in ULONG FrameContextsEntrySize,
+ __out_opt PULONG FramesFilled
+ ) PURE;
+ STDMETHOD(OutputContextStackTrace)(
+ THIS_
+ __in ULONG OutputControl,
+ __in_ecount(FramesSize) PDEBUG_STACK_FRAME Frames,
+ __in ULONG FramesSize,
+ __in_bcount(FrameContextsSize) PVOID FrameContexts,
+ __in ULONG FrameContextsSize,
+ __in ULONG FrameContextsEntrySize,
+ __in ULONG Flags
+ ) PURE;
+
+ // Some targets, such as user-mode minidump files,
+ // have separate "event of interest" information
+ // stored within them. This method allows
+ // access to that information.
+ STDMETHOD(GetStoredEventInformation)(
+ THIS_
+ __out PULONG Type,
+ __out PULONG ProcessId,
+ __out PULONG ThreadId,
+ __out_bcount_opt(ContextSize) PVOID Context,
+ __in ULONG ContextSize,
+ __out_opt PULONG ContextUsed,
+ __out_bcount_opt(ExtraInformationSize) PVOID ExtraInformation,
+ __in ULONG ExtraInformationSize,
+ __out_opt PULONG ExtraInformationUsed
+ ) PURE;
+
+ // Managed debugging support relies on debugging
+ // functionality provided by the Common Language Runtime.
+ // This method provides feedback on the engine's
+ // use of the runtime debugging APIs.
+ STDMETHOD(GetManagedStatus)(
+ THIS_
+ __out_opt PULONG Flags,
+ __in ULONG WhichString,
+ __out_ecount_opt(StringSize) PSTR String,
+ __in ULONG StringSize,
+ __out_opt PULONG StringNeeded
+ ) PURE;
+ STDMETHOD(GetManagedStatusWide)(
+ THIS_
+ __out_opt PULONG Flags,
+ __in ULONG WhichString,
+ __out_ecount_opt(StringSize) PWSTR String,
+ __in ULONG StringSize,
+ __out_opt PULONG StringNeeded
+ ) PURE;
+ // Clears and reinitializes the engine's
+ // managed code debugging support.
+ STDMETHOD(ResetManagedStatus)(
+ THIS_
+ __in ULONG Flags
+ ) PURE;
+};
+
+//----------------------------------------------------------------------------
+//
+// IDebugDataSpaces.
+//
+//----------------------------------------------------------------------------
+
+// Data space indices for callbacks and other methods.
+#define DEBUG_DATA_SPACE_VIRTUAL 0
+#define DEBUG_DATA_SPACE_PHYSICAL 1
+#define DEBUG_DATA_SPACE_CONTROL 2
+#define DEBUG_DATA_SPACE_IO 3
+#define DEBUG_DATA_SPACE_MSR 4
+#define DEBUG_DATA_SPACE_BUS_DATA 5
+#define DEBUG_DATA_SPACE_DEBUGGER_DATA 6
+// Count of data spaces.
+#define DEBUG_DATA_SPACE_COUNT 7
+
+// Indices for ReadDebuggerData interface
+#define DEBUG_DATA_KernBase 24
+#define DEBUG_DATA_BreakpointWithStatusAddr 32
+#define DEBUG_DATA_SavedContextAddr 40
+#define DEBUG_DATA_KiCallUserModeAddr 56
+#define DEBUG_DATA_KeUserCallbackDispatcherAddr 64
+#define DEBUG_DATA_PsLoadedModuleListAddr 72
+#define DEBUG_DATA_PsActiveProcessHeadAddr 80
+#define DEBUG_DATA_PspCidTableAddr 88
+#define DEBUG_DATA_ExpSystemResourcesListAddr 96
+#define DEBUG_DATA_ExpPagedPoolDescriptorAddr 104
+#define DEBUG_DATA_ExpNumberOfPagedPoolsAddr 112
+#define DEBUG_DATA_KeTimeIncrementAddr 120
+#define DEBUG_DATA_KeBugCheckCallbackListHeadAddr 128
+#define DEBUG_DATA_KiBugcheckDataAddr 136
+#define DEBUG_DATA_IopErrorLogListHeadAddr 144
+#define DEBUG_DATA_ObpRootDirectoryObjectAddr 152
+#define DEBUG_DATA_ObpTypeObjectTypeAddr 160
+#define DEBUG_DATA_MmSystemCacheStartAddr 168
+#define DEBUG_DATA_MmSystemCacheEndAddr 176
+#define DEBUG_DATA_MmSystemCacheWsAddr 184
+#define DEBUG_DATA_MmPfnDatabaseAddr 192
+#define DEBUG_DATA_MmSystemPtesStartAddr 200
+#define DEBUG_DATA_MmSystemPtesEndAddr 208
+#define DEBUG_DATA_MmSubsectionBaseAddr 216
+#define DEBUG_DATA_MmNumberOfPagingFilesAddr 224
+#define DEBUG_DATA_MmLowestPhysicalPageAddr 232
+#define DEBUG_DATA_MmHighestPhysicalPageAddr 240
+#define DEBUG_DATA_MmNumberOfPhysicalPagesAddr 248
+#define DEBUG_DATA_MmMaximumNonPagedPoolInBytesAddr 256
+#define DEBUG_DATA_MmNonPagedSystemStartAddr 264
+#define DEBUG_DATA_MmNonPagedPoolStartAddr 272
+#define DEBUG_DATA_MmNonPagedPoolEndAddr 280
+#define DEBUG_DATA_MmPagedPoolStartAddr 288
+#define DEBUG_DATA_MmPagedPoolEndAddr 296
+#define DEBUG_DATA_MmPagedPoolInformationAddr 304
+#define DEBUG_DATA_MmPageSize 312
+#define DEBUG_DATA_MmSizeOfPagedPoolInBytesAddr 320
+#define DEBUG_DATA_MmTotalCommitLimitAddr 328
+#define DEBUG_DATA_MmTotalCommittedPagesAddr 336
+#define DEBUG_DATA_MmSharedCommitAddr 344
+#define DEBUG_DATA_MmDriverCommitAddr 352
+#define DEBUG_DATA_MmProcessCommitAddr 360
+#define DEBUG_DATA_MmPagedPoolCommitAddr 368
+#define DEBUG_DATA_MmExtendedCommitAddr 376
+#define DEBUG_DATA_MmZeroedPageListHeadAddr 384
+#define DEBUG_DATA_MmFreePageListHeadAddr 392
+#define DEBUG_DATA_MmStandbyPageListHeadAddr 400
+#define DEBUG_DATA_MmModifiedPageListHeadAddr 408
+#define DEBUG_DATA_MmModifiedNoWritePageListHeadAddr 416
+#define DEBUG_DATA_MmAvailablePagesAddr 424
+#define DEBUG_DATA_MmResidentAvailablePagesAddr 432
+#define DEBUG_DATA_PoolTrackTableAddr 440
+#define DEBUG_DATA_NonPagedPoolDescriptorAddr 448
+#define DEBUG_DATA_MmHighestUserAddressAddr 456
+#define DEBUG_DATA_MmSystemRangeStartAddr 464
+#define DEBUG_DATA_MmUserProbeAddressAddr 472
+#define DEBUG_DATA_KdPrintCircularBufferAddr 480
+#define DEBUG_DATA_KdPrintCircularBufferEndAddr 488
+#define DEBUG_DATA_KdPrintWritePointerAddr 496
+#define DEBUG_DATA_KdPrintRolloverCountAddr 504
+#define DEBUG_DATA_MmLoadedUserImageListAddr 512
+#define DEBUG_DATA_NtBuildLabAddr 520
+#define DEBUG_DATA_KiNormalSystemCall 528
+#define DEBUG_DATA_KiProcessorBlockAddr 536
+#define DEBUG_DATA_MmUnloadedDriversAddr 544
+#define DEBUG_DATA_MmLastUnloadedDriverAddr 552
+#define DEBUG_DATA_MmTriageActionTakenAddr 560
+#define DEBUG_DATA_MmSpecialPoolTagAddr 568
+#define DEBUG_DATA_KernelVerifierAddr 576
+#define DEBUG_DATA_MmVerifierDataAddr 584
+#define DEBUG_DATA_MmAllocatedNonPagedPoolAddr 592
+#define DEBUG_DATA_MmPeakCommitmentAddr 600
+#define DEBUG_DATA_MmTotalCommitLimitMaximumAddr 608
+#define DEBUG_DATA_CmNtCSDVersionAddr 616
+#define DEBUG_DATA_MmPhysicalMemoryBlockAddr 624
+#define DEBUG_DATA_MmSessionBase 632
+#define DEBUG_DATA_MmSessionSize 640
+#define DEBUG_DATA_MmSystemParentTablePage 648
+#define DEBUG_DATA_MmVirtualTranslationBase 656
+#define DEBUG_DATA_OffsetKThreadNextProcessor 664
+#define DEBUG_DATA_OffsetKThreadTeb 666
+#define DEBUG_DATA_OffsetKThreadKernelStack 668
+#define DEBUG_DATA_OffsetKThreadInitialStack 670
+#define DEBUG_DATA_OffsetKThreadApcProcess 672
+#define DEBUG_DATA_OffsetKThreadState 674
+#define DEBUG_DATA_OffsetKThreadBStore 676
+#define DEBUG_DATA_OffsetKThreadBStoreLimit 678
+#define DEBUG_DATA_SizeEProcess 680
+#define DEBUG_DATA_OffsetEprocessPeb 682
+#define DEBUG_DATA_OffsetEprocessParentCID 684
+#define DEBUG_DATA_OffsetEprocessDirectoryTableBase 686
+#define DEBUG_DATA_SizePrcb 688
+#define DEBUG_DATA_OffsetPrcbDpcRoutine 690
+#define DEBUG_DATA_OffsetPrcbCurrentThread 692
+#define DEBUG_DATA_OffsetPrcbMhz 694
+#define DEBUG_DATA_OffsetPrcbCpuType 696
+#define DEBUG_DATA_OffsetPrcbVendorString 698
+#define DEBUG_DATA_OffsetPrcbProcessorState 700
+#define DEBUG_DATA_OffsetPrcbNumber 702
+#define DEBUG_DATA_SizeEThread 704
+#define DEBUG_DATA_KdPrintCircularBufferPtrAddr 712
+#define DEBUG_DATA_KdPrintBufferSizeAddr 720
+#define DEBUG_DATA_MmBadPagesDetected 800
+
+#define DEBUG_DATA_PaeEnabled 100000
+#define DEBUG_DATA_SharedUserData 100008
+#define DEBUG_DATA_ProductType 100016
+#define DEBUG_DATA_SuiteMask 100024
+#define DEBUG_DATA_DumpWriterStatus 100032
+#define DEBUG_DATA_DumpFormatVersion 100040
+#define DEBUG_DATA_DumpWriterVersion 100048
+#define DEBUG_DATA_DumpPowerState 100056
+#define DEBUG_DATA_DumpMmStorage 100064
+
+//
+// Processor information structures.
+//
+
+typedef struct _DEBUG_PROCESSOR_IDENTIFICATION_ALPHA
+{
+ ULONG Type;
+ ULONG Revision;
+} DEBUG_PROCESSOR_IDENTIFICATION_ALPHA, *PDEBUG_PROCESSOR_IDENTIFICATION_ALPHA;
+
+typedef struct _DEBUG_PROCESSOR_IDENTIFICATION_AMD64
+{
+ ULONG Family;
+ ULONG Model;
+ ULONG Stepping;
+ CHAR VendorString[16];
+} DEBUG_PROCESSOR_IDENTIFICATION_AMD64, *PDEBUG_PROCESSOR_IDENTIFICATION_AMD64;
+
+typedef struct _DEBUG_PROCESSOR_IDENTIFICATION_IA64
+{
+ ULONG Model;
+ ULONG Revision;
+ ULONG Family;
+ ULONG ArchRev;
+ CHAR VendorString[16];
+} DEBUG_PROCESSOR_IDENTIFICATION_IA64, *PDEBUG_PROCESSOR_IDENTIFICATION_IA64;
+
+typedef struct _DEBUG_PROCESSOR_IDENTIFICATION_X86
+{
+ ULONG Family;
+ ULONG Model;
+ ULONG Stepping;
+ CHAR VendorString[16];
+} DEBUG_PROCESSOR_IDENTIFICATION_X86, *PDEBUG_PROCESSOR_IDENTIFICATION_X86;
+
+typedef struct _DEBUG_PROCESSOR_IDENTIFICATION_ARM
+{
+ ULONG Type;
+ ULONG Revision;
+} DEBUG_PROCESSOR_IDENTIFICATION_ARM, *PDEBUG_PROCESSOR_IDENTIFICATION_ARM;
+
+typedef union _DEBUG_PROCESSOR_IDENTIFICATION_ALL
+{
+ DEBUG_PROCESSOR_IDENTIFICATION_ALPHA Alpha;
+ DEBUG_PROCESSOR_IDENTIFICATION_AMD64 Amd64;
+ DEBUG_PROCESSOR_IDENTIFICATION_IA64 Ia64;
+ DEBUG_PROCESSOR_IDENTIFICATION_X86 X86;
+ DEBUG_PROCESSOR_IDENTIFICATION_ARM Arm;
+} DEBUG_PROCESSOR_IDENTIFICATION_ALL, *PDEBUG_PROCESSOR_IDENTIFICATION_ALL;
+
+// Indices for ReadProcessorSystemData.
+#define DEBUG_DATA_KPCR_OFFSET 0
+#define DEBUG_DATA_KPRCB_OFFSET 1
+#define DEBUG_DATA_KTHREAD_OFFSET 2
+#define DEBUG_DATA_BASE_TRANSLATION_VIRTUAL_OFFSET 3
+#define DEBUG_DATA_PROCESSOR_IDENTIFICATION 4
+#define DEBUG_DATA_PROCESSOR_SPEED 5
+
+#undef INTERFACE
+#define INTERFACE IDebugDataSpaces
+DECLARE_INTERFACE_(IDebugDataSpaces, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugDataSpaces.
+ STDMETHOD(ReadVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ // SearchVirtual searches the given virtual
+ // address range for the given pattern. PatternSize
+ // gives the byte length of the pattern and PatternGranularity
+ // controls the granularity of comparisons during
+ // the search.
+ // For example, a DWORD-granular search would
+ // use a pattern granularity of four to search by DWORD
+ // increments.
+ STDMETHOD(SearchVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Length,
+ __in_bcount(PatternSize) PVOID Pattern,
+ __in ULONG PatternSize,
+ __in ULONG PatternGranularity,
+ __out PULONG64 MatchOffset
+ ) PURE;
+ // These methods are identical to Read/WriteVirtual
+ // except that they avoid the kernel virtual memory
+ // cache entirely and are therefore useful for reading
+ // virtual memory which is inherently volatile, such
+ // as memory-mapped device areas, without contaminating
+ // or invalidating the cache.
+ // In user-mode they are the same as Read/WriteVirtual.
+ STDMETHOD(ReadVirtualUncached)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteVirtualUncached)(
+ THIS_
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ // The following two methods are convenience
+ // methods for accessing pointer values.
+ // They automatically convert between native pointers
+ // and canonical 64-bit values as necessary.
+ // These routines stop at the first failure.
+ STDMETHOD(ReadPointersVirtual)(
+ THIS_
+ __in ULONG Count,
+ __in ULONG64 Offset,
+ __out_ecount(Count) PULONG64 Ptrs
+ ) PURE;
+ STDMETHOD(WritePointersVirtual)(
+ THIS_
+ __in ULONG Count,
+ __in ULONG64 Offset,
+ __in_ecount(Count) PULONG64 Ptrs
+ ) PURE;
+ // All non-virtual data spaces are only
+ // available when kernel debugging.
+ STDMETHOD(ReadPhysical)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WritePhysical)(
+ THIS_
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(ReadControl)(
+ THIS_
+ __in ULONG Processor,
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteControl)(
+ THIS_
+ __in ULONG Processor,
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(ReadIo)(
+ THIS_
+ __in ULONG InterfaceType,
+ __in ULONG BusNumber,
+ __in ULONG AddressSpace,
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteIo)(
+ THIS_
+ __in ULONG InterfaceType,
+ __in ULONG BusNumber,
+ __in ULONG AddressSpace,
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(ReadMsr)(
+ THIS_
+ __in ULONG Msr,
+ __out PULONG64 Value
+ ) PURE;
+ STDMETHOD(WriteMsr)(
+ THIS_
+ __in ULONG Msr,
+ __in ULONG64 Value
+ ) PURE;
+ STDMETHOD(ReadBusData)(
+ THIS_
+ __in ULONG BusDataType,
+ __in ULONG BusNumber,
+ __in ULONG SlotNumber,
+ __in ULONG Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteBusData)(
+ THIS_
+ __in ULONG BusDataType,
+ __in ULONG BusNumber,
+ __in ULONG SlotNumber,
+ __in ULONG Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(CheckLowMemory)(
+ THIS
+ ) PURE;
+ STDMETHOD(ReadDebuggerData)(
+ THIS_
+ __in ULONG Index,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DataSize
+ ) PURE;
+ STDMETHOD(ReadProcessorSystemData)(
+ THIS_
+ __in ULONG Processor,
+ __in ULONG Index,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DataSize
+ ) PURE;
+};
+
+//
+// Handle data types and structures.
+//
+
+#define DEBUG_HANDLE_DATA_TYPE_BASIC 0
+#define DEBUG_HANDLE_DATA_TYPE_TYPE_NAME 1
+#define DEBUG_HANDLE_DATA_TYPE_OBJECT_NAME 2
+#define DEBUG_HANDLE_DATA_TYPE_HANDLE_COUNT 3
+#define DEBUG_HANDLE_DATA_TYPE_TYPE_NAME_WIDE 4
+#define DEBUG_HANDLE_DATA_TYPE_OBJECT_NAME_WIDE 5
+#define DEBUG_HANDLE_DATA_TYPE_MINI_THREAD_1 6
+#define DEBUG_HANDLE_DATA_TYPE_MINI_MUTANT_1 7
+#define DEBUG_HANDLE_DATA_TYPE_MINI_MUTANT_2 8
+#define DEBUG_HANDLE_DATA_TYPE_PER_HANDLE_OPERATIONS 9
+#define DEBUG_HANDLE_DATA_TYPE_ALL_HANDLE_OPERATIONS 10
+#define DEBUG_HANDLE_DATA_TYPE_MINI_PROCESS_1 11
+#define DEBUG_HANDLE_DATA_TYPE_MINI_PROCESS_2 12
+
+typedef struct _DEBUG_HANDLE_DATA_BASIC
+{
+ ULONG TypeNameSize;
+ ULONG ObjectNameSize;
+ ULONG Attributes;
+ ULONG GrantedAccess;
+ ULONG HandleCount;
+ ULONG PointerCount;
+} DEBUG_HANDLE_DATA_BASIC, *PDEBUG_HANDLE_DATA_BASIC;
+
+#undef INTERFACE
+#define INTERFACE IDebugDataSpaces2
+DECLARE_INTERFACE_(IDebugDataSpaces2, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugDataSpaces.
+ STDMETHOD(ReadVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ // SearchVirtual searches the given virtual
+ // address range for the given pattern. PatternSize
+ // gives the byte length of the pattern and PatternGranularity
+ // controls the granularity of comparisons during
+ // the search.
+ // For example, a DWORD-granular search would
+ // use a pattern granularity of four to search by DWORD
+ // increments.
+ STDMETHOD(SearchVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Length,
+ __in_bcount(PatternSize) PVOID Pattern,
+ __in ULONG PatternSize,
+ __in ULONG PatternGranularity,
+ __out PULONG64 MatchOffset
+ ) PURE;
+ // These methods are identical to Read/WriteVirtual
+ // except that they avoid the kernel virtual memory
+ // cache entirely and are therefore useful for reading
+ // virtual memory which is inherently volatile, such
+ // as memory-mapped device areas, without contaminating
+ // or invalidating the cache.
+ // In user-mode they are the same as Read/WriteVirtual.
+ STDMETHOD(ReadVirtualUncached)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteVirtualUncached)(
+ THIS_
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ // The following two methods are convenience
+ // methods for accessing pointer values.
+ // They automatically convert between native pointers
+ // and canonical 64-bit values as necessary.
+ // These routines stop at the first failure.
+ STDMETHOD(ReadPointersVirtual)(
+ THIS_
+ __in ULONG Count,
+ __in ULONG64 Offset,
+ __out_ecount(Count) PULONG64 Ptrs
+ ) PURE;
+ STDMETHOD(WritePointersVirtual)(
+ THIS_
+ __in ULONG Count,
+ __in ULONG64 Offset,
+ __in_ecount(Count) PULONG64 Ptrs
+ ) PURE;
+ // All non-virtual data spaces are only
+ // available when kernel debugging.
+ STDMETHOD(ReadPhysical)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WritePhysical)(
+ THIS_
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(ReadControl)(
+ THIS_
+ __in ULONG Processor,
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteControl)(
+ THIS_
+ __in ULONG Processor,
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(ReadIo)(
+ THIS_
+ __in ULONG InterfaceType,
+ __in ULONG BusNumber,
+ __in ULONG AddressSpace,
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteIo)(
+ THIS_
+ __in ULONG InterfaceType,
+ __in ULONG BusNumber,
+ __in ULONG AddressSpace,
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(ReadMsr)(
+ THIS_
+ __in ULONG Msr,
+ __out PULONG64 Value
+ ) PURE;
+ STDMETHOD(WriteMsr)(
+ THIS_
+ __in ULONG Msr,
+ __in ULONG64 Value
+ ) PURE;
+ STDMETHOD(ReadBusData)(
+ THIS_
+ __in ULONG BusDataType,
+ __in ULONG BusNumber,
+ __in ULONG SlotNumber,
+ __in ULONG Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteBusData)(
+ THIS_
+ __in ULONG BusDataType,
+ __in ULONG BusNumber,
+ __in ULONG SlotNumber,
+ __in ULONG Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(CheckLowMemory)(
+ THIS
+ ) PURE;
+ STDMETHOD(ReadDebuggerData)(
+ THIS_
+ __in ULONG Index,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DataSize
+ ) PURE;
+ STDMETHOD(ReadProcessorSystemData)(
+ THIS_
+ __in ULONG Processor,
+ __in ULONG Index,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DataSize
+ ) PURE;
+
+ // IDebugDataSpaces2.
+
+ STDMETHOD(VirtualToPhysical)(
+ THIS_
+ __in ULONG64 Virtual,
+ __out PULONG64 Physical
+ ) PURE;
+ // Returns the physical addresses for the
+ // N levels of the systems paging structures.
+ // Level zero is the starting base physical
+ // address for virtual translations.
+ // Levels one-(N-1) will point to the appropriate
+ // paging descriptor for the virtual address at
+ // the given level of the paging hierarchy. The
+ // exact number of levels depends on many factors.
+ // The last level will be the fully translated
+ // physical address, matching what VirtualToPhysical
+ // returns. If the address can only be partially
+ // translated S_FALSE is returned.
+ STDMETHOD(GetVirtualTranslationPhysicalOffsets)(
+ THIS_
+ __in ULONG64 Virtual,
+ __out_ecount_opt(OffsetsSize) PULONG64 Offsets,
+ __in ULONG OffsetsSize,
+ __out_opt PULONG Levels
+ ) PURE;
+
+ // System handle data is accessible in certain
+ // debug sessions. The particular data available
+ // varies from session to session and platform
+ // to platform.
+ STDMETHOD(ReadHandleData)(
+ THIS_
+ __in ULONG64 Handle,
+ __in ULONG DataType,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DataSize
+ ) PURE;
+
+ // Fills memory with the given pattern.
+ // The fill stops at the first non-writable byte.
+ STDMETHOD(FillVirtual)(
+ THIS_
+ __in ULONG64 Start,
+ __in ULONG Size,
+ __in_bcount(PatternSize) PVOID Pattern,
+ __in ULONG PatternSize,
+ __out_opt PULONG Filled
+ ) PURE;
+ STDMETHOD(FillPhysical)(
+ THIS_
+ __in ULONG64 Start,
+ __in ULONG Size,
+ __in_bcount(PatternSize) PVOID Pattern,
+ __in ULONG PatternSize,
+ __out_opt PULONG Filled
+ ) PURE;
+
+ // Queries virtual memory mapping information given
+ // an address similarly to the Win32 API VirtualQuery.
+ // MEMORY_BASIC_INFORMATION64 is defined in crash.h.
+ // This method currently only works for user-mode sessions.
+ STDMETHOD(QueryVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PMEMORY_BASIC_INFORMATION64 Info
+ ) PURE;
+};
+
+#undef INTERFACE
+#define INTERFACE IDebugDataSpaces3
+DECLARE_INTERFACE_(IDebugDataSpaces3, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugDataSpaces.
+ STDMETHOD(ReadVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ // SearchVirtual searches the given virtual
+ // address range for the given pattern. PatternSize
+ // gives the byte length of the pattern and PatternGranularity
+ // controls the granularity of comparisons during
+ // the search.
+ // For example, a DWORD-granular search would
+ // use a pattern granularity of four to search by DWORD
+ // increments.
+ STDMETHOD(SearchVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Length,
+ __in_bcount(PatternSize) PVOID Pattern,
+ __in ULONG PatternSize,
+ __in ULONG PatternGranularity,
+ __out PULONG64 MatchOffset
+ ) PURE;
+ // These methods are identical to Read/WriteVirtual
+ // except that they avoid the kernel virtual memory
+ // cache entirely and are therefore useful for reading
+ // virtual memory which is inherently volatile, such
+ // as memory-mapped device areas, without contaminating
+ // or invalidating the cache.
+ // In user-mode they are the same as Read/WriteVirtual.
+ STDMETHOD(ReadVirtualUncached)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteVirtualUncached)(
+ THIS_
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ // The following two methods are convenience
+ // methods for accessing pointer values.
+ // They automatically convert between native pointers
+ // and canonical 64-bit values as necessary.
+ // These routines stop at the first failure.
+ STDMETHOD(ReadPointersVirtual)(
+ THIS_
+ __in ULONG Count,
+ __in ULONG64 Offset,
+ __out_ecount(Count) PULONG64 Ptrs
+ ) PURE;
+ STDMETHOD(WritePointersVirtual)(
+ THIS_
+ __in ULONG Count,
+ __in ULONG64 Offset,
+ __in_ecount(Count) PULONG64 Ptrs
+ ) PURE;
+ // All non-virtual data spaces are only
+ // available when kernel debugging.
+ STDMETHOD(ReadPhysical)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WritePhysical)(
+ THIS_
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(ReadControl)(
+ THIS_
+ __in ULONG Processor,
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteControl)(
+ THIS_
+ __in ULONG Processor,
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(ReadIo)(
+ THIS_
+ __in ULONG InterfaceType,
+ __in ULONG BusNumber,
+ __in ULONG AddressSpace,
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteIo)(
+ THIS_
+ __in ULONG InterfaceType,
+ __in ULONG BusNumber,
+ __in ULONG AddressSpace,
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(ReadMsr)(
+ THIS_
+ __in ULONG Msr,
+ __out PULONG64 Value
+ ) PURE;
+ STDMETHOD(WriteMsr)(
+ THIS_
+ __in ULONG Msr,
+ __in ULONG64 Value
+ ) PURE;
+ STDMETHOD(ReadBusData)(
+ THIS_
+ __in ULONG BusDataType,
+ __in ULONG BusNumber,
+ __in ULONG SlotNumber,
+ __in ULONG Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteBusData)(
+ THIS_
+ __in ULONG BusDataType,
+ __in ULONG BusNumber,
+ __in ULONG SlotNumber,
+ __in ULONG Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(CheckLowMemory)(
+ THIS
+ ) PURE;
+ STDMETHOD(ReadDebuggerData)(
+ THIS_
+ __in ULONG Index,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DataSize
+ ) PURE;
+ STDMETHOD(ReadProcessorSystemData)(
+ THIS_
+ __in ULONG Processor,
+ __in ULONG Index,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DataSize
+ ) PURE;
+
+ // IDebugDataSpaces2.
+
+ STDMETHOD(VirtualToPhysical)(
+ THIS_
+ __in ULONG64 Virtual,
+ __out PULONG64 Physical
+ ) PURE;
+ // Returns the physical addresses for the
+ // N levels of the systems paging structures.
+ // Level zero is the starting base physical
+ // address for virtual translations.
+ // Levels one-(N-1) will point to the appropriate
+ // paging descriptor for the virtual address at
+ // the given level of the paging hierarchy. The
+ // exact number of levels depends on many factors.
+ // The last level will be the fully translated
+ // physical address, matching what VirtualToPhysical
+ // returns. If the address can only be partially
+ // translated S_FALSE is returned.
+ STDMETHOD(GetVirtualTranslationPhysicalOffsets)(
+ THIS_
+ __in ULONG64 Virtual,
+ __out_ecount_opt(OffsetsSize) PULONG64 Offsets,
+ __in ULONG OffsetsSize,
+ __out_opt PULONG Levels
+ ) PURE;
+
+ // System handle data is accessible in certain
+ // debug sessions. The particular data available
+ // varies from session to session and platform
+ // to platform.
+ STDMETHOD(ReadHandleData)(
+ THIS_
+ __in ULONG64 Handle,
+ __in ULONG DataType,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DataSize
+ ) PURE;
+
+ // Fills memory with the given pattern.
+ // The fill stops at the first non-writable byte.
+ STDMETHOD(FillVirtual)(
+ THIS_
+ __in ULONG64 Start,
+ __in ULONG Size,
+ __in_bcount(PatternSize) PVOID Pattern,
+ __in ULONG PatternSize,
+ __out_opt PULONG Filled
+ ) PURE;
+ STDMETHOD(FillPhysical)(
+ THIS_
+ __in ULONG64 Start,
+ __in ULONG Size,
+ __in_bcount(PatternSize) PVOID Pattern,
+ __in ULONG PatternSize,
+ __out_opt PULONG Filled
+ ) PURE;
+
+ // Queries virtual memory mapping information given
+ // an address similarly to the Win32 API VirtualQuery.
+ // MEMORY_BASIC_INFORMATION64 is defined in crash.h.
+ // This method currently only works for user-mode sessions.
+ STDMETHOD(QueryVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PMEMORY_BASIC_INFORMATION64 Info
+ ) PURE;
+
+ // IDebugDataSpaces3.
+
+ // Convenience method for reading an image
+ // header from virtual memory. Given the
+ // image base, this method determines where
+ // the NT headers are, validates the necessary
+ // markers and converts the headers into
+ // 64-bit form for consistency.
+ // A caller can check whether the headers were
+ // originally 32-bit by checking the optional
+ // header magic value.
+ // This method will not read ROM headers.
+ STDMETHOD(ReadImageNtHeaders)(
+ THIS_
+ __in ULONG64 ImageBase,
+ __out PIMAGE_NT_HEADERS64 Headers
+ ) PURE;
+
+ // Some debug sessions have arbitrary additional
+ // data available. For example, additional dump
+ // information files may contain extra information
+ // gathered at the same time as the primary dump.
+ // Such information is tagged with a unique identifier
+ // and can only be retrieved via the tag.
+ // Tagged data cannot be partially available; the
+ // tagged block is either fully present or completely
+ // absent.
+ STDMETHOD(ReadTagged)(
+ THIS_
+ __in LPGUID Tag,
+ __in ULONG Offset,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG TotalSize
+ ) PURE;
+ STDMETHOD(StartEnumTagged)(
+ THIS_
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(GetNextTagged)(
+ THIS_
+ __in ULONG64 Handle,
+ __out LPGUID Tag,
+ __out PULONG Size
+ ) PURE;
+ STDMETHOD(EndEnumTagged)(
+ THIS_
+ __in ULONG64 Handle
+ ) PURE;
+};
+
+#define DEBUG_OFFSINFO_VIRTUAL_SOURCE 0x00000001
+
+#define DEBUG_VSOURCE_INVALID 0x00000000
+#define DEBUG_VSOURCE_DEBUGGEE 0x00000001
+#define DEBUG_VSOURCE_MAPPED_IMAGE 0x00000002
+
+#define DEBUG_VSEARCH_DEFAULT 0x00000000
+#define DEBUG_VSEARCH_WRITABLE_ONLY 0x00000001
+
+#define DEBUG_PHYSICAL_DEFAULT 0x00000000
+#define DEBUG_PHYSICAL_CACHED 0x00000001
+#define DEBUG_PHYSICAL_UNCACHED 0x00000002
+#define DEBUG_PHYSICAL_WRITE_COMBINED 0x00000003
+
+#undef INTERFACE
+#define INTERFACE IDebugDataSpaces4
+DECLARE_INTERFACE_(IDebugDataSpaces4, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugDataSpaces.
+
+ STDMETHOD(ReadVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ // SearchVirtual searches the given virtual
+ // address range for the given pattern. PatternSize
+ // gives the byte length of the pattern and PatternGranularity
+ // controls the granularity of comparisons during
+ // the search.
+ // For example, a DWORD-granular search would
+ // use a pattern granularity of four to search by DWORD
+ // increments.
+ STDMETHOD(SearchVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Length,
+ __in_bcount(PatternSize) PVOID Pattern,
+ __in ULONG PatternSize,
+ __in ULONG PatternGranularity,
+ __out PULONG64 MatchOffset
+ ) PURE;
+ // These methods are identical to Read/WriteVirtual
+ // except that they avoid the kernel virtual memory
+ // cache entirely and are therefore useful for reading
+ // virtual memory which is inherently volatile, such
+ // as memory-mapped device areas, without contaminating
+ // or invalidating the cache.
+ // In user-mode they are the same as Read/WriteVirtual.
+ STDMETHOD(ReadVirtualUncached)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteVirtualUncached)(
+ THIS_
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ // The following two methods are convenience
+ // methods for accessing pointer values.
+ // They automatically convert between native pointers
+ // and canonical 64-bit values as necessary.
+ // These routines stop at the first failure.
+ STDMETHOD(ReadPointersVirtual)(
+ THIS_
+ __in ULONG Count,
+ __in ULONG64 Offset,
+ __out_ecount(Count) PULONG64 Ptrs
+ ) PURE;
+ STDMETHOD(WritePointersVirtual)(
+ THIS_
+ __in ULONG Count,
+ __in ULONG64 Offset,
+ __in_ecount(Count) PULONG64 Ptrs
+ ) PURE;
+ // All non-virtual data spaces are only
+ // available when kernel debugging.
+ STDMETHOD(ReadPhysical)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WritePhysical)(
+ THIS_
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(ReadControl)(
+ THIS_
+ __in ULONG Processor,
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteControl)(
+ THIS_
+ __in ULONG Processor,
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(ReadIo)(
+ THIS_
+ __in ULONG InterfaceType,
+ __in ULONG BusNumber,
+ __in ULONG AddressSpace,
+ __in ULONG64 Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteIo)(
+ THIS_
+ __in ULONG InterfaceType,
+ __in ULONG BusNumber,
+ __in ULONG AddressSpace,
+ __in ULONG64 Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(ReadMsr)(
+ THIS_
+ __in ULONG Msr,
+ __out PULONG64 Value
+ ) PURE;
+ STDMETHOD(WriteMsr)(
+ THIS_
+ __in ULONG Msr,
+ __in ULONG64 Value
+ ) PURE;
+ STDMETHOD(ReadBusData)(
+ THIS_
+ __in ULONG BusDataType,
+ __in ULONG BusNumber,
+ __in ULONG SlotNumber,
+ __in ULONG Offset,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteBusData)(
+ THIS_
+ __in ULONG BusDataType,
+ __in ULONG BusNumber,
+ __in ULONG SlotNumber,
+ __in ULONG Offset,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(CheckLowMemory)(
+ THIS
+ ) PURE;
+ STDMETHOD(ReadDebuggerData)(
+ THIS_
+ __in ULONG Index,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DataSize
+ ) PURE;
+ STDMETHOD(ReadProcessorSystemData)(
+ THIS_
+ __in ULONG Processor,
+ __in ULONG Index,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DataSize
+ ) PURE;
+
+ // IDebugDataSpaces2.
+
+ STDMETHOD(VirtualToPhysical)(
+ THIS_
+ __in ULONG64 Virtual,
+ __out PULONG64 Physical
+ ) PURE;
+ // Returns the physical addresses for the
+ // N levels of the systems paging structures.
+ // Level zero is the starting base physical
+ // address for virtual translations.
+ // Levels one-(N-1) will point to the appropriate
+ // paging descriptor for the virtual address at
+ // the given level of the paging hierarchy. The
+ // exact number of levels depends on many factors.
+ // The last level will be the fully translated
+ // physical address, matching what VirtualToPhysical
+ // returns. If the address can only be partially
+ // translated S_FALSE is returned.
+ STDMETHOD(GetVirtualTranslationPhysicalOffsets)(
+ THIS_
+ __in ULONG64 Virtual,
+ __out_ecount_opt(OffsetsSize) PULONG64 Offsets,
+ __in ULONG OffsetsSize,
+ __out_opt PULONG Levels
+ ) PURE;
+
+ // System handle data is accessible in certain
+ // debug sessions. The particular data available
+ // varies from session to session and platform
+ // to platform.
+ STDMETHOD(ReadHandleData)(
+ THIS_
+ __in ULONG64 Handle,
+ __in ULONG DataType,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG DataSize
+ ) PURE;
+
+ // Fills memory with the given pattern.
+ // The fill stops at the first non-writable byte.
+ STDMETHOD(FillVirtual)(
+ THIS_
+ __in ULONG64 Start,
+ __in ULONG Size,
+ __in_bcount(PatternSize) PVOID Pattern,
+ __in ULONG PatternSize,
+ __out_opt PULONG Filled
+ ) PURE;
+ STDMETHOD(FillPhysical)(
+ THIS_
+ __in ULONG64 Start,
+ __in ULONG Size,
+ __in_bcount(PatternSize) PVOID Pattern,
+ __in ULONG PatternSize,
+ __out_opt PULONG Filled
+ ) PURE;
+
+ // Queries virtual memory mapping information given
+ // an address similarly to the Win32 API VirtualQuery.
+ // MEMORY_BASIC_INFORMATION64 is defined in crash.h.
+ // This method currently only works for user-mode sessions.
+ STDMETHOD(QueryVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PMEMORY_BASIC_INFORMATION64 Info
+ ) PURE;
+
+ // IDebugDataSpaces3.
+
+ // Convenience method for reading an image
+ // header from virtual memory. Given the
+ // image base, this method determines where
+ // the NT headers are, validates the necessary
+ // markers and converts the headers into
+ // 64-bit form for consistency.
+ // A caller can check whether the headers were
+ // originally 32-bit by checking the optional
+ // header magic value.
+ // This method will not read ROM headers.
+ STDMETHOD(ReadImageNtHeaders)(
+ THIS_
+ __in ULONG64 ImageBase,
+ __out PIMAGE_NT_HEADERS64 Headers
+ ) PURE;
+
+ // Some debug sessions have arbitrary additional
+ // data available. For example, additional dump
+ // information files may contain extra information
+ // gathered at the same time as the primary dump.
+ // Such information is tagged with a unique identifier
+ // and can only be retrieved via the tag.
+ // Tagged data cannot be partially available; the
+ // tagged block is either fully present or completely
+ // absent.
+ STDMETHOD(ReadTagged)(
+ THIS_
+ __in LPGUID Tag,
+ __in ULONG Offset,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG TotalSize
+ ) PURE;
+ STDMETHOD(StartEnumTagged)(
+ THIS_
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(GetNextTagged)(
+ THIS_
+ __in ULONG64 Handle,
+ __out LPGUID Tag,
+ __out PULONG Size
+ ) PURE;
+ STDMETHOD(EndEnumTagged)(
+ THIS_
+ __in ULONG64 Handle
+ ) PURE;
+
+ // IDebugDataSpaces4.
+
+ // General information about an address in the given data space.
+ // Queries are from DEBUG_OFFSINFO_*.
+ STDMETHOD(GetOffsetInformation)(
+ THIS_
+ __in ULONG Space,
+ __in ULONG Which,
+ __in ULONG64 Offset,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG InfoSize
+ ) PURE;
+
+ // Given a particular address, return the
+ // next address which has a different validity.
+ // For example, in debug sessions such as a live
+ // user-mode session where virtual address validity
+ // changes from page to page this will return the
+ // page after the given page. In sessions such as
+ // a user-mode dump file where validity can change
+ // from byte to byte this will return the start of
+ // the next region that has different validity.
+ STDMETHOD(GetNextDifferentlyValidOffsetVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG64 NextOffset
+ ) PURE;
+
+ // Given a particular range of virtual addresses,
+ // find the first region which is valid memory.
+ STDMETHOD(GetValidRegionVirtual)(
+ THIS_
+ __in ULONG64 Base,
+ __in ULONG Size,
+ __out PULONG64 ValidBase,
+ __out PULONG ValidSize
+ ) PURE;
+
+ STDMETHOD(SearchVirtual2)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Length,
+ __in ULONG Flags,
+ __in_bcount(PatternSize) PVOID Pattern,
+ __in ULONG PatternSize,
+ __in ULONG PatternGranularity,
+ __out PULONG64 MatchOffset
+ ) PURE;
+
+ // Attempts to read a multi-byte string
+ // starting at the given virtual address.
+ // The possible string length, including terminator,
+ // is capped at the given max size.
+ // If a return buffer is given it will always
+ // be terminated.
+ STDMETHOD(ReadMultiByteStringVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG MaxBytes,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG StringBytes
+ ) PURE;
+ // Reads a multi-byte string and converts
+ // it to Unicode using the given code page.
+ STDMETHOD(ReadMultiByteStringVirtualWide)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG MaxBytes,
+ __in ULONG CodePage,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG StringBytes
+ ) PURE;
+ STDMETHOD(ReadUnicodeStringVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG MaxBytes,
+ __in ULONG CodePage,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG StringBytes
+ ) PURE;
+ STDMETHOD(ReadUnicodeStringVirtualWide)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG MaxBytes,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG StringBytes
+ ) PURE;
+
+ STDMETHOD(ReadPhysical2)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WritePhysical2)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+};
+
+//----------------------------------------------------------------------------
+//
+// IDebugEventCallbacks.
+//
+//----------------------------------------------------------------------------
+
+// Interest mask bits.
+#define DEBUG_EVENT_BREAKPOINT 0x00000001
+#define DEBUG_EVENT_EXCEPTION 0x00000002
+#define DEBUG_EVENT_CREATE_THREAD 0x00000004
+#define DEBUG_EVENT_EXIT_THREAD 0x00000008
+#define DEBUG_EVENT_CREATE_PROCESS 0x00000010
+#define DEBUG_EVENT_EXIT_PROCESS 0x00000020
+#define DEBUG_EVENT_LOAD_MODULE 0x00000040
+#define DEBUG_EVENT_UNLOAD_MODULE 0x00000080
+#define DEBUG_EVENT_SYSTEM_ERROR 0x00000100
+#define DEBUG_EVENT_SESSION_STATUS 0x00000200
+#define DEBUG_EVENT_CHANGE_DEBUGGEE_STATE 0x00000400
+#define DEBUG_EVENT_CHANGE_ENGINE_STATE 0x00000800
+#define DEBUG_EVENT_CHANGE_SYMBOL_STATE 0x00001000
+
+// SessionStatus flags.
+// A debuggee has been discovered for the session.
+#define DEBUG_SESSION_ACTIVE 0x00000000
+// The session has been ended by EndSession.
+#define DEBUG_SESSION_END_SESSION_ACTIVE_TERMINATE 0x00000001
+#define DEBUG_SESSION_END_SESSION_ACTIVE_DETACH 0x00000002
+#define DEBUG_SESSION_END_SESSION_PASSIVE 0x00000003
+// The debuggee has run to completion. User-mode only.
+#define DEBUG_SESSION_END 0x00000004
+// The target machine has rebooted. Kernel-mode only.
+#define DEBUG_SESSION_REBOOT 0x00000005
+// The target machine has hibernated. Kernel-mode only.
+#define DEBUG_SESSION_HIBERNATE 0x00000006
+// The engine was unable to continue the session.
+#define DEBUG_SESSION_FAILURE 0x00000007
+
+// ChangeDebuggeeState flags.
+// The debuggees state has changed generally, such
+// as when the debuggee has been executing.
+// Argument is zero.
+#define DEBUG_CDS_ALL 0xffffffff
+// Registers have changed. If only a single register
+// changed, argument is the index of the register.
+// Otherwise it is DEBUG_ANY_ID.
+#define DEBUG_CDS_REGISTERS 0x00000001
+// Data spaces have changed. If only a single
+// space was affected, argument is the data
+// space. Otherwise it is DEBUG_ANY_ID.
+#define DEBUG_CDS_DATA 0x00000002
+
+// ChangeEngineState flags.
+// The engine state has changed generally.
+// Argument is zero.
+#define DEBUG_CES_ALL 0xffffffff
+// Current thread changed. This may imply a change
+// of system and process also. Argument is the ID of the new
+// current thread or DEBUG_ANY_ID if no thread is current.
+#define DEBUG_CES_CURRENT_THREAD 0x00000001
+// Effective processor changed. Argument is the
+// new processor type.
+#define DEBUG_CES_EFFECTIVE_PROCESSOR 0x00000002
+// Breakpoints changed. If only a single breakpoint
+// changed, argument is the ID of the breakpoint.
+// Otherwise it is DEBUG_ANY_ID.
+#define DEBUG_CES_BREAKPOINTS 0x00000004
+// Code interpretation level changed. Argument is
+// the new level.
+#define DEBUG_CES_CODE_LEVEL 0x00000008
+// Execution status changed. Argument is the new
+// execution status.
+#define DEBUG_CES_EXECUTION_STATUS 0x00000010
+// Engine options have changed. Argument is the new
+// options value.
+#define DEBUG_CES_ENGINE_OPTIONS 0x00000020
+// Log file information has changed. Argument
+// is TRUE if a log file was opened and FALSE if
+// a log file was closed.
+#define DEBUG_CES_LOG_FILE 0x00000040
+// Default number radix has changed. Argument
+// is the new radix.
+#define DEBUG_CES_RADIX 0x00000080
+// Event filters changed. If only a single filter
+// changed the argument is the filter's index,
+// otherwise it is DEBUG_ANY_ID.
+#define DEBUG_CES_EVENT_FILTERS 0x00000100
+// Process options have changed. Argument is the new
+// options value.
+#define DEBUG_CES_PROCESS_OPTIONS 0x00000200
+// Extensions have been added or removed.
+#define DEBUG_CES_EXTENSIONS 0x00000400
+// Systems have been added or removed. The argument
+// is the system ID. Systems, unlike processes and
+// threads, may be created at any time and not
+// just during WaitForEvent.
+#define DEBUG_CES_SYSTEMS 0x00000800
+// Assembly/disassembly options have changed. Argument
+// is the new options value.
+#define DEBUG_CES_ASSEMBLY_OPTIONS 0x00001000
+// Expression syntax has changed. Argument
+// is the new syntax value.
+#define DEBUG_CES_EXPRESSION_SYNTAX 0x00002000
+// Text replacements have changed.
+#define DEBUG_CES_TEXT_REPLACEMENTS 0x00004000
+
+// ChangeSymbolState flags.
+// Symbol state has changed generally, such
+// as after reload operations. Argument is zero.
+#define DEBUG_CSS_ALL 0xffffffff
+// Modules have been loaded. If only a
+// single module changed, argument is the
+// base address of the module. Otherwise
+// it is zero.
+#define DEBUG_CSS_LOADS 0x00000001
+// Modules have been unloaded. If only a
+// single module changed, argument is the
+// base address of the module. Otherwise
+// it is zero.
+#define DEBUG_CSS_UNLOADS 0x00000002
+// Current symbol scope changed.
+#define DEBUG_CSS_SCOPE 0x00000004
+// Paths have changed.
+#define DEBUG_CSS_PATHS 0x00000008
+// Symbol options have changed. Argument is the new
+// options value.
+#define DEBUG_CSS_SYMBOL_OPTIONS 0x00000010
+// Type options have changed. Argument is the new
+// options value.
+#define DEBUG_CSS_TYPE_OPTIONS 0x00000020
+
+#undef INTERFACE
+#define INTERFACE IDebugEventCallbacks
+DECLARE_INTERFACE_(IDebugEventCallbacks, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugEventCallbacks.
+
+ // The engine calls GetInterestMask once when
+ // the event callbacks are set for a client.
+ STDMETHOD(GetInterestMask)(
+ THIS_
+ __out PULONG Mask
+ ) PURE;
+
+ // A breakpoint event is generated when
+ // a breakpoint exception is received and
+ // it can be mapped to an existing breakpoint.
+ // The callback method is given a reference
+ // to the breakpoint and should release it when
+ // it is done with it.
+ STDMETHOD(Breakpoint)(
+ THIS_
+ __in PDEBUG_BREAKPOINT Bp
+ ) PURE;
+
+ // Exceptions include breaks which cannot
+ // be mapped to an existing breakpoint
+ // instance.
+ STDMETHOD(Exception)(
+ THIS_
+ __in PEXCEPTION_RECORD64 Exception,
+ __in ULONG FirstChance
+ ) PURE;
+
+ // Any of these values can be zero if they
+ // cannot be provided by the engine.
+ // Currently the kernel does not return thread
+ // or process change events.
+ STDMETHOD(CreateThread)(
+ THIS_
+ __in ULONG64 Handle,
+ __in ULONG64 DataOffset,
+ __in ULONG64 StartOffset
+ ) PURE;
+ STDMETHOD(ExitThread)(
+ THIS_
+ __in ULONG ExitCode
+ ) PURE;
+
+ // Any of these values can be zero if they
+ // cannot be provided by the engine.
+ STDMETHOD(CreateProcess)(
+ THIS_
+ __in ULONG64 ImageFileHandle,
+ __in ULONG64 Handle,
+ __in ULONG64 BaseOffset,
+ __in ULONG ModuleSize,
+ __in_opt PCSTR ModuleName,
+ __in_opt PCSTR ImageName,
+ __in ULONG CheckSum,
+ __in ULONG TimeDateStamp,
+ __in ULONG64 InitialThreadHandle,
+ __in ULONG64 ThreadDataOffset,
+ __in ULONG64 StartOffset
+ ) PURE;
+ STDMETHOD(ExitProcess)(
+ THIS_
+ __in ULONG ExitCode
+ ) PURE;
+
+ // Any of these values may be zero.
+ STDMETHOD(LoadModule)(
+ THIS_
+ __in ULONG64 ImageFileHandle,
+ __in ULONG64 BaseOffset,
+ __in ULONG ModuleSize,
+ __in_opt PCSTR ModuleName,
+ __in_opt PCSTR ImageName,
+ __in ULONG CheckSum,
+ __in ULONG TimeDateStamp
+ ) PURE;
+ STDMETHOD(UnloadModule)(
+ THIS_
+ __in_opt PCSTR ImageBaseName,
+ __in ULONG64 BaseOffset
+ ) PURE;
+
+ STDMETHOD(SystemError)(
+ THIS_
+ __in ULONG Error,
+ __in ULONG Level
+ ) PURE;
+
+ // Session status is synchronous like the other
+ // wait callbacks but it is called as the state
+ // of the session is changing rather than at
+ // specific events so its return value does not
+ // influence waiting. Implementations should just
+ // return DEBUG_STATUS_NO_CHANGE.
+ // Also, because some of the status
+ // notifications are very early or very
+ // late in the session lifetime there may not be
+ // current processes or threads when the notification
+ // is generated.
+ STDMETHOD(SessionStatus)(
+ THIS_
+ __in ULONG Status
+ ) PURE;
+
+ // The following callbacks are informational
+ // callbacks notifying the provider about
+ // changes in debug state. The return value
+ // of these callbacks is ignored. Implementations
+ // can not call back into the engine.
+
+ // Debuggee state, such as registers or data spaces,
+ // has changed.
+ STDMETHOD(ChangeDebuggeeState)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG64 Argument
+ ) PURE;
+ // Engine state has changed.
+ STDMETHOD(ChangeEngineState)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG64 Argument
+ ) PURE;
+ // Symbol state has changed.
+ STDMETHOD(ChangeSymbolState)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG64 Argument
+ ) PURE;
+};
+
+#undef INTERFACE
+#define INTERFACE IDebugEventCallbacksWide
+DECLARE_INTERFACE_(IDebugEventCallbacksWide, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugEventCallbacksWide.
+
+ // The engine calls GetInterestMask once when
+ // the event callbacks are set for a client.
+ STDMETHOD(GetInterestMask)(
+ THIS_
+ __out PULONG Mask
+ ) PURE;
+
+ // A breakpoint event is generated when
+ // a breakpoint exception is received and
+ // it can be mapped to an existing breakpoint.
+ // The callback method is given a reference
+ // to the breakpoint and should release it when
+ // it is done with it.
+ STDMETHOD(Breakpoint)(
+ THIS_
+ __in PDEBUG_BREAKPOINT2 Bp
+ ) PURE;
+
+ // Exceptions include breaks which cannot
+ // be mapped to an existing breakpoint
+ // instance.
+ STDMETHOD(Exception)(
+ THIS_
+ __in PEXCEPTION_RECORD64 Exception,
+ __in ULONG FirstChance
+ ) PURE;
+
+ // Any of these values can be zero if they
+ // cannot be provided by the engine.
+ // Currently the kernel does not return thread
+ // or process change events.
+ STDMETHOD(CreateThread)(
+ THIS_
+ __in ULONG64 Handle,
+ __in ULONG64 DataOffset,
+ __in ULONG64 StartOffset
+ ) PURE;
+ STDMETHOD(ExitThread)(
+ THIS_
+ __in ULONG ExitCode
+ ) PURE;
+
+ // Any of these values can be zero if they
+ // cannot be provided by the engine.
+ STDMETHOD(CreateProcess)(
+ THIS_
+ __in ULONG64 ImageFileHandle,
+ __in ULONG64 Handle,
+ __in ULONG64 BaseOffset,
+ __in ULONG ModuleSize,
+ __in_opt PCWSTR ModuleName,
+ __in_opt PCWSTR ImageName,
+ __in ULONG CheckSum,
+ __in ULONG TimeDateStamp,
+ __in ULONG64 InitialThreadHandle,
+ __in ULONG64 ThreadDataOffset,
+ __in ULONG64 StartOffset
+ ) PURE;
+ STDMETHOD(ExitProcess)(
+ THIS_
+ __in ULONG ExitCode
+ ) PURE;
+
+ // Any of these values may be zero.
+ STDMETHOD(LoadModule)(
+ THIS_
+ __in ULONG64 ImageFileHandle,
+ __in ULONG64 BaseOffset,
+ __in ULONG ModuleSize,
+ __in_opt PCWSTR ModuleName,
+ __in_opt PCWSTR ImageName,
+ __in ULONG CheckSum,
+ __in ULONG TimeDateStamp
+ ) PURE;
+ STDMETHOD(UnloadModule)(
+ THIS_
+ __in_opt PCWSTR ImageBaseName,
+ __in ULONG64 BaseOffset
+ ) PURE;
+
+ STDMETHOD(SystemError)(
+ THIS_
+ __in ULONG Error,
+ __in ULONG Level
+ ) PURE;
+
+ // Session status is synchronous like the other
+ // wait callbacks but it is called as the state
+ // of the session is changing rather than at
+ // specific events so its return value does not
+ // influence waiting. Implementations should just
+ // return DEBUG_STATUS_NO_CHANGE.
+ // Also, because some of the status
+ // notifications are very early or very
+ // late in the session lifetime there may not be
+ // current processes or threads when the notification
+ // is generated.
+ STDMETHOD(SessionStatus)(
+ THIS_
+ __in ULONG Status
+ ) PURE;
+
+ // The following callbacks are informational
+ // callbacks notifying the provider about
+ // changes in debug state. The return value
+ // of these callbacks is ignored. Implementations
+ // can not call back into the engine.
+
+ // Debuggee state, such as registers or data spaces,
+ // has changed.
+ STDMETHOD(ChangeDebuggeeState)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG64 Argument
+ ) PURE;
+ // Engine state has changed.
+ STDMETHOD(ChangeEngineState)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG64 Argument
+ ) PURE;
+ // Symbol state has changed.
+ STDMETHOD(ChangeSymbolState)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG64 Argument
+ ) PURE;
+};
+
+//----------------------------------------------------------------------------
+//
+// IDebugInputCallbacks.
+//
+//----------------------------------------------------------------------------
+
+#undef INTERFACE
+#define INTERFACE IDebugInputCallbacks
+DECLARE_INTERFACE_(IDebugInputCallbacks, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugInputCallbacks.
+
+ // A call to the StartInput method is a request for
+ // a line of input from any client. The returned input
+ // should always be zero-terminated. The buffer size
+ // provided is only a guideline. A client can return
+ // more if necessary and the engine will truncate it
+ // before returning from IDebugControl::Input.
+ // The return value is ignored.
+ STDMETHOD(StartInput)(
+ THIS_
+ __in ULONG BufferSize
+ ) PURE;
+ // The return value is ignored.
+ STDMETHOD(EndInput)(
+ THIS
+ ) PURE;
+};
+
+//----------------------------------------------------------------------------
+//
+// IDebugOutputCallbacks.
+//
+//----------------------------------------------------------------------------
+
+#undef INTERFACE
+#define INTERFACE IDebugOutputCallbacks
+DECLARE_INTERFACE_(IDebugOutputCallbacks, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugOutputCallbacks.
+
+ // This method is only called if the supplied mask
+ // is allowed by the clients output control.
+ // The return value is ignored.
+ STDMETHOD(Output)(
+ THIS_
+ __in ULONG Mask,
+ __in PCSTR Text
+ ) PURE;
+};
+
+#undef INTERFACE
+#define INTERFACE IDebugOutputCallbacksWide
+DECLARE_INTERFACE_(IDebugOutputCallbacksWide, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugOutputCallbacksWide.
+
+ // This method is only called if the supplied mask
+ // is allowed by the clients output control.
+ // The return value is ignored.
+ STDMETHOD(Output)(
+ THIS_
+ __in ULONG Mask,
+ __in PCWSTR Text
+ ) PURE;
+};
+
+//
+// IDebugOutputCallbacks2 interest mask flags.
+//
+
+// Indicates that the callback wants notifications
+// of all explicit flushes.
+#define DEBUG_OUTCBI_EXPLICIT_FLUSH 0x00000001
+// Indicates that the callback wants
+// content in text form.
+#define DEBUG_OUTCBI_TEXT 0x00000002
+// Indicates that the callback wants
+// content in markup form.
+#define DEBUG_OUTCBI_DML 0x00000004
+
+#define DEBUG_OUTCBI_ANY_FORMAT 0x00000006
+
+//
+// Different kinds of output callback notifications
+// that can be sent to Output2.
+//
+
+// Plain text content, flags are below, argument is mask.
+#define DEBUG_OUTCB_TEXT 0
+// Debugger markup content, flags are below, argument is mask.
+#define DEBUG_OUTCB_DML 1
+// Notification of an explicit output flush, flags and argument are zero.
+#define DEBUG_OUTCB_EXPLICIT_FLUSH 2
+
+//
+// Flags for various Output2 callbacks.
+//
+
+// The content string was followed by an
+// explicit flush. This flag will be used
+// instead of a separate DEBUG_OUTCB_EXPLICIT_FLUSH
+// callback when a flush has text to flush,
+// thus avoiding two callbacks.
+#define DEBUG_OUTCBF_COMBINED_EXPLICIT_FLUSH 0x00000001
+
+// The markup content string has embedded tags.
+#define DEBUG_OUTCBF_DML_HAS_TAGS 0x00000002
+// The markup content has encoded special characters like ", &, < and >.
+#define DEBUG_OUTCBF_DML_HAS_SPECIAL_CHARACTERS 0x00000004
+
+#undef INTERFACE
+#define INTERFACE IDebugOutputCallbacks2
+DECLARE_INTERFACE_(IDebugOutputCallbacks2, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugOutputCallbacks.
+
+ // This method is not used.
+ STDMETHOD(Output)(
+ THIS_
+ __in ULONG Mask,
+ __in PCSTR Text
+ ) PURE;
+
+ // IDebugOutputCallbacks2.
+
+ // The engine calls GetInterestMask once when
+ // the callbacks are set for a client.
+ STDMETHOD(GetInterestMask)(
+ THIS_
+ __out PULONG Mask
+ ) PURE;
+
+ STDMETHOD(Output2)(
+ THIS_
+ __in ULONG Which,
+ __in ULONG Flags,
+ __in ULONG64 Arg,
+ __in_opt PCWSTR Text
+ ) PURE;
+};
+
+//----------------------------------------------------------------------------
+//
+// IDebugRegisters.
+//
+//----------------------------------------------------------------------------
+
+#define DEBUG_REGISTERS_DEFAULT 0x00000000
+#define DEBUG_REGISTERS_INT32 0x00000001
+#define DEBUG_REGISTERS_INT64 0x00000002
+#define DEBUG_REGISTERS_FLOAT 0x00000004
+#define DEBUG_REGISTERS_ALL 0x00000007
+
+#define DEBUG_REGISTER_SUB_REGISTER 0x00000001
+
+typedef struct _DEBUG_REGISTER_DESCRIPTION
+{
+ // DEBUG_VALUE type.
+ ULONG Type;
+ ULONG Flags;
+
+ // If this is a subregister the full
+ // registers description index is
+ // given in SubregMaster. The length, mask
+ // and shift describe how the subregisters
+ // bits fit into the full register.
+ ULONG SubregMaster;
+ ULONG SubregLength;
+ ULONG64 SubregMask;
+ ULONG SubregShift;
+
+ ULONG Reserved0;
+} DEBUG_REGISTER_DESCRIPTION, *PDEBUG_REGISTER_DESCRIPTION;
+
+#undef INTERFACE
+#define INTERFACE IDebugRegisters
+DECLARE_INTERFACE_(IDebugRegisters, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugRegisters.
+ STDMETHOD(GetNumberRegisters)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetDescription)(
+ THIS_
+ __in ULONG Register,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PDEBUG_REGISTER_DESCRIPTION Desc
+ ) PURE;
+ STDMETHOD(GetIndexByName)(
+ THIS_
+ __in PCSTR Name,
+ __out PULONG Index
+ ) PURE;
+
+ STDMETHOD(GetValue)(
+ THIS_
+ __in ULONG Register,
+ __out PDEBUG_VALUE Value
+ ) PURE;
+ // SetValue makes a best effort at coercing
+ // the given value into the given registers
+ // value type. If the given value is larger
+ // than the register can hold the least
+ // significant bits will be dropped. Float
+ // to int and int to float will be done
+ // if necessary. Subregister bits will be
+ // inserted into the master register.
+ STDMETHOD(SetValue)(
+ THIS_
+ __in ULONG Register,
+ __in PDEBUG_VALUE Value
+ ) PURE;
+ // Gets Count register values. If Indices is
+ // non-NULL it must contain Count register
+ // indices which control the registers affected.
+ // If Indices is NULL the registers from Start
+ // to Start + Count 1 are retrieved.
+ STDMETHOD(GetValues)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Indices,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_VALUE Values
+ ) PURE;
+ STDMETHOD(SetValues)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Indices,
+ __in ULONG Start,
+ __in_ecount(Count) PDEBUG_VALUE Values
+ ) PURE;
+
+ // Outputs a group of registers in a well-formatted
+ // way thats specific to the platforms register set.
+ // Uses the line prefix.
+ STDMETHOD(OutputRegisters)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags
+ ) PURE;
+
+ // 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.
+ STDMETHOD(GetInstructionOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(GetStackOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(GetFrameOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+};
+
+//
+// The engine maintains several separate
+// pieces of context information. There is
+// the current debuggee context, a possible
+// override context, such as from .cxr,
+// a context for the current scope frame and so on.
+//
+
+// Get register information from the debuggee.
+#define DEBUG_REGSRC_DEBUGGEE 0x00000000
+// Get register information from an explicit
+// override context, such as one set by .cxr.
+// If there is no override context the request will fail.
+#define DEBUG_REGSRC_EXPLICIT 0x00000001
+// Get register information from the current scope
+// frame. Note that stack unwinding does not guarantee
+// accurate updating of the register context,
+// so scope frame register context may not be accurate
+// in all cases.
+#define DEBUG_REGSRC_FRAME 0x00000002
+
+#undef INTERFACE
+#define INTERFACE IDebugRegisters2
+DECLARE_INTERFACE_(IDebugRegisters2, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugRegisters.
+
+ STDMETHOD(GetNumberRegisters)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetDescription)(
+ THIS_
+ __in ULONG Register,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PDEBUG_REGISTER_DESCRIPTION Desc
+ ) PURE;
+ STDMETHOD(GetIndexByName)(
+ THIS_
+ __in PCSTR Name,
+ __out PULONG Index
+ ) PURE;
+
+ STDMETHOD(GetValue)(
+ THIS_
+ __in ULONG Register,
+ __out PDEBUG_VALUE Value
+ ) PURE;
+ // SetValue makes a best effort at coercing
+ // the given value into the given registers
+ // value type. If the given value is larger
+ // than the register can hold the least
+ // significant bits will be dropped. Float
+ // to int and int to float will be done
+ // if necessary. Subregister bits will be
+ // inserted into the master register.
+ STDMETHOD(SetValue)(
+ THIS_
+ __in ULONG Register,
+ __in PDEBUG_VALUE Value
+ ) PURE;
+ // Gets Count register values. If Indices is
+ // non-NULL it must contain Count register
+ // indices which control the registers affected.
+ // If Indices is NULL the registers from Start
+ // to Start + Count 1 are retrieved.
+ STDMETHOD(GetValues)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Indices,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_VALUE Values
+ ) PURE;
+ STDMETHOD(SetValues)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Indices,
+ __in ULONG Start,
+ __in_ecount(Count) PDEBUG_VALUE Values
+ ) PURE;
+
+ // Outputs a group of registers in a well-formatted
+ // way thats specific to the platforms register set.
+ // Uses the line prefix.
+ STDMETHOD(OutputRegisters)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags
+ ) PURE;
+
+ // 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.
+ STDMETHOD(GetInstructionOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(GetStackOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(GetFrameOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+
+ // IDebugRegisters2.
+
+ STDMETHOD(GetDescriptionWide)(
+ THIS_
+ __in ULONG Register,
+ __out_ecount_opt(NameBufferSize) PWSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PDEBUG_REGISTER_DESCRIPTION Desc
+ ) PURE;
+ STDMETHOD(GetIndexByNameWide)(
+ THIS_
+ __in PCWSTR Name,
+ __out PULONG Index
+ ) PURE;
+
+ // Pseudo-registers are synthetic values derived
+ // by the engine that are presented in a manner
+ // similar to regular registers. They are simple
+ // value holders, similar to actual registers.
+ // Pseudo-registers are defined for concepts,
+ // such as current-instruction-pointer or
+ // current-thread-data. As such they have
+ // types appropriate for their data.
+ STDMETHOD(GetNumberPseudoRegisters)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetPseudoDescription)(
+ THIS_
+ __in ULONG Register,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PULONG64 TypeModule,
+ __out_opt PULONG TypeId
+ ) PURE;
+ STDMETHOD(GetPseudoDescriptionWide)(
+ THIS_
+ __in ULONG Register,
+ __out_ecount_opt(NameBufferSize) PWSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PULONG64 TypeModule,
+ __out_opt PULONG TypeId
+ ) PURE;
+ STDMETHOD(GetPseudoIndexByName)(
+ THIS_
+ __in PCSTR Name,
+ __out PULONG Index
+ ) PURE;
+ STDMETHOD(GetPseudoIndexByNameWide)(
+ THIS_
+ __in PCWSTR Name,
+ __out PULONG Index
+ ) PURE;
+ // Some pseudo-register values are affected
+ // by the register source, others are not.
+ STDMETHOD(GetPseudoValues)(
+ THIS_
+ __in ULONG Source,
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Indices,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_VALUE Values
+ ) PURE;
+ // Many pseudo-registers are read-only and cannot be set.
+ STDMETHOD(SetPseudoValues)(
+ THIS_
+ __in ULONG Source,
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Indices,
+ __in ULONG Start,
+ __in_ecount(Count) PDEBUG_VALUE Values
+ ) PURE;
+
+ // These expanded methods allow selection
+ // of the source of register information.
+ STDMETHOD(GetValues2)(
+ THIS_
+ __in ULONG Source,
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Indices,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_VALUE Values
+ ) PURE;
+ STDMETHOD(SetValues2)(
+ THIS_
+ __in ULONG Source,
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG Indices,
+ __in ULONG Start,
+ __in_ecount(Count) PDEBUG_VALUE Values
+ ) PURE;
+ STDMETHOD(OutputRegisters2)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Source,
+ __in ULONG Flags
+ ) PURE;
+ STDMETHOD(GetInstructionOffset2)(
+ THIS_
+ __in ULONG Source,
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(GetStackOffset2)(
+ THIS_
+ __in ULONG Source,
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(GetFrameOffset2)(
+ THIS_
+ __in ULONG Source,
+ __out PULONG64 Offset
+ ) PURE;
+};
+
+//----------------------------------------------------------------------------
+//
+// IDebugSymbolGroup
+//
+//----------------------------------------------------------------------------
+
+// OutputSymbols flags.
+// Default output contains
+// <Name>**NAME**<Offset>**OFF**<Value>**VALUE**<Type>**TYPE**
+// per symbol.
+#define DEBUG_OUTPUT_SYMBOLS_DEFAULT 0x00000000
+#define DEBUG_OUTPUT_SYMBOLS_NO_NAMES 0x00000001
+#define DEBUG_OUTPUT_SYMBOLS_NO_OFFSETS 0x00000002
+#define DEBUG_OUTPUT_SYMBOLS_NO_VALUES 0x00000004
+#define DEBUG_OUTPUT_SYMBOLS_NO_TYPES 0x00000010
+
+#define DEBUG_OUTPUT_NAME_END "**NAME**"
+#define DEBUG_OUTPUT_OFFSET_END "**OFF**"
+#define DEBUG_OUTPUT_VALUE_END "**VALUE**"
+#define DEBUG_OUTPUT_TYPE_END "**TYPE**"
+
+#define DEBUG_OUTPUT_NAME_END_WIDE L"**NAME**"
+#define DEBUG_OUTPUT_OFFSET_END_WIDE L"**OFF**"
+#define DEBUG_OUTPUT_VALUE_END_WIDE L"**VALUE**"
+#define DEBUG_OUTPUT_TYPE_END_WIDE L"**TYPE**"
+
+#ifdef UNICODE
+#define DEBUG_OUTPUT_NAME_END_T DEBUG_OUTPUT_NAME_END_WIDE
+#define DEBUG_OUTPUT_OFFSET_END_T DEBUG_OUTPUT_OFFSET_END_WIDE
+#define DEBUG_OUTPUT_VALUE_END_T DEBUG_OUTPUT_VALUE_END_WIDE
+#define DEBUG_OUTPUT_TYPE_END_T DEBUG_OUTPUT_TYPE_END_WIDE
+#else
+#define DEBUG_OUTPUT_NAME_END_T DEBUG_OUTPUT_NAME_END
+#define DEBUG_OUTPUT_OFFSET_END_T DEBUG_OUTPUT_OFFSET_END
+#define DEBUG_OUTPUT_VALUE_END_T DEBUG_OUTPUT_VALUE_END
+#define DEBUG_OUTPUT_TYPE_END_T DEBUG_OUTPUT_TYPE_END
+#endif
+
+// DEBUG_SYMBOL_PARAMETERS flags.
+// Cumulative expansion level, takes four bits.
+#define DEBUG_SYMBOL_EXPANSION_LEVEL_MASK 0x0000000f
+// Symbols subelements follow.
+#define DEBUG_SYMBOL_EXPANDED 0x00000010
+// Symbols value is read-only.
+#define DEBUG_SYMBOL_READ_ONLY 0x00000020
+// Symbol subelements are array elements.
+#define DEBUG_SYMBOL_IS_ARRAY 0x00000040
+// Symbol is a float value.
+#define DEBUG_SYMBOL_IS_FLOAT 0x00000080
+// Symbol is a scope argument.
+#define DEBUG_SYMBOL_IS_ARGUMENT 0x00000100
+// Symbol is a scope argument.
+#define DEBUG_SYMBOL_IS_LOCAL 0x00000200
+
+typedef struct _DEBUG_SYMBOL_PARAMETERS
+{
+ ULONG64 Module;
+ ULONG TypeId;
+ // ParentSymbol may be DEBUG_ANY_ID when unknown.
+ ULONG ParentSymbol;
+ // A subelement of a symbol can be a field, such
+ // as in structs, unions or classes; or an array
+ // element count for arrays.
+ ULONG SubElements;
+ ULONG Flags;
+ ULONG64 Reserved;
+} DEBUG_SYMBOL_PARAMETERS, *PDEBUG_SYMBOL_PARAMETERS;
+
+#undef INTERFACE
+#define INTERFACE IDebugSymbolGroup
+DECLARE_INTERFACE_(IDebugSymbolGroup, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugSymbolGroup.
+ STDMETHOD(GetNumberSymbols)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ // On input Index indicates the desired insertion
+ // index. On output Index contains the actual index.
+ // Use DEBUG_ANY_ID to append a symbol to the end.
+ STDMETHOD(AddSymbol)(
+ THIS_
+ __in PCSTR Name,
+ __inout PULONG Index
+ ) PURE;
+ STDMETHOD(RemoveSymbolByName)(
+ THIS_
+ __in PCSTR Name
+ ) PURE;
+ STDMETHOD(RemoveSymbolByIndex)(
+ THIS_
+ __in ULONG Index
+ ) PURE;
+ STDMETHOD(GetSymbolName)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+ STDMETHOD(GetSymbolParameters)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PDEBUG_SYMBOL_PARAMETERS Params
+ ) PURE;
+ STDMETHOD(ExpandSymbol)(
+ THIS_
+ __in ULONG Index,
+ __in BOOL Expand
+ ) PURE;
+ // Uses the line prefix.
+ STDMETHOD(OutputSymbols)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags,
+ __in ULONG Start,
+ __in ULONG Count
+ ) PURE;
+ STDMETHOD(WriteSymbol)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Value
+ ) PURE;
+ STDMETHOD(OutputAsType)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Type
+ ) PURE;
+};
+
+#define DEBUG_SYMENT_IS_CODE 0x00000001
+#define DEBUG_SYMENT_IS_DATA 0x00000002
+#define DEBUG_SYMENT_IS_PARAMETER 0x00000004
+#define DEBUG_SYMENT_IS_LOCAL 0x00000008
+#define DEBUG_SYMENT_IS_MANAGED 0x00000010
+#define DEBUG_SYMENT_IS_SYNTHETIC 0x00000020
+
+typedef struct _DEBUG_SYMBOL_ENTRY
+{
+ ULONG64 ModuleBase;
+ ULONG64 Offset;
+ ULONG64 Id;
+ ULONG64 Arg64;
+ ULONG Size;
+ ULONG Flags;
+ ULONG TypeId;
+ ULONG NameSize;
+ ULONG Token;
+ ULONG Tag;
+ ULONG Arg32;
+ ULONG Reserved;
+} DEBUG_SYMBOL_ENTRY, *PDEBUG_SYMBOL_ENTRY;
+
+#undef INTERFACE
+#define INTERFACE IDebugSymbolGroup2
+DECLARE_INTERFACE_(IDebugSymbolGroup2, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugSymbolGroup.
+
+ STDMETHOD(GetNumberSymbols)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ // On input Index indicates the desired insertion
+ // index. On output Index contains the actual index.
+ // Use DEBUG_ANY_ID to append a symbol to the end.
+ STDMETHOD(AddSymbol)(
+ THIS_
+ __in PCSTR Name,
+ __inout PULONG Index
+ ) PURE;
+ STDMETHOD(RemoveSymbolByName)(
+ THIS_
+ __in PCSTR Name
+ ) PURE;
+ STDMETHOD(RemoveSymbolByIndex)(
+ THIS_
+ __in ULONG Index
+ ) PURE;
+ STDMETHOD(GetSymbolName)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+ STDMETHOD(GetSymbolParameters)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PDEBUG_SYMBOL_PARAMETERS Params
+ ) PURE;
+ STDMETHOD(ExpandSymbol)(
+ THIS_
+ __in ULONG Index,
+ __in BOOL Expand
+ ) PURE;
+ // Uses the line prefix.
+ STDMETHOD(OutputSymbols)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags,
+ __in ULONG Start,
+ __in ULONG Count
+ ) PURE;
+ STDMETHOD(WriteSymbol)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Value
+ ) PURE;
+ STDMETHOD(OutputAsType)(
+ THIS_
+ __in ULONG Index,
+ __in PCSTR Type
+ ) PURE;
+
+ // IDebugSymbolGroup2.
+
+ STDMETHOD(AddSymbolWide)(
+ THIS_
+ __in PCWSTR Name,
+ __inout PULONG Index
+ ) PURE;
+ STDMETHOD(RemoveSymbolByNameWide)(
+ THIS_
+ __in PCWSTR Name
+ ) PURE;
+ STDMETHOD(GetSymbolNameWide)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+ STDMETHOD(WriteSymbolWide)(
+ THIS_
+ __in ULONG Index,
+ __in PCWSTR Value
+ ) PURE;
+ STDMETHOD(OutputAsTypeWide)(
+ THIS_
+ __in ULONG Index,
+ __in PCWSTR Type
+ ) PURE;
+
+ STDMETHOD(GetSymbolTypeName)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+ STDMETHOD(GetSymbolTypeNameWide)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+ STDMETHOD(GetSymbolSize)(
+ THIS_
+ __in ULONG Index,
+ __out PULONG Size
+ ) PURE;
+ // If the symbol has an absolute address
+ // this method will retrieve it.
+ STDMETHOD(GetSymbolOffset)(
+ THIS_
+ __in ULONG Index,
+ __out PULONG64 Offset
+ ) PURE;
+ // If the symbol is enregistered this
+ // method will return the register index.
+ STDMETHOD(GetSymbolRegister)(
+ THIS_
+ __in ULONG Index,
+ __out PULONG Register
+ ) PURE;
+ STDMETHOD(GetSymbolValueText)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+ STDMETHOD(GetSymbolValueTextWide)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+ STDMETHOD(GetSymbolEntryInformation)(
+ THIS_
+ __in ULONG Index,
+ __out PDEBUG_SYMBOL_ENTRY Entry
+ ) PURE;
+};
+
+//----------------------------------------------------------------------------
+//
+// IDebugSymbols.
+//
+//----------------------------------------------------------------------------
+
+//
+// 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;
+
+// Scope arguments are function arguments
+// and thus only change when the scope
+// crosses functions.
+#define DEBUG_SCOPE_GROUP_ARGUMENTS 0x00000001
+// Scope locals are locals declared in a particular
+// scope and are only defined within that scope.
+#define DEBUG_SCOPE_GROUP_LOCALS 0x00000002
+// All symbols in the scope.
+#define DEBUG_SCOPE_GROUP_ALL 0x00000003
+
+// Typed data output control flags.
+#define DEBUG_OUTTYPE_DEFAULT 0x00000000
+#define DEBUG_OUTTYPE_NO_INDENT 0x00000001
+#define DEBUG_OUTTYPE_NO_OFFSET 0x00000002
+#define DEBUG_OUTTYPE_VERBOSE 0x00000004
+#define DEBUG_OUTTYPE_COMPACT_OUTPUT 0x00000008
+#define DEBUG_OUTTYPE_RECURSION_LEVEL(Max) (((Max) & 0xf) << 4)
+#define DEBUG_OUTTYPE_ADDRESS_OF_FIELD 0x00010000
+#define DEBUG_OUTTYPE_ADDRESS_AT_END 0x00020000
+#define DEBUG_OUTTYPE_BLOCK_RECURSE 0x00200000
+
+// 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)
+
+#undef INTERFACE
+#define INTERFACE IDebugSymbols
+DECLARE_INTERFACE_(IDebugSymbols, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugSymbols.
+
+ // Controls the symbol options used during
+ // symbol operations.
+ // Uses the same flags as dbghelps SymSetOptions.
+ STDMETHOD(GetSymbolOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddSymbolOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveSymbolOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetSymbolOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ STDMETHOD(GetNameByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PULONG64 Displacement
+ ) PURE;
+ // A symbol name may not be unique, particularly
+ // when overloaded functions exist which all
+ // have the same name. If GetOffsetByName
+ // finds multiple matches for the name it
+ // can return any one of them. In that
+ // case it will return S_FALSE to indicate
+ // that ambiguity was arbitrarily resolved.
+ // A caller can then use SearchSymbols to
+ // find all of the matches if it wishes to
+ // perform different disambiguation.
+ STDMETHOD(GetOffsetByName)(
+ THIS_
+ __in PCSTR Symbol,
+ __out PULONG64 Offset
+ ) PURE;
+ // GetNearNameByOffset returns symbols
+ // located near the symbol closest to
+ // to the offset, such as the previous
+ // or next symbol. If Delta is zero it
+ // operates identically to GetNameByOffset.
+ // If Delta is nonzero and such a symbol
+ // does not exist an error is returned.
+ // The next symbol, if one exists, will
+ // always have a higher offset than the
+ // input offset so the displacement is
+ // always negative. The situation is
+ // reversed for the previous symbol.
+ STDMETHOD(GetNearNameByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __in LONG Delta,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PULONG64 Displacement
+ ) PURE;
+
+ STDMETHOD(GetLineByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_opt PULONG Line,
+ __out_ecount_opt(FileBufferSize) PSTR FileBuffer,
+ __in ULONG FileBufferSize,
+ __out_opt PULONG FileSize,
+ __out_opt PULONG64 Displacement
+ ) PURE;
+ STDMETHOD(GetOffsetByLine)(
+ THIS_
+ __in ULONG Line,
+ __in PCSTR File,
+ __out PULONG64 Offset
+ ) PURE;
+
+ // 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.
+ STDMETHOD(GetNumberModules)(
+ THIS_
+ __out PULONG Loaded,
+ __out PULONG Unloaded
+ ) PURE;
+ STDMETHOD(GetModuleByIndex)(
+ THIS_
+ __in ULONG Index,
+ __out PULONG64 Base
+ ) PURE;
+ // The module name may not be unique.
+ // This method returns the first match.
+ STDMETHOD(GetModuleByModuleName)(
+ THIS_
+ __in PCSTR Name,
+ __in ULONG StartIndex,
+ __out_opt PULONG Index,
+ __out_opt PULONG64 Base
+ ) PURE;
+ // Offset can be any offset within
+ // the module extent. Extents may
+ // not be unique when including unloaded
+ // drivers. This method returns the
+ // first match.
+ STDMETHOD(GetModuleByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG StartIndex,
+ __out_opt PULONG Index,
+ __out_opt PULONG64 Base
+ ) PURE;
+ // If Index is DEBUG_ANY_ID the base address
+ // is used to look up the module instead.
+ STDMETHOD(GetModuleNames)(
+ THIS_
+ __in ULONG Index,
+ __in ULONG64 Base,
+ __out_ecount_opt(ImageNameBufferSize) PSTR ImageNameBuffer,
+ __in ULONG ImageNameBufferSize,
+ __out_opt PULONG ImageNameSize,
+ __out_ecount_opt(ModuleNameBufferSize) PSTR ModuleNameBuffer,
+ __in ULONG ModuleNameBufferSize,
+ __out_opt PULONG ModuleNameSize,
+ __out_ecount_opt(LoadedImageNameBufferSize) PSTR LoadedImageNameBuffer,
+ __in ULONG LoadedImageNameBufferSize,
+ __out_opt PULONG LoadedImageNameSize
+ ) PURE;
+ STDMETHOD(GetModuleParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG64 Bases,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_MODULE_PARAMETERS Params
+ ) PURE;
+ // Looks up the module from a <Module>!<Symbol>
+ // string.
+ STDMETHOD(GetSymbolModule)(
+ THIS_
+ __in PCSTR Symbol,
+ __out PULONG64 Base
+ ) PURE;
+
+ // Returns the string name of a type.
+ STDMETHOD(GetTypeName)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+ // Returns the ID for a type name.
+ STDMETHOD(GetTypeId)(
+ THIS_
+ __in ULONG64 Module,
+ __in PCSTR Name,
+ __out PULONG TypeId
+ ) PURE;
+ STDMETHOD(GetTypeSize)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __out PULONG Size
+ ) PURE;
+ // Given a type which can contain members
+ // this method returns the offset of a
+ // particular member within the type.
+ // TypeId should give the container type ID
+ // and Field gives the dot-separated path
+ // to the field of interest.
+ STDMETHOD(GetFieldOffset)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in PCSTR Field,
+ __out PULONG Offset
+ ) PURE;
+
+ STDMETHOD(GetSymbolTypeId)(
+ THIS_
+ __in PCSTR Symbol,
+ __out PULONG TypeId,
+ __out_opt PULONG64 Module
+ ) PURE;
+ // As with GetOffsetByName a symbol's
+ // name may be ambiguous. GetOffsetTypeId
+ // returns the type for the symbol closest
+ // to the given offset and can be used
+ // to avoid ambiguity.
+ STDMETHOD(GetOffsetTypeId)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG TypeId,
+ __out_opt PULONG64 Module
+ ) PURE;
+
+ // Helpers for virtual and physical data
+ // which combine creation of a location with
+ // the actual operation.
+ STDMETHOD(ReadTypedDataVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteTypedDataVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(OutputTypedDataVirtual)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in ULONG Flags
+ ) PURE;
+ STDMETHOD(ReadTypedDataPhysical)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteTypedDataPhysical)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(OutputTypedDataPhysical)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in ULONG Flags
+ ) PURE;
+
+ // Function arguments and scope block symbols
+ // can be retrieved relative to currently
+ // executing code. A caller can provide just
+ // a code offset for scoping purposes and look
+ // up names or the caller can provide a full frame
+ // and look up actual values. The values for
+ // scoped symbols are best-guess and may or may not
+ // be accurate depending on program optimizations,
+ // the machine architecture, the current point
+ // in the programs execution and so on.
+ // A caller can also provide a complete register
+ // context for setting a scope to a previous
+ // machine state such as a context saved for
+ // an exception. Usually this isnt necessary
+ // and the current register context is used.
+ STDMETHOD(GetScope)(
+ THIS_
+ __out_opt PULONG64 InstructionOffset,
+ __out_opt PDEBUG_STACK_FRAME ScopeFrame,
+ __out_bcount_opt(ScopeContextSize) PVOID ScopeContext,
+ __in ULONG ScopeContextSize
+ ) PURE;
+ // If ScopeFrame or ScopeContext is non-NULL then
+ // InstructionOffset is ignored.
+ // If ScopeContext is NULL the current
+ // register context is used.
+ // If the scope identified by the given
+ // information is the same as before
+ // SetScope returns S_OK. If the scope
+ // information changes, such as when the
+ // scope moves between functions or scope
+ // blocks, SetScope returns S_FALSE.
+ STDMETHOD(SetScope)(
+ THIS_
+ __in ULONG64 InstructionOffset,
+ __in_opt PDEBUG_STACK_FRAME ScopeFrame,
+ __in_bcount_opt(ScopeContextSize) PVOID ScopeContext,
+ __in ULONG ScopeContextSize
+ ) PURE;
+ // ResetScope clears the scope information
+ // for situations where scoped symbols
+ // mask global symbols or when resetting
+ // from explicit information to the current
+ // information.
+ STDMETHOD(ResetScope)(
+ THIS
+ ) PURE;
+ // A scope symbol is tied to its particular
+ // scope and only is meaningful within the scope.
+ // The returned group can be updated by passing it back
+ // into the method for lower-cost
+ // incremental updates when stepping.
+ STDMETHOD(GetScopeSymbolGroup)(
+ THIS_
+ __in ULONG Flags,
+ __in_opt PDEBUG_SYMBOL_GROUP Update,
+ __out PDEBUG_SYMBOL_GROUP* Symbols
+ ) PURE;
+
+ // Create a new symbol group.
+ STDMETHOD(CreateSymbolGroup)(
+ THIS_
+ __out PDEBUG_SYMBOL_GROUP* Group
+ ) PURE;
+
+ // StartSymbolMatch matches symbol names
+ // against the given pattern using simple
+ // regular expressions. The search results
+ // are iterated through using GetNextSymbolMatch.
+ // When the caller is done examining results
+ // the match should be freed via EndSymbolMatch.
+ // If the match pattern contains a module name
+ // the search is restricted to a single module.
+ // Pattern matching is only done on symbol names,
+ // not module names.
+ // All active symbol match handles are invalidated
+ // when the set of loaded symbols changes.
+ STDMETHOD(StartSymbolMatch)(
+ THIS_
+ __in PCSTR Pattern,
+ __out PULONG64 Handle
+ ) PURE;
+ // If Buffer is NULL the match does not
+ // advance.
+ STDMETHOD(GetNextSymbolMatch)(
+ THIS_
+ __in ULONG64 Handle,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG MatchSize,
+ __out_opt PULONG64 Offset
+ ) PURE;
+ STDMETHOD(EndSymbolMatch)(
+ THIS_
+ __in ULONG64 Handle
+ ) PURE;
+
+ STDMETHOD(Reload)(
+ THIS_
+ __in PCSTR Module
+ ) PURE;
+
+ STDMETHOD(GetSymbolPath)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PathSize
+ ) PURE;
+ STDMETHOD(SetSymbolPath)(
+ THIS_
+ __in PCSTR Path
+ ) PURE;
+ STDMETHOD(AppendSymbolPath)(
+ THIS_
+ __in PCSTR Addition
+ ) PURE;
+
+ // Manipulate the path for executable images.
+ // Some dump files need to load executable images
+ // in order to resolve dump information. This
+ // path controls where the engine looks for
+ // images.
+ STDMETHOD(GetImagePath)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PathSize
+ ) PURE;
+ STDMETHOD(SetImagePath)(
+ THIS_
+ __in PCSTR Path
+ ) PURE;
+ STDMETHOD(AppendImagePath)(
+ THIS_
+ __in PCSTR Addition
+ ) PURE;
+
+ // Path routines for source file location
+ // methods.
+ STDMETHOD(GetSourcePath)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PathSize
+ ) PURE;
+ // Gets the nth part of the source path.
+ STDMETHOD(GetSourcePathElement)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ElementSize
+ ) PURE;
+ STDMETHOD(SetSourcePath)(
+ THIS_
+ __in PCSTR Path
+ ) PURE;
+ STDMETHOD(AppendSourcePath)(
+ THIS_
+ __in PCSTR Addition
+ ) PURE;
+ // 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.
+ STDMETHOD(FindSourceFile)(
+ THIS_
+ __in ULONG StartElement,
+ __in PCSTR File,
+ __in ULONG Flags,
+ __out_opt PULONG FoundElement,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG FoundSize
+ ) PURE;
+ // Retrieves all the line offset information
+ // for a particular source file. Buffer is
+ // first intialized to DEBUG_INVALID_OFFSET for
+ // every entry. Then for each piece of line
+ // symbol information Buffer[Line] set to
+ // Lines offset. This produces a per-line
+ // map of the offsets for the lines of the
+ // given file. Line numbers are decremented
+ // for the map so Buffer[0] contains the offset
+ // for line number 1.
+ // If there is no line information at all for
+ // the given file the method fails rather
+ // than returning a map of invalid offsets.
+ STDMETHOD(GetSourceFileLineOffsets)(
+ THIS_
+ __in PCSTR File,
+ __out_ecount_opt(BufferLines) PULONG64 Buffer,
+ __in ULONG BufferLines,
+ __out_opt PULONG FileLines
+ ) PURE;
+};
+
+//
+// GetModuleNameString strings.
+//
+
+#define DEBUG_MODNAME_IMAGE 0x00000000
+#define DEBUG_MODNAME_MODULE 0x00000001
+#define DEBUG_MODNAME_LOADED_IMAGE 0x00000002
+#define DEBUG_MODNAME_SYMBOL_FILE 0x00000003
+#define DEBUG_MODNAME_MAPPED_IMAGE 0x00000004
+
+//
+// Type options, used with Get/SetTypeOptions.
+//
+
+// Display PUSHORT and USHORT arrays in Unicode.
+#define DEBUG_TYPEOPTS_UNICODE_DISPLAY 0x00000001
+// Display LONG types in default base instead of decimal.
+#define DEBUG_TYPEOPTS_LONGSTATUS_DISPLAY 0x00000002
+// Display integer types in default base instead of decimal.
+#define DEBUG_TYPEOPTS_FORCERADIX_OUTPUT 0x00000004
+// Search for the type/symbol with largest size when
+// multiple type/symbol match for a given name
+#define DEBUG_TYPEOPTS_MATCH_MAXSIZE 0x00000008
+
+#undef INTERFACE
+#define INTERFACE IDebugSymbols2
+DECLARE_INTERFACE_(IDebugSymbols2, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugSymbols.
+
+ // Controls the symbol options used during
+ // symbol operations.
+ // Uses the same flags as dbghelps SymSetOptions.
+ STDMETHOD(GetSymbolOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddSymbolOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveSymbolOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetSymbolOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ STDMETHOD(GetNameByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PULONG64 Displacement
+ ) PURE;
+ // A symbol name may not be unique, particularly
+ // when overloaded functions exist which all
+ // have the same name. If GetOffsetByName
+ // finds multiple matches for the name it
+ // can return any one of them. In that
+ // case it will return S_FALSE to indicate
+ // that ambiguity was arbitrarily resolved.
+ // A caller can then use SearchSymbols to
+ // find all of the matches if it wishes to
+ // perform different disambiguation.
+ STDMETHOD(GetOffsetByName)(
+ THIS_
+ __in PCSTR Symbol,
+ __out PULONG64 Offset
+ ) PURE;
+ // GetNearNameByOffset returns symbols
+ // located near the symbol closest to
+ // to the offset, such as the previous
+ // or next symbol. If Delta is zero it
+ // operates identically to GetNameByOffset.
+ // If Delta is nonzero and such a symbol
+ // does not exist an error is returned.
+ // The next symbol, if one exists, will
+ // always have a higher offset than the
+ // input offset so the displacement is
+ // always negative. The situation is
+ // reversed for the previous symbol.
+ STDMETHOD(GetNearNameByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __in LONG Delta,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PULONG64 Displacement
+ ) PURE;
+
+ STDMETHOD(GetLineByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_opt PULONG Line,
+ __out_ecount_opt(FileBufferSize) PSTR FileBuffer,
+ __in ULONG FileBufferSize,
+ __out_opt PULONG FileSize,
+ __out_opt PULONG64 Displacement
+ ) PURE;
+ STDMETHOD(GetOffsetByLine)(
+ THIS_
+ __in ULONG Line,
+ __in PCSTR File,
+ __out PULONG64 Offset
+ ) PURE;
+
+ // 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.
+ STDMETHOD(GetNumberModules)(
+ THIS_
+ __out PULONG Loaded,
+ __out PULONG Unloaded
+ ) PURE;
+ STDMETHOD(GetModuleByIndex)(
+ THIS_
+ __in ULONG Index,
+ __out PULONG64 Base
+ ) PURE;
+ // The module name may not be unique.
+ // This method returns the first match.
+ STDMETHOD(GetModuleByModuleName)(
+ THIS_
+ __in PCSTR Name,
+ __in ULONG StartIndex,
+ __out_opt PULONG Index,
+ __out_opt PULONG64 Base
+ ) PURE;
+ // Offset can be any offset within
+ // the module extent. Extents may
+ // not be unique when including unloaded
+ // drivers. This method returns the
+ // first match.
+ STDMETHOD(GetModuleByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG StartIndex,
+ __out_opt PULONG Index,
+ __out_opt PULONG64 Base
+ ) PURE;
+ // If Index is DEBUG_ANY_ID the base address
+ // is used to look up the module instead.
+ STDMETHOD(GetModuleNames)(
+ THIS_
+ __in ULONG Index,
+ __in ULONG64 Base,
+ __out_ecount_opt(ImageNameBufferSize) PSTR ImageNameBuffer,
+ __in ULONG ImageNameBufferSize,
+ __out_opt PULONG ImageNameSize,
+ __out_ecount_opt(ModuleNameBufferSize) PSTR ModuleNameBuffer,
+ __in ULONG ModuleNameBufferSize,
+ __out_opt PULONG ModuleNameSize,
+ __out_ecount_opt(LoadedImageNameBufferSize) PSTR LoadedImageNameBuffer,
+ __in ULONG LoadedImageNameBufferSize,
+ __out_opt PULONG LoadedImageNameSize
+ ) PURE;
+ STDMETHOD(GetModuleParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG64 Bases,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_MODULE_PARAMETERS Params
+ ) PURE;
+ // Looks up the module from a <Module>!<Symbol>
+ // string.
+ STDMETHOD(GetSymbolModule)(
+ THIS_
+ __in PCSTR Symbol,
+ __out PULONG64 Base
+ ) PURE;
+
+ // Returns the string name of a type.
+ STDMETHOD(GetTypeName)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+ // Returns the ID for a type name.
+ STDMETHOD(GetTypeId)(
+ THIS_
+ __in ULONG64 Module,
+ __in PCSTR Name,
+ __out PULONG TypeId
+ ) PURE;
+ STDMETHOD(GetTypeSize)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __out PULONG Size
+ ) PURE;
+ // Given a type which can contain members
+ // this method returns the offset of a
+ // particular member within the type.
+ // TypeId should give the container type ID
+ // and Field gives the dot-separated path
+ // to the field of interest.
+ STDMETHOD(GetFieldOffset)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in PCSTR Field,
+ __out PULONG Offset
+ ) PURE;
+
+ STDMETHOD(GetSymbolTypeId)(
+ THIS_
+ __in PCSTR Symbol,
+ __out PULONG TypeId,
+ __out_opt PULONG64 Module
+ ) PURE;
+ // As with GetOffsetByName a symbol's
+ // name may be ambiguous. GetOffsetTypeId
+ // returns the type for the symbol closest
+ // to the given offset and can be used
+ // to avoid ambiguity.
+ STDMETHOD(GetOffsetTypeId)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG TypeId,
+ __out_opt PULONG64 Module
+ ) PURE;
+
+ // Helpers for virtual and physical data
+ // which combine creation of a location with
+ // the actual operation.
+ STDMETHOD(ReadTypedDataVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteTypedDataVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(OutputTypedDataVirtual)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in ULONG Flags
+ ) PURE;
+ STDMETHOD(ReadTypedDataPhysical)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteTypedDataPhysical)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(OutputTypedDataPhysical)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in ULONG Flags
+ ) PURE;
+
+ // Function arguments and scope block symbols
+ // can be retrieved relative to currently
+ // executing code. A caller can provide just
+ // a code offset for scoping purposes and look
+ // up names or the caller can provide a full frame
+ // and look up actual values. The values for
+ // scoped symbols are best-guess and may or may not
+ // be accurate depending on program optimizations,
+ // the machine architecture, the current point
+ // in the programs execution and so on.
+ // A caller can also provide a complete register
+ // context for setting a scope to a previous
+ // machine state such as a context saved for
+ // an exception. Usually this isnt necessary
+ // and the current register context is used.
+ STDMETHOD(GetScope)(
+ THIS_
+ __out_opt PULONG64 InstructionOffset,
+ __out_opt PDEBUG_STACK_FRAME ScopeFrame,
+ __out_bcount_opt(ScopeContextSize) PVOID ScopeContext,
+ __in ULONG ScopeContextSize
+ ) PURE;
+ // If ScopeFrame or ScopeContext is non-NULL then
+ // InstructionOffset is ignored.
+ // If ScopeContext is NULL the current
+ // register context is used.
+ // If the scope identified by the given
+ // information is the same as before
+ // SetScope returns S_OK. If the scope
+ // information changes, such as when the
+ // scope moves between functions or scope
+ // blocks, SetScope returns S_FALSE.
+ STDMETHOD(SetScope)(
+ THIS_
+ __in ULONG64 InstructionOffset,
+ __in_opt PDEBUG_STACK_FRAME ScopeFrame,
+ __in_bcount_opt(ScopeContextSize) PVOID ScopeContext,
+ __in ULONG ScopeContextSize
+ ) PURE;
+ // ResetScope clears the scope information
+ // for situations where scoped symbols
+ // mask global symbols or when resetting
+ // from explicit information to the current
+ // information.
+ STDMETHOD(ResetScope)(
+ THIS
+ ) PURE;
+ // A scope symbol is tied to its particular
+ // scope and only is meaningful within the scope.
+ // The returned group can be updated by passing it back
+ // into the method for lower-cost
+ // incremental updates when stepping.
+ STDMETHOD(GetScopeSymbolGroup)(
+ THIS_
+ __in ULONG Flags,
+ __in_opt PDEBUG_SYMBOL_GROUP Update,
+ __out PDEBUG_SYMBOL_GROUP* Symbols
+ ) PURE;
+
+ // Create a new symbol group.
+ STDMETHOD(CreateSymbolGroup)(
+ THIS_
+ __out PDEBUG_SYMBOL_GROUP* Group
+ ) PURE;
+
+ // StartSymbolMatch matches symbol names
+ // against the given pattern using simple
+ // regular expressions. The search results
+ // are iterated through using GetNextSymbolMatch.
+ // When the caller is done examining results
+ // the match should be freed via EndSymbolMatch.
+ // If the match pattern contains a module name
+ // the search is restricted to a single module.
+ // Pattern matching is only done on symbol names,
+ // not module names.
+ // All active symbol match handles are invalidated
+ // when the set of loaded symbols changes.
+ STDMETHOD(StartSymbolMatch)(
+ THIS_
+ __in PCSTR Pattern,
+ __out PULONG64 Handle
+ ) PURE;
+ // If Buffer is NULL the match does not
+ // advance.
+ STDMETHOD(GetNextSymbolMatch)(
+ THIS_
+ __in ULONG64 Handle,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG MatchSize,
+ __out_opt PULONG64 Offset
+ ) PURE;
+ STDMETHOD(EndSymbolMatch)(
+ THIS_
+ __in ULONG64 Handle
+ ) PURE;
+
+ STDMETHOD(Reload)(
+ THIS_
+ __in PCSTR Module
+ ) PURE;
+
+ STDMETHOD(GetSymbolPath)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PathSize
+ ) PURE;
+ STDMETHOD(SetSymbolPath)(
+ THIS_
+ __in PCSTR Path
+ ) PURE;
+ STDMETHOD(AppendSymbolPath)(
+ THIS_
+ __in PCSTR Addition
+ ) PURE;
+
+ // Manipulate the path for executable images.
+ // Some dump files need to load executable images
+ // in order to resolve dump information. This
+ // path controls where the engine looks for
+ // images.
+ STDMETHOD(GetImagePath)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PathSize
+ ) PURE;
+ STDMETHOD(SetImagePath)(
+ THIS_
+ __in PCSTR Path
+ ) PURE;
+ STDMETHOD(AppendImagePath)(
+ THIS_
+ __in PCSTR Addition
+ ) PURE;
+
+ // Path routines for source file location
+ // methods.
+ STDMETHOD(GetSourcePath)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PathSize
+ ) PURE;
+ // Gets the nth part of the source path.
+ STDMETHOD(GetSourcePathElement)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ElementSize
+ ) PURE;
+ STDMETHOD(SetSourcePath)(
+ THIS_
+ __in PCSTR Path
+ ) PURE;
+ STDMETHOD(AppendSourcePath)(
+ THIS_
+ __in PCSTR Addition
+ ) PURE;
+ // 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.
+ STDMETHOD(FindSourceFile)(
+ THIS_
+ __in ULONG StartElement,
+ __in PCSTR File,
+ __in ULONG Flags,
+ __out_opt PULONG FoundElement,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG FoundSize
+ ) PURE;
+ // Retrieves all the line offset information
+ // for a particular source file. Buffer is
+ // first intialized to DEBUG_INVALID_OFFSET for
+ // every entry. Then for each piece of line
+ // symbol information Buffer[Line] set to
+ // Lines offset. This produces a per-line
+ // map of the offsets for the lines of the
+ // given file. Line numbers are decremented
+ // for the map so Buffer[0] contains the offset
+ // for line number 1.
+ // If there is no line information at all for
+ // the given file the method fails rather
+ // than returning a map of invalid offsets.
+ STDMETHOD(GetSourceFileLineOffsets)(
+ THIS_
+ __in PCSTR File,
+ __out_ecount_opt(BufferLines) PULONG64 Buffer,
+ __in ULONG BufferLines,
+ __out_opt PULONG FileLines
+ ) PURE;
+
+ // IDebugSymbols2.
+
+ // If Index is DEBUG_ANY_ID the base address
+ // is used to look up the module instead.
+ // Item is specified as in VerQueryValue.
+ // Module version information is only
+ // available for loaded modules and may
+ // not be available in all debug sessions.
+ STDMETHOD(GetModuleVersionInformation)(
+ THIS_
+ __in ULONG Index,
+ __in ULONG64 Base,
+ __in PCSTR Item,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG VerInfoSize
+ ) PURE;
+ // Retrieves any available module name string
+ // such as module name or symbol file name.
+ // If Index is DEBUG_ANY_ID the base address
+ // is used to look up the module instead.
+ // If symbols are deferred an error will
+ // be returned.
+ // E_NOINTERFACE may be returned, indicating
+ // no information exists.
+ STDMETHOD(GetModuleNameString)(
+ THIS_
+ __in ULONG Which,
+ __in ULONG Index,
+ __in ULONG64 Base,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+
+ // Returns the string name of a constant type.
+ STDMETHOD(GetConstantName)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in ULONG64 Value,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+
+ // Gets name of a field in a struct
+ // FieldNumber is 0 based index of field in a struct
+ STDMETHOD(GetFieldName)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in ULONG FieldIndex,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+
+ // Control options for typed values.
+ STDMETHOD(GetTypeOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddTypeOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveTypeOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetTypeOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+};
+
+//
+// GetModuleBy* flags.
+//
+
+// Scan all modules, loaded and unloaded.
+#define DEBUG_GETMOD_DEFAULT 0x00000000
+// Do not scan loaded modules.
+#define DEBUG_GETMOD_NO_LOADED_MODULES 0x00000001
+// Do not scan unloaded modules.
+#define DEBUG_GETMOD_NO_UNLOADED_MODULES 0x00000002
+
+//
+// AddSyntheticModule flags.
+//
+
+#define DEBUG_ADDSYNTHMOD_DEFAULT 0x00000000
+
+//
+// AddSyntheticSymbol flags.
+//
+
+#define DEBUG_ADDSYNTHSYM_DEFAULT 0x00000000
+
+//
+// OutputSymbolByOffset flags.
+//
+
+// Use the current debugger settings for symbol output.
+#define DEBUG_OUTSYM_DEFAULT 0x00000000
+// Always display the offset in addition to any symbol hit.
+#define DEBUG_OUTSYM_FORCE_OFFSET 0x00000001
+// Display source line information if found.
+#define DEBUG_OUTSYM_SOURCE_LINE 0x00000002
+// Output symbol hits that don't exactly match.
+#define DEBUG_OUTSYM_ALLOW_DISPLACEMENT 0x00000004
+
+//
+// GetFunctionEntryByOffset flags.
+//
+
+#define DEBUG_GETFNENT_DEFAULT 0x00000000
+// The engine provides artificial entries for well-known
+// cases. This flag limits the entry search to only
+// the raw entries and disables artificial entry lookup.
+#define DEBUG_GETFNENT_RAW_ENTRY_ONLY 0x00000001
+
+typedef struct _DEBUG_MODULE_AND_ID
+{
+ ULONG64 ModuleBase;
+ ULONG64 Id;
+} DEBUG_MODULE_AND_ID, *PDEBUG_MODULE_AND_ID;
+
+#define DEBUG_SOURCE_IS_STATEMENT 0x00000001
+
+//
+// GetSourceEntriesByLine flags.
+//
+
+#define DEBUG_GSEL_DEFAULT 0x00000000
+// Do not allow any extra symbols to load during the search.
+#define DEBUG_GSEL_NO_SYMBOL_LOADS 0x00000001
+// Allow source hits with lower line numbers.
+#define DEBUG_GSEL_ALLOW_LOWER 0x00000002
+// Allow source hits with higher line numbers.
+#define DEBUG_GSEL_ALLOW_HIGHER 0x00000004
+// Only return the nearest hits.
+#define DEBUG_GSEL_NEAREST_ONLY 0x00000008
+
+typedef struct _DEBUG_SYMBOL_SOURCE_ENTRY
+{
+ ULONG64 ModuleBase;
+ ULONG64 Offset;
+ ULONG64 FileNameId;
+ ULONG64 EngineInternal;
+ ULONG Size;
+ ULONG Flags;
+ ULONG FileNameSize;
+ // Line numbers are one-based.
+ // May be DEBUG_ANY_ID if unknown.
+ ULONG StartLine;
+ ULONG EndLine;
+ // Column numbers are one-based byte indices.
+ // May be DEBUG_ANY_ID if unknown.
+ ULONG StartColumn;
+ ULONG EndColumn;
+ ULONG Reserved;
+} DEBUG_SYMBOL_SOURCE_ENTRY, *PDEBUG_SYMBOL_SOURCE_ENTRY;
+
+#undef INTERFACE
+#define INTERFACE IDebugSymbols3
+DECLARE_INTERFACE_(IDebugSymbols3, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugSymbols.
+
+ // Controls the symbol options used during
+ // symbol operations.
+ // Uses the same flags as dbghelps SymSetOptions.
+ STDMETHOD(GetSymbolOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddSymbolOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveSymbolOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetSymbolOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ STDMETHOD(GetNameByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PULONG64 Displacement
+ ) PURE;
+ // A symbol name may not be unique, particularly
+ // when overloaded functions exist which all
+ // have the same name. If GetOffsetByName
+ // finds multiple matches for the name it
+ // can return any one of them. In that
+ // case it will return S_FALSE to indicate
+ // that ambiguity was arbitrarily resolved.
+ // A caller can then use SearchSymbols to
+ // find all of the matches if it wishes to
+ // perform different disambiguation.
+ STDMETHOD(GetOffsetByName)(
+ THIS_
+ __in PCSTR Symbol,
+ __out PULONG64 Offset
+ ) PURE;
+ // GetNearNameByOffset returns symbols
+ // located near the symbol closest to
+ // to the offset, such as the previous
+ // or next symbol. If Delta is zero it
+ // operates identically to GetNameByOffset.
+ // If Delta is nonzero and such a symbol
+ // does not exist an error is returned.
+ // The next symbol, if one exists, will
+ // always have a higher offset than the
+ // input offset so the displacement is
+ // always negative. The situation is
+ // reversed for the previous symbol.
+ STDMETHOD(GetNearNameByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __in LONG Delta,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PULONG64 Displacement
+ ) PURE;
+
+ STDMETHOD(GetLineByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_opt PULONG Line,
+ __out_ecount_opt(FileBufferSize) PSTR FileBuffer,
+ __in ULONG FileBufferSize,
+ __out_opt PULONG FileSize,
+ __out_opt PULONG64 Displacement
+ ) PURE;
+ STDMETHOD(GetOffsetByLine)(
+ THIS_
+ __in ULONG Line,
+ __in PCSTR File,
+ __out PULONG64 Offset
+ ) PURE;
+
+ // 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.
+ STDMETHOD(GetNumberModules)(
+ THIS_
+ __out PULONG Loaded,
+ __out PULONG Unloaded
+ ) PURE;
+ STDMETHOD(GetModuleByIndex)(
+ THIS_
+ __in ULONG Index,
+ __out PULONG64 Base
+ ) PURE;
+ // The module name may not be unique.
+ // This method returns the first match.
+ STDMETHOD(GetModuleByModuleName)(
+ THIS_
+ __in PCSTR Name,
+ __in ULONG StartIndex,
+ __out_opt PULONG Index,
+ __out_opt PULONG64 Base
+ ) PURE;
+ // Offset can be any offset within
+ // the module extent. Extents may
+ // not be unique when including unloaded
+ // drivers. This method returns the
+ // first match.
+ STDMETHOD(GetModuleByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG StartIndex,
+ __out_opt PULONG Index,
+ __out_opt PULONG64 Base
+ ) PURE;
+ // If Index is DEBUG_ANY_ID the base address
+ // is used to look up the module instead.
+ STDMETHOD(GetModuleNames)(
+ THIS_
+ __in ULONG Index,
+ __in ULONG64 Base,
+ __out_ecount_opt(ImageNameBufferSize) PSTR ImageNameBuffer,
+ __in ULONG ImageNameBufferSize,
+ __out_opt PULONG ImageNameSize,
+ __out_ecount_opt(ModuleNameBufferSize) PSTR ModuleNameBuffer,
+ __in ULONG ModuleNameBufferSize,
+ __out_opt PULONG ModuleNameSize,
+ __out_ecount_opt(LoadedImageNameBufferSize) PSTR LoadedImageNameBuffer,
+ __in ULONG LoadedImageNameBufferSize,
+ __out_opt PULONG LoadedImageNameSize
+ ) PURE;
+ STDMETHOD(GetModuleParameters)(
+ THIS_
+ __in ULONG Count,
+ __in_ecount_opt(Count) PULONG64 Bases,
+ __in ULONG Start,
+ __out_ecount(Count) PDEBUG_MODULE_PARAMETERS Params
+ ) PURE;
+ // Looks up the module from a <Module>!<Symbol>
+ // string.
+ STDMETHOD(GetSymbolModule)(
+ THIS_
+ __in PCSTR Symbol,
+ __out PULONG64 Base
+ ) PURE;
+
+ // Returns the string name of a type.
+ STDMETHOD(GetTypeName)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+ // Returns the ID for a type name.
+ STDMETHOD(GetTypeId)(
+ THIS_
+ __in ULONG64 Module,
+ __in PCSTR Name,
+ __out PULONG TypeId
+ ) PURE;
+ STDMETHOD(GetTypeSize)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __out PULONG Size
+ ) PURE;
+ // Given a type which can contain members
+ // this method returns the offset of a
+ // particular member within the type.
+ // TypeId should give the container type ID
+ // and Field gives the dot-separated path
+ // to the field of interest.
+ STDMETHOD(GetFieldOffset)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in PCSTR Field,
+ __out PULONG Offset
+ ) PURE;
+
+ STDMETHOD(GetSymbolTypeId)(
+ THIS_
+ __in PCSTR Symbol,
+ __out PULONG TypeId,
+ __out_opt PULONG64 Module
+ ) PURE;
+ // As with GetOffsetByName a symbol's
+ // name may be ambiguous. GetOffsetTypeId
+ // returns the type for the symbol closest
+ // to the given offset and can be used
+ // to avoid ambiguity.
+ STDMETHOD(GetOffsetTypeId)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG TypeId,
+ __out_opt PULONG64 Module
+ ) PURE;
+
+ // Helpers for virtual and physical data
+ // which combine creation of a location with
+ // the actual operation.
+ STDMETHOD(ReadTypedDataVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteTypedDataVirtual)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(OutputTypedDataVirtual)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in ULONG Flags
+ ) PURE;
+ STDMETHOD(ReadTypedDataPhysical)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __out_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesRead
+ ) PURE;
+ STDMETHOD(WriteTypedDataPhysical)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in_bcount(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BytesWritten
+ ) PURE;
+ STDMETHOD(OutputTypedDataPhysical)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG64 Offset,
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in ULONG Flags
+ ) PURE;
+
+ // Function arguments and scope block symbols
+ // can be retrieved relative to currently
+ // executing code. A caller can provide just
+ // a code offset for scoping purposes and look
+ // up names or the caller can provide a full frame
+ // and look up actual values. The values for
+ // scoped symbols are best-guess and may or may not
+ // be accurate depending on program optimizations,
+ // the machine architecture, the current point
+ // in the programs execution and so on.
+ // A caller can also provide a complete register
+ // context for setting a scope to a previous
+ // machine state such as a context saved for
+ // an exception. Usually this isnt necessary
+ // and the current register context is used.
+ STDMETHOD(GetScope)(
+ THIS_
+ __out_opt PULONG64 InstructionOffset,
+ __out_opt PDEBUG_STACK_FRAME ScopeFrame,
+ __out_bcount_opt(ScopeContextSize) PVOID ScopeContext,
+ __in ULONG ScopeContextSize
+ ) PURE;
+ // If ScopeFrame or ScopeContext is non-NULL then
+ // InstructionOffset is ignored.
+ // If ScopeContext is NULL the current
+ // register context is used.
+ // If the scope identified by the given
+ // information is the same as before
+ // SetScope returns S_OK. If the scope
+ // information changes, such as when the
+ // scope moves between functions or scope
+ // blocks, SetScope returns S_FALSE.
+ STDMETHOD(SetScope)(
+ THIS_
+ __in ULONG64 InstructionOffset,
+ __in_opt PDEBUG_STACK_FRAME ScopeFrame,
+ __in_bcount_opt(ScopeContextSize) PVOID ScopeContext,
+ __in ULONG ScopeContextSize
+ ) PURE;
+ // ResetScope clears the scope information
+ // for situations where scoped symbols
+ // mask global symbols or when resetting
+ // from explicit information to the current
+ // information.
+ STDMETHOD(ResetScope)(
+ THIS
+ ) PURE;
+ // A scope symbol is tied to its particular
+ // scope and only is meaningful within the scope.
+ // The returned group can be updated by passing it back
+ // into the method for lower-cost
+ // incremental updates when stepping.
+ STDMETHOD(GetScopeSymbolGroup)(
+ THIS_
+ __in ULONG Flags,
+ __in_opt PDEBUG_SYMBOL_GROUP Update,
+ __out PDEBUG_SYMBOL_GROUP* Symbols
+ ) PURE;
+
+ // Create a new symbol group.
+ STDMETHOD(CreateSymbolGroup)(
+ THIS_
+ __out PDEBUG_SYMBOL_GROUP* Group
+ ) PURE;
+
+ // StartSymbolMatch matches symbol names
+ // against the given pattern using simple
+ // regular expressions. The search results
+ // are iterated through using GetNextSymbolMatch.
+ // When the caller is done examining results
+ // the match should be freed via EndSymbolMatch.
+ // If the match pattern contains a module name
+ // the search is restricted to a single module.
+ // Pattern matching is only done on symbol names,
+ // not module names.
+ // All active symbol match handles are invalidated
+ // when the set of loaded symbols changes.
+ STDMETHOD(StartSymbolMatch)(
+ THIS_
+ __in PCSTR Pattern,
+ __out PULONG64 Handle
+ ) PURE;
+ // If Buffer is NULL the match does not
+ // advance.
+ STDMETHOD(GetNextSymbolMatch)(
+ THIS_
+ __in ULONG64 Handle,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG MatchSize,
+ __out_opt PULONG64 Offset
+ ) PURE;
+ STDMETHOD(EndSymbolMatch)(
+ THIS_
+ __in ULONG64 Handle
+ ) PURE;
+
+ STDMETHOD(Reload)(
+ THIS_
+ __in PCSTR Module
+ ) PURE;
+
+ STDMETHOD(GetSymbolPath)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PathSize
+ ) PURE;
+ STDMETHOD(SetSymbolPath)(
+ THIS_
+ __in PCSTR Path
+ ) PURE;
+ STDMETHOD(AppendSymbolPath)(
+ THIS_
+ __in PCSTR Addition
+ ) PURE;
+
+ // Manipulate the path for executable images.
+ // Some dump files need to load executable images
+ // in order to resolve dump information. This
+ // path controls where the engine looks for
+ // images.
+ STDMETHOD(GetImagePath)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PathSize
+ ) PURE;
+ STDMETHOD(SetImagePath)(
+ THIS_
+ __in PCSTR Path
+ ) PURE;
+ STDMETHOD(AppendImagePath)(
+ THIS_
+ __in PCSTR Addition
+ ) PURE;
+
+ // Path routines for source file location
+ // methods.
+ STDMETHOD(GetSourcePath)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PathSize
+ ) PURE;
+ // Gets the nth part of the source path.
+ STDMETHOD(GetSourcePathElement)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ElementSize
+ ) PURE;
+ STDMETHOD(SetSourcePath)(
+ THIS_
+ __in PCSTR Path
+ ) PURE;
+ STDMETHOD(AppendSourcePath)(
+ THIS_
+ __in PCSTR Addition
+ ) PURE;
+ // 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.
+ STDMETHOD(FindSourceFile)(
+ THIS_
+ __in ULONG StartElement,
+ __in PCSTR File,
+ __in ULONG Flags,
+ __out_opt PULONG FoundElement,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG FoundSize
+ ) PURE;
+ // Retrieves all the line offset information
+ // for a particular source file. Buffer is
+ // first intialized to DEBUG_INVALID_OFFSET for
+ // every entry. Then for each piece of line
+ // symbol information Buffer[Line] set to
+ // Lines offset. This produces a per-line
+ // map of the offsets for the lines of the
+ // given file. Line numbers are decremented
+ // for the map so Buffer[0] contains the offset
+ // for line number 1.
+ // If there is no line information at all for
+ // the given file the method fails rather
+ // than returning a map of invalid offsets.
+ STDMETHOD(GetSourceFileLineOffsets)(
+ THIS_
+ __in PCSTR File,
+ __out_ecount_opt(BufferLines) PULONG64 Buffer,
+ __in ULONG BufferLines,
+ __out_opt PULONG FileLines
+ ) PURE;
+
+ // IDebugSymbols2.
+
+ // If Index is DEBUG_ANY_ID the base address
+ // is used to look up the module instead.
+ // Item is specified as in VerQueryValue.
+ // Module version information is only
+ // available for loaded modules and may
+ // not be available in all debug sessions.
+ STDMETHOD(GetModuleVersionInformation)(
+ THIS_
+ __in ULONG Index,
+ __in ULONG64 Base,
+ __in PCSTR Item,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG VerInfoSize
+ ) PURE;
+ // Retrieves any available module name string
+ // such as module name or symbol file name.
+ // If Index is DEBUG_ANY_ID the base address
+ // is used to look up the module instead.
+ // If symbols are deferred an error will
+ // be returned.
+ // E_NOINTERFACE may be returned, indicating
+ // no information exists.
+ STDMETHOD(GetModuleNameString)(
+ THIS_
+ __in ULONG Which,
+ __in ULONG Index,
+ __in ULONG64 Base,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+
+ // Returns the string name of a constant type.
+ STDMETHOD(GetConstantName)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in ULONG64 Value,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+
+ // Gets name of a field in a struct
+ // FieldNumber is 0 based index of field in a struct
+ STDMETHOD(GetFieldName)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in ULONG FieldIndex,
+ __out_ecount_opt(NameBufferSize) PSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+
+ // Control options for typed values.
+ STDMETHOD(GetTypeOptions)(
+ THIS_
+ __out PULONG Options
+ ) PURE;
+ STDMETHOD(AddTypeOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(RemoveTypeOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+ STDMETHOD(SetTypeOptions)(
+ THIS_
+ __in ULONG Options
+ ) PURE;
+
+ // IDebugSymbols3.
+
+ STDMETHOD(GetNameByOffsetWide)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_ecount_opt(NameBufferSize) PWSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PULONG64 Displacement
+ ) PURE;
+ STDMETHOD(GetOffsetByNameWide)(
+ THIS_
+ __in PCWSTR Symbol,
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(GetNearNameByOffsetWide)(
+ THIS_
+ __in ULONG64 Offset,
+ __in LONG Delta,
+ __out_ecount_opt(NameBufferSize) PWSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize,
+ __out_opt PULONG64 Displacement
+ ) PURE;
+
+ STDMETHOD(GetLineByOffsetWide)(
+ THIS_
+ __in ULONG64 Offset,
+ __out_opt PULONG Line,
+ __out_ecount_opt(FileBufferSize) PWSTR FileBuffer,
+ __in ULONG FileBufferSize,
+ __out_opt PULONG FileSize,
+ __out_opt PULONG64 Displacement
+ ) PURE;
+ STDMETHOD(GetOffsetByLineWide)(
+ THIS_
+ __in ULONG Line,
+ __in PCWSTR File,
+ __out PULONG64 Offset
+ ) PURE;
+
+ STDMETHOD(GetModuleByModuleNameWide)(
+ THIS_
+ __in PCWSTR Name,
+ __in ULONG StartIndex,
+ __out_opt PULONG Index,
+ __out_opt PULONG64 Base
+ ) PURE;
+ STDMETHOD(GetSymbolModuleWide)(
+ THIS_
+ __in PCWSTR Symbol,
+ __out PULONG64 Base
+ ) PURE;
+
+ STDMETHOD(GetTypeNameWide)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __out_ecount_opt(NameBufferSize) PWSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+ // Returns the ID for a type name.
+ STDMETHOD(GetTypeIdWide)(
+ THIS_
+ __in ULONG64 Module,
+ __in PCWSTR Name,
+ __out PULONG TypeId
+ ) PURE;
+ STDMETHOD(GetFieldOffsetWide)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in PCWSTR Field,
+ __out PULONG Offset
+ ) PURE;
+
+ STDMETHOD(GetSymbolTypeIdWide)(
+ THIS_
+ __in PCWSTR Symbol,
+ __out PULONG TypeId,
+ __out_opt PULONG64 Module
+ ) PURE;
+
+ STDMETHOD(GetScopeSymbolGroup2)(
+ THIS_
+ __in ULONG Flags,
+ __in_opt PDEBUG_SYMBOL_GROUP2 Update,
+ __out PDEBUG_SYMBOL_GROUP2* Symbols
+ ) PURE;
+
+ STDMETHOD(CreateSymbolGroup2)(
+ THIS_
+ __out PDEBUG_SYMBOL_GROUP2* Group
+ ) PURE;
+
+ STDMETHOD(StartSymbolMatchWide)(
+ THIS_
+ __in PCWSTR Pattern,
+ __out PULONG64 Handle
+ ) PURE;
+ STDMETHOD(GetNextSymbolMatchWide)(
+ THIS_
+ __in ULONG64 Handle,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG MatchSize,
+ __out_opt PULONG64 Offset
+ ) PURE;
+
+ STDMETHOD(ReloadWide)(
+ THIS_
+ __in PCWSTR Module
+ ) PURE;
+
+ STDMETHOD(GetSymbolPathWide)(
+ THIS_
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PathSize
+ ) PURE;
+ STDMETHOD(SetSymbolPathWide)(
+ THIS_
+ __in PCWSTR Path
+ ) PURE;
+ STDMETHOD(AppendSymbolPathWide)(
+ THIS_
+ __in PCWSTR Addition
+ ) PURE;
+
+ STDMETHOD(GetImagePathWide)(
+ THIS_
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PathSize
+ ) PURE;
+ STDMETHOD(SetImagePathWide)(
+ THIS_
+ __in PCWSTR Path
+ ) PURE;
+ STDMETHOD(AppendImagePathWide)(
+ THIS_
+ __in PCWSTR Addition
+ ) PURE;
+
+ STDMETHOD(GetSourcePathWide)(
+ THIS_
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG PathSize
+ ) PURE;
+ STDMETHOD(GetSourcePathElementWide)(
+ THIS_
+ __in ULONG Index,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ElementSize
+ ) PURE;
+ STDMETHOD(SetSourcePathWide)(
+ THIS_
+ __in PCWSTR Path
+ ) PURE;
+ STDMETHOD(AppendSourcePathWide)(
+ THIS_
+ __in PCWSTR Addition
+ ) PURE;
+ STDMETHOD(FindSourceFileWide)(
+ THIS_
+ __in ULONG StartElement,
+ __in PCWSTR File,
+ __in ULONG Flags,
+ __out_opt PULONG FoundElement,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG FoundSize
+ ) PURE;
+ STDMETHOD(GetSourceFileLineOffsetsWide)(
+ THIS_
+ __in PCWSTR File,
+ __out_ecount_opt(BufferLines) PULONG64 Buffer,
+ __in ULONG BufferLines,
+ __out_opt PULONG FileLines
+ ) PURE;
+
+ STDMETHOD(GetModuleVersionInformationWide)(
+ THIS_
+ __in ULONG Index,
+ __in ULONG64 Base,
+ __in PCWSTR Item,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG VerInfoSize
+ ) PURE;
+ STDMETHOD(GetModuleNameStringWide)(
+ THIS_
+ __in ULONG Which,
+ __in ULONG Index,
+ __in ULONG64 Base,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+
+ STDMETHOD(GetConstantNameWide)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in ULONG64 Value,
+ __out_ecount_opt(NameBufferSize) PWSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+
+ STDMETHOD(GetFieldNameWide)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG TypeId,
+ __in ULONG FieldIndex,
+ __out_ecount_opt(NameBufferSize) PWSTR NameBuffer,
+ __in ULONG NameBufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+
+ // Returns S_OK if the engine is using managed
+ // debugging support when retriving information
+ // for the given module. This can be expensive
+ // to check.
+ STDMETHOD(IsManagedModule)(
+ THIS_
+ __in ULONG Index,
+ __in ULONG64 Base
+ ) PURE;
+
+ // The module name may not be unique.
+ // This method returns the first match.
+ STDMETHOD(GetModuleByModuleName2)(
+ THIS_
+ __in PCSTR Name,
+ __in ULONG StartIndex,
+ __in ULONG Flags,
+ __out_opt PULONG Index,
+ __out_opt PULONG64 Base
+ ) PURE;
+ STDMETHOD(GetModuleByModuleName2Wide)(
+ THIS_
+ __in PCWSTR Name,
+ __in ULONG StartIndex,
+ __in ULONG Flags,
+ __out_opt PULONG Index,
+ __out_opt PULONG64 Base
+ ) PURE;
+ // Offset can be any offset within
+ // the module extent. Extents may
+ // not be unique when including unloaded
+ // drivers. This method returns the
+ // first match.
+ STDMETHOD(GetModuleByOffset2)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG StartIndex,
+ __in ULONG Flags,
+ __out_opt PULONG Index,
+ __out_opt PULONG64 Base
+ ) PURE;
+
+ // A caller can create artificial loaded modules in
+ // the engine's module list if desired.
+ // These modules only serve as names for
+ // a region of addresses. They cannot have
+ // real symbols loaded for them; if that
+ // is desired Reload can be used with explicit
+ // parameters to create a true module entry.
+ // The region must not be in use by any other
+ // module.
+ // A general reload will discard any synthetic modules.
+ STDMETHOD(AddSyntheticModule)(
+ THIS_
+ __in ULONG64 Base,
+ __in ULONG Size,
+ __in PCSTR ImagePath,
+ __in PCSTR ModuleName,
+ __in ULONG Flags
+ ) PURE;
+ STDMETHOD(AddSyntheticModuleWide)(
+ THIS_
+ __in ULONG64 Base,
+ __in ULONG Size,
+ __in PCWSTR ImagePath,
+ __in PCWSTR ModuleName,
+ __in ULONG Flags
+ ) PURE;
+ STDMETHOD(RemoveSyntheticModule)(
+ THIS_
+ __in ULONG64 Base
+ ) PURE;
+
+ // Modify the current frame used for scoping.
+ // This is equivalent to the '.frame' command.
+ STDMETHOD(GetCurrentScopeFrameIndex)(
+ THIS_
+ __out PULONG Index
+ ) PURE;
+ STDMETHOD(SetScopeFrameByIndex)(
+ THIS_
+ __in ULONG Index
+ ) PURE;
+
+ // Recovers JIT_DEBUG_INFO information at the given
+ // address from the debuggee and sets current
+ // debugger scope context from it.
+ // Equivalent to '.jdinfo' command.
+ STDMETHOD(SetScopeFromJitDebugInfo)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG64 InfoOffset
+ ) PURE;
+
+ // Switches the current debugger scope to
+ // the stored event information.
+ // Equivalent to the '.ecxr' command.
+ STDMETHOD(SetScopeFromStoredEvent)(
+ THIS
+ ) PURE;
+
+ // Takes the first symbol hit and outputs it.
+ // Controlled with DEBUG_OUTSYM_* flags.
+ STDMETHOD(OutputSymbolByOffset)(
+ THIS_
+ __in ULONG OutputControl,
+ __in ULONG Flags,
+ __in ULONG64 Offset
+ ) PURE;
+
+ // Function entry information for a particular
+ // piece of code can be retrieved by this method.
+ // The actual data returned is system-dependent.
+ STDMETHOD(GetFunctionEntryByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out_bcount_opt(BufferSize) PVOID Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG BufferNeeded
+ ) PURE;
+
+ // Given a type which can contain members
+ // this method returns the type ID and offset of a
+ // particular member within the type.
+ // Field gives the dot-separated path
+ // to the field of interest.
+ STDMETHOD(GetFieldTypeAndOffset)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG ContainerTypeId,
+ __in PCSTR Field,
+ __out_opt PULONG FieldTypeId,
+ __out_opt PULONG Offset
+ ) PURE;
+ STDMETHOD(GetFieldTypeAndOffsetWide)(
+ THIS_
+ __in ULONG64 Module,
+ __in ULONG ContainerTypeId,
+ __in PCWSTR Field,
+ __out_opt PULONG FieldTypeId,
+ __out_opt PULONG Offset
+ ) PURE;
+
+ // Artificial symbols can be created in any
+ // existing module as a way to name an address.
+ // The address must not already have symbol
+ // information.
+ // A reload will discard synthetic symbols
+ // for all address regions reloaded.
+ STDMETHOD(AddSyntheticSymbol)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG Size,
+ __in PCSTR Name,
+ __in ULONG Flags,
+ __out_opt PDEBUG_MODULE_AND_ID Id
+ ) PURE;
+ STDMETHOD(AddSyntheticSymbolWide)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG Size,
+ __in PCWSTR Name,
+ __in ULONG Flags,
+ __out_opt PDEBUG_MODULE_AND_ID Id
+ ) PURE;
+ STDMETHOD(RemoveSyntheticSymbol)(
+ THIS_
+ __in PDEBUG_MODULE_AND_ID Id
+ ) PURE;
+
+ // The following methods can return multiple
+ // hits for symbol lookups to allow for all
+ // possible hits to be returned.
+ STDMETHOD(GetSymbolEntriesByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out_ecount_opt(IdsCount) PDEBUG_MODULE_AND_ID Ids,
+ __out_ecount_opt(IdsCount) PULONG64 Displacements,
+ __in ULONG IdsCount,
+ __out_opt PULONG Entries
+ ) PURE;
+ STDMETHOD(GetSymbolEntriesByName)(
+ THIS_
+ __in PCSTR Symbol,
+ __in ULONG Flags,
+ __out_ecount_opt(IdsCount) PDEBUG_MODULE_AND_ID Ids,
+ __in ULONG IdsCount,
+ __out_opt PULONG Entries
+ ) PURE;
+ STDMETHOD(GetSymbolEntriesByNameWide)(
+ THIS_
+ __in PCWSTR Symbol,
+ __in ULONG Flags,
+ __out_ecount_opt(IdsCount) PDEBUG_MODULE_AND_ID Ids,
+ __in ULONG IdsCount,
+ __out_opt PULONG Entries
+ ) PURE;
+ // Symbol lookup by managed metadata token.
+ STDMETHOD(GetSymbolEntryByToken)(
+ THIS_
+ __in ULONG64 ModuleBase,
+ __in ULONG Token,
+ __out PDEBUG_MODULE_AND_ID Id
+ ) PURE;
+
+ // Retrieves full symbol entry information from an ID.
+ STDMETHOD(GetSymbolEntryInformation)(
+ THIS_
+ __in PDEBUG_MODULE_AND_ID Id,
+ __out PDEBUG_SYMBOL_ENTRY Info
+ ) PURE;
+ STDMETHOD(GetSymbolEntryString)(
+ THIS_
+ __in PDEBUG_MODULE_AND_ID Id,
+ __in ULONG Which,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG StringSize
+ ) PURE;
+ STDMETHOD(GetSymbolEntryStringWide)(
+ THIS_
+ __in PDEBUG_MODULE_AND_ID Id,
+ __in ULONG Which,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG StringSize
+ ) PURE;
+ // Returns all known memory regions associated
+ // with the given symbol. Simple symbols will
+ // have a single region starting from their base.
+ // More complicated regions, such as functions
+ // with multiple code areas, can have an arbitrarily
+ // large number of regions.
+ // The quality of information returned is highly
+ // dependent on the symbolic information availble.
+ STDMETHOD(GetSymbolEntryOffsetRegions)(
+ THIS_
+ __in PDEBUG_MODULE_AND_ID Id,
+ __in ULONG Flags,
+ __out_ecount_opt(RegionsCount) PDEBUG_OFFSET_REGION Regions,
+ __in ULONG RegionsCount,
+ __out_opt PULONG RegionsAvail
+ ) PURE;
+
+ // This method allows navigating within the
+ // symbol entry hierarchy.
+ STDMETHOD(GetSymbolEntryBySymbolEntry)(
+ THIS_
+ __in PDEBUG_MODULE_AND_ID FromId,
+ __in ULONG Flags,
+ __out PDEBUG_MODULE_AND_ID ToId
+ ) PURE;
+
+ // The following methods can return multiple
+ // hits for source lookups to allow for all
+ // possible hits to be returned.
+ STDMETHOD(GetSourceEntriesByOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __in ULONG Flags,
+ __out_ecount_opt(EntriesCount) PDEBUG_SYMBOL_SOURCE_ENTRY Entries,
+ __in ULONG EntriesCount,
+ __out_opt PULONG EntriesAvail
+ ) PURE;
+ STDMETHOD(GetSourceEntriesByLine)(
+ THIS_
+ __in ULONG Line,
+ __in PCSTR File,
+ __in ULONG Flags,
+ __out_ecount_opt(EntriesCount) PDEBUG_SYMBOL_SOURCE_ENTRY Entries,
+ __in ULONG EntriesCount,
+ __out_opt PULONG EntriesAvail
+ ) PURE;
+ STDMETHOD(GetSourceEntriesByLineWide)(
+ THIS_
+ __in ULONG Line,
+ __in PCWSTR File,
+ __in ULONG Flags,
+ __out_ecount_opt(EntriesCount) PDEBUG_SYMBOL_SOURCE_ENTRY Entries,
+ __in ULONG EntriesCount,
+ __out_opt PULONG EntriesAvail
+ ) PURE;
+
+ STDMETHOD(GetSourceEntryString)(
+ THIS_
+ __in PDEBUG_SYMBOL_SOURCE_ENTRY Entry,
+ __in ULONG Which,
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG StringSize
+ ) PURE;
+ STDMETHOD(GetSourceEntryStringWide)(
+ THIS_
+ __in PDEBUG_SYMBOL_SOURCE_ENTRY Entry,
+ __in ULONG Which,
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG StringSize
+ ) PURE;
+ // Returns all known memory regions associated
+ // with the given source entry. As with
+ // GetSymbolEntryOffsetRegions the regions available
+ // are variable.
+ STDMETHOD(GetSourceEntryOffsetRegions)(
+ THIS_
+ __in PDEBUG_SYMBOL_SOURCE_ENTRY Entry,
+ __in ULONG Flags,
+ __out_ecount_opt(RegionsCount) PDEBUG_OFFSET_REGION Regions,
+ __in ULONG RegionsCount,
+ __out_opt PULONG RegionsAvail
+ ) PURE;
+
+ // This method allows navigating within the
+ // source entries.
+ STDMETHOD(GetSourceEntryBySourceEntry)(
+ THIS_
+ __in PDEBUG_SYMBOL_SOURCE_ENTRY FromEntry,
+ __in ULONG Flags,
+ __out PDEBUG_SYMBOL_SOURCE_ENTRY ToEntry
+ ) PURE;
+};
+
+//----------------------------------------------------------------------------
+//
+// IDebugSystemObjects
+//
+//----------------------------------------------------------------------------
+
+#undef INTERFACE
+#define INTERFACE IDebugSystemObjects
+DECLARE_INTERFACE_(IDebugSystemObjects, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugSystemObjects.
+
+ // In user mode debugging the debugger
+ // tracks all threads and processes and
+ // enumerates them through the following
+ // methods. When enumerating threads
+ // the threads are enumerated for the current
+ // process.
+ // Kernel mode debugging currently is
+ // limited to enumerating only the threads
+ // assigned to processors, not all of
+ // the threads in the system. Process
+ // enumeration is limited to a single
+ // virtual process representing kernel space.
+
+ // Returns the ID of the thread on which
+ // the last event occurred.
+ STDMETHOD(GetEventThread)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(GetEventProcess)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+
+ // 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.
+ STDMETHOD(GetCurrentThreadId)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(SetCurrentThreadId)(
+ THIS_
+ __in ULONG Id
+ ) PURE;
+ // The current process is the process
+ // that owns the current thread.
+ STDMETHOD(GetCurrentProcessId)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ // Setting the current process automatically
+ // sets the current thread to the thread that
+ // was last current in that process.
+ STDMETHOD(SetCurrentProcessId)(
+ THIS_
+ __in ULONG Id
+ ) PURE;
+
+ // Gets the number of threads in the current process.
+ STDMETHOD(GetNumberThreads)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ // Gets thread count information for all processes
+ // and the largest number of threads in a single process.
+ STDMETHOD(GetTotalNumberThreads)(
+ THIS_
+ __out PULONG Total,
+ __out PULONG LargestProcess
+ ) PURE;
+ STDMETHOD(GetThreadIdsByIndex)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount_opt(Count) PULONG Ids,
+ __out_ecount_opt(Count) PULONG SysIds
+ ) PURE;
+ // Gets the debugger ID for the thread
+ // currently running on the given
+ // processor. Only works in kernel
+ // debugging.
+ STDMETHOD(GetThreadIdByProcessor)(
+ THIS_
+ __in ULONG Processor,
+ __out PULONG Id
+ ) PURE;
+ // Returns the offset of the current threads
+ // system data structure. When kernel debugging
+ // this is the offset of the KTHREAD.
+ // When user debugging it is the offset
+ // of the current TEB.
+ STDMETHOD(GetCurrentThreadDataOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger thread ID for the given
+ // system thread data structure.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ STDMETHOD(GetThreadIdByDataOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the offset of the current threads
+ // TEB. In user mode this is equivalent to
+ // the threads data offset.
+ STDMETHOD(GetCurrentThreadTeb)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger thread ID for the given TEB.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ STDMETHOD(GetThreadIdByTeb)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the system unique ID for the current thread.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetCurrentThreadSystemId)(
+ THIS_
+ __out PULONG SysId
+ ) PURE;
+ // 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.
+ STDMETHOD(GetThreadIdBySystemId)(
+ THIS_
+ __in ULONG SysId,
+ __out PULONG Id
+ ) PURE;
+ // Returns the handle of the current thread.
+ // In kernel mode the value returned is the
+ // index of the processor the thread is
+ // executing on plus one.
+ STDMETHOD(GetCurrentThreadHandle)(
+ THIS_
+ __out PULONG64 Handle
+ ) PURE;
+ // Looks up a debugger thread ID for the given handle.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ STDMETHOD(GetThreadIdByHandle)(
+ THIS_
+ __in ULONG64 Handle,
+ __out PULONG Id
+ ) PURE;
+
+ // Currently kernel mode sessions will only have
+ // a single process representing kernel space.
+ STDMETHOD(GetNumberProcesses)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetProcessIdsByIndex)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount_opt(Count) PULONG Ids,
+ __out_ecount_opt(Count) PULONG SysIds
+ ) PURE;
+ // Returns the offset of the current processs
+ // system data structure. When kernel debugging
+ // this is the offset of the KPROCESS of
+ // the process that owns the current thread.
+ // When user debugging it is the offset
+ // of the current PEB.
+ STDMETHOD(GetCurrentProcessDataOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger process ID for the given
+ // system process data structure.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetProcessIdByDataOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the offset of the current processs
+ // PEB. In user mode this is equivalent to
+ // the processs data offset.
+ STDMETHOD(GetCurrentProcessPeb)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger process ID for the given PEB.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetProcessIdByPeb)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the system unique ID for the current process.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetCurrentProcessSystemId)(
+ THIS_
+ __out PULONG SysId
+ ) PURE;
+ // Looks up a debugger process ID for the given
+ // system process ID.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetProcessIdBySystemId)(
+ THIS_
+ __in ULONG SysId,
+ __out PULONG Id
+ ) PURE;
+ // Returns the handle of the current process.
+ // In kernel mode this is the kernel processs
+ // artificial handle used for symbol operations
+ // and so can only be used with dbghelp APIs.
+ STDMETHOD(GetCurrentProcessHandle)(
+ THIS_
+ __out PULONG64 Handle
+ ) PURE;
+ // Looks up a debugger process ID for the given handle.
+ STDMETHOD(GetProcessIdByHandle)(
+ THIS_
+ __in ULONG64 Handle,
+ __out PULONG Id
+ ) PURE;
+ // Retrieve the name of the executable loaded
+ // in the process. This may fail if no executable
+ // was identified.
+ STDMETHOD(GetCurrentProcessExecutableName)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ExeSize
+ ) PURE;
+};
+
+#undef INTERFACE
+#define INTERFACE IDebugSystemObjects2
+DECLARE_INTERFACE_(IDebugSystemObjects2, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugSystemObjects.
+
+ // In user mode debugging the debugger
+ // tracks all threads and processes and
+ // enumerates them through the following
+ // methods. When enumerating threads
+ // the threads are enumerated for the current
+ // process.
+ // Kernel mode debugging currently is
+ // limited to enumerating only the threads
+ // assigned to processors, not all of
+ // the threads in the system. Process
+ // enumeration is limited to a single
+ // virtual process representing kernel space.
+
+ // Returns the ID of the thread on which
+ // the last event occurred.
+ STDMETHOD(GetEventThread)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(GetEventProcess)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+
+ // 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.
+ STDMETHOD(GetCurrentThreadId)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(SetCurrentThreadId)(
+ THIS_
+ __in ULONG Id
+ ) PURE;
+ // The current process is the process
+ // that owns the current thread.
+ STDMETHOD(GetCurrentProcessId)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ // Setting the current process automatically
+ // sets the current thread to the thread that
+ // was last current in that process.
+ STDMETHOD(SetCurrentProcessId)(
+ THIS_
+ __in ULONG Id
+ ) PURE;
+
+ // Gets the number of threads in the current process.
+ STDMETHOD(GetNumberThreads)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ // Gets thread count information for all processes
+ // and the largest number of threads in a single process.
+ STDMETHOD(GetTotalNumberThreads)(
+ THIS_
+ __out PULONG Total,
+ __out PULONG LargestProcess
+ ) PURE;
+ STDMETHOD(GetThreadIdsByIndex)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount_opt(Count) PULONG Ids,
+ __out_ecount_opt(Count) PULONG SysIds
+ ) PURE;
+ // Gets the debugger ID for the thread
+ // currently running on the given
+ // processor. Only works in kernel
+ // debugging.
+ STDMETHOD(GetThreadIdByProcessor)(
+ THIS_
+ __in ULONG Processor,
+ __out PULONG Id
+ ) PURE;
+ // Returns the offset of the current threads
+ // system data structure. When kernel debugging
+ // this is the offset of the KTHREAD.
+ // When user debugging it is the offset
+ // of the current TEB.
+ STDMETHOD(GetCurrentThreadDataOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger thread ID for the given
+ // system thread data structure.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ STDMETHOD(GetThreadIdByDataOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the offset of the current threads
+ // TEB. In user mode this is equivalent to
+ // the threads data offset.
+ STDMETHOD(GetCurrentThreadTeb)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger thread ID for the given TEB.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ STDMETHOD(GetThreadIdByTeb)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the system unique ID for the current thread.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetCurrentThreadSystemId)(
+ THIS_
+ __out PULONG SysId
+ ) PURE;
+ // 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.
+ STDMETHOD(GetThreadIdBySystemId)(
+ THIS_
+ __in ULONG SysId,
+ __out PULONG Id
+ ) PURE;
+ // Returns the handle of the current thread.
+ // In kernel mode the value returned is the
+ // index of the processor the thread is
+ // executing on plus one.
+ STDMETHOD(GetCurrentThreadHandle)(
+ THIS_
+ __out PULONG64 Handle
+ ) PURE;
+ // Looks up a debugger thread ID for the given handle.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ STDMETHOD(GetThreadIdByHandle)(
+ THIS_
+ __in ULONG64 Handle,
+ __out PULONG Id
+ ) PURE;
+
+ // Currently kernel mode sessions will only have
+ // a single process representing kernel space.
+ STDMETHOD(GetNumberProcesses)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetProcessIdsByIndex)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount_opt(Count) PULONG Ids,
+ __out_ecount_opt(Count) PULONG SysIds
+ ) PURE;
+ // Returns the offset of the current processs
+ // system data structure. When kernel debugging
+ // this is the offset of the KPROCESS of
+ // the process that owns the current thread.
+ // When user debugging it is the offset
+ // of the current PEB.
+ STDMETHOD(GetCurrentProcessDataOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger process ID for the given
+ // system process data structure.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetProcessIdByDataOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the offset of the current processs
+ // PEB. In user mode this is equivalent to
+ // the processs data offset.
+ STDMETHOD(GetCurrentProcessPeb)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger process ID for the given PEB.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetProcessIdByPeb)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the system unique ID for the current process.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetCurrentProcessSystemId)(
+ THIS_
+ __out PULONG SysId
+ ) PURE;
+ // Looks up a debugger process ID for the given
+ // system process ID.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetProcessIdBySystemId)(
+ THIS_
+ __in ULONG SysId,
+ __out PULONG Id
+ ) PURE;
+ // Returns the handle of the current process.
+ // In kernel mode this is the kernel processs
+ // artificial handle used for symbol operations
+ // and so can only be used with dbghelp APIs.
+ STDMETHOD(GetCurrentProcessHandle)(
+ THIS_
+ __out PULONG64 Handle
+ ) PURE;
+ // Looks up a debugger process ID for the given handle.
+ STDMETHOD(GetProcessIdByHandle)(
+ THIS_
+ __in ULONG64 Handle,
+ __out PULONG Id
+ ) PURE;
+ // Retrieve the name of the executable loaded
+ // in the process. This may fail if no executable
+ // was identified.
+ STDMETHOD(GetCurrentProcessExecutableName)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ExeSize
+ ) PURE;
+
+ // IDebugSystemObjects2.
+
+ // Return the number of seconds that the current
+ // process has been running.
+ STDMETHOD(GetCurrentProcessUpTime)(
+ THIS_
+ __out PULONG UpTime
+ ) PURE;
+
+ // During kernel sessions the debugger retrieves
+ // some information from the system thread and process
+ // running on the current processor. For example,
+ // the debugger will retrieve virtual memory translation
+ // information for when the debugger needs to
+ // carry out its own virtual to physical translations.
+ // Occasionally it can be interesting to perform
+ // similar operations but on a process which isnt
+ // currently running. The follow methods allow a caller
+ // to override the data offsets used by the debugger
+ // so that other system threads and processes can
+ // be used instead. These values are defaulted to
+ // the thread and process running on the current
+ // processor each time the debuggee executes or
+ // the current processor changes.
+ // The thread and process settings are independent so
+ // it is possible to refer to a thread in a process
+ // other than the current process and vice versa.
+ // Setting an offset of zero will reload the
+ // default value.
+ STDMETHOD(GetImplicitThreadDataOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(SetImplicitThreadDataOffset)(
+ THIS_
+ __in ULONG64 Offset
+ ) PURE;
+ STDMETHOD(GetImplicitProcessDataOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(SetImplicitProcessDataOffset)(
+ THIS_
+ __in ULONG64 Offset
+ ) PURE;
+};
+
+#undef INTERFACE
+#define INTERFACE IDebugSystemObjects3
+DECLARE_INTERFACE_(IDebugSystemObjects3, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugSystemObjects.
+
+ // In user mode debugging the debugger
+ // tracks all threads and processes and
+ // enumerates them through the following
+ // methods. When enumerating threads
+ // the threads are enumerated for the current
+ // process.
+ // Kernel mode debugging currently is
+ // limited to enumerating only the threads
+ // assigned to processors, not all of
+ // the threads in the system. Process
+ // enumeration is limited to a single
+ // virtual process representing kernel space.
+
+ // Returns the ID of the thread on which
+ // the last event occurred.
+ STDMETHOD(GetEventThread)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(GetEventProcess)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+
+ // 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.
+ STDMETHOD(GetCurrentThreadId)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(SetCurrentThreadId)(
+ THIS_
+ __in ULONG Id
+ ) PURE;
+ // The current process is the process
+ // that owns the current thread.
+ STDMETHOD(GetCurrentProcessId)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ // Setting the current process automatically
+ // sets the current thread to the thread that
+ // was last current in that process.
+ STDMETHOD(SetCurrentProcessId)(
+ THIS_
+ __in ULONG Id
+ ) PURE;
+
+ // Gets the number of threads in the current process.
+ STDMETHOD(GetNumberThreads)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ // Gets thread count information for all processes
+ // and the largest number of threads in a single process.
+ STDMETHOD(GetTotalNumberThreads)(
+ THIS_
+ __out PULONG Total,
+ __out PULONG LargestProcess
+ ) PURE;
+ STDMETHOD(GetThreadIdsByIndex)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount_opt(Count) PULONG Ids,
+ __out_ecount_opt(Count) PULONG SysIds
+ ) PURE;
+ // Gets the debugger ID for the thread
+ // currently running on the given
+ // processor. Only works in kernel
+ // debugging.
+ STDMETHOD(GetThreadIdByProcessor)(
+ THIS_
+ __in ULONG Processor,
+ __out PULONG Id
+ ) PURE;
+ // Returns the offset of the current threads
+ // system data structure. When kernel debugging
+ // this is the offset of the KTHREAD.
+ // When user debugging it is the offset
+ // of the current TEB.
+ STDMETHOD(GetCurrentThreadDataOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger thread ID for the given
+ // system thread data structure.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ STDMETHOD(GetThreadIdByDataOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the offset of the current threads
+ // TEB. In user mode this is equivalent to
+ // the threads data offset.
+ STDMETHOD(GetCurrentThreadTeb)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger thread ID for the given TEB.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ STDMETHOD(GetThreadIdByTeb)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the system unique ID for the current thread.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetCurrentThreadSystemId)(
+ THIS_
+ __out PULONG SysId
+ ) PURE;
+ // 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.
+ STDMETHOD(GetThreadIdBySystemId)(
+ THIS_
+ __in ULONG SysId,
+ __out PULONG Id
+ ) PURE;
+ // Returns the handle of the current thread.
+ // In kernel mode the value returned is the
+ // index of the processor the thread is
+ // executing on plus one.
+ STDMETHOD(GetCurrentThreadHandle)(
+ THIS_
+ __out PULONG64 Handle
+ ) PURE;
+ // Looks up a debugger thread ID for the given handle.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ STDMETHOD(GetThreadIdByHandle)(
+ THIS_
+ __in ULONG64 Handle,
+ __out PULONG Id
+ ) PURE;
+
+ // Currently kernel mode sessions will only have
+ // a single process representing kernel space.
+ STDMETHOD(GetNumberProcesses)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetProcessIdsByIndex)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount_opt(Count) PULONG Ids,
+ __out_ecount_opt(Count) PULONG SysIds
+ ) PURE;
+ // Returns the offset of the current processs
+ // system data structure. When kernel debugging
+ // this is the offset of the KPROCESS of
+ // the process that owns the current thread.
+ // When user debugging it is the offset
+ // of the current PEB.
+ STDMETHOD(GetCurrentProcessDataOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger process ID for the given
+ // system process data structure.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetProcessIdByDataOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the offset of the current processs
+ // PEB. In user mode this is equivalent to
+ // the processs data offset.
+ STDMETHOD(GetCurrentProcessPeb)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger process ID for the given PEB.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetProcessIdByPeb)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the system unique ID for the current process.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetCurrentProcessSystemId)(
+ THIS_
+ __out PULONG SysId
+ ) PURE;
+ // Looks up a debugger process ID for the given
+ // system process ID.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetProcessIdBySystemId)(
+ THIS_
+ __in ULONG SysId,
+ __out PULONG Id
+ ) PURE;
+ // Returns the handle of the current process.
+ // In kernel mode this is the kernel processs
+ // artificial handle used for symbol operations
+ // and so can only be used with dbghelp APIs.
+ STDMETHOD(GetCurrentProcessHandle)(
+ THIS_
+ __out PULONG64 Handle
+ ) PURE;
+ // Looks up a debugger process ID for the given handle.
+ STDMETHOD(GetProcessIdByHandle)(
+ THIS_
+ __in ULONG64 Handle,
+ __out PULONG Id
+ ) PURE;
+ // Retrieve the name of the executable loaded
+ // in the process. This may fail if no executable
+ // was identified.
+ STDMETHOD(GetCurrentProcessExecutableName)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ExeSize
+ ) PURE;
+
+ // IDebugSystemObjects2.
+
+ // Return the number of seconds that the current
+ // process has been running.
+ STDMETHOD(GetCurrentProcessUpTime)(
+ THIS_
+ __out PULONG UpTime
+ ) PURE;
+
+ // During kernel sessions the debugger retrieves
+ // some information from the system thread and process
+ // running on the current processor. For example,
+ // the debugger will retrieve virtual memory translation
+ // information for when the debugger needs to
+ // carry out its own virtual to physical translations.
+ // Occasionally it can be interesting to perform
+ // similar operations but on a process which isnt
+ // currently running. The follow methods allow a caller
+ // to override the data offsets used by the debugger
+ // so that other system threads and processes can
+ // be used instead. These values are defaulted to
+ // the thread and process running on the current
+ // processor each time the debuggee executes or
+ // the current processor changes.
+ // The thread and process settings are independent so
+ // it is possible to refer to a thread in a process
+ // other than the current process and vice versa.
+ // Setting an offset of zero will reload the
+ // default value.
+ STDMETHOD(GetImplicitThreadDataOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(SetImplicitThreadDataOffset)(
+ THIS_
+ __in ULONG64 Offset
+ ) PURE;
+ STDMETHOD(GetImplicitProcessDataOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(SetImplicitProcessDataOffset)(
+ THIS_
+ __in ULONG64 Offset
+ ) PURE;
+
+ // IDebugSystemObjects3.
+
+ STDMETHOD(GetEventSystem)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+
+ STDMETHOD(GetCurrentSystemId)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(SetCurrentSystemId)(
+ THIS_
+ __in ULONG Id
+ ) PURE;
+
+ STDMETHOD(GetNumberSystems)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetSystemIdsByIndex)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PULONG Ids
+ ) PURE;
+ STDMETHOD(GetTotalNumberThreadsAndProcesses)(
+ THIS_
+ __out PULONG TotalThreads,
+ __out PULONG TotalProcesses,
+ __out PULONG LargestProcessThreads,
+ __out PULONG LargestSystemThreads,
+ __out PULONG LargestSystemProcesses
+ ) PURE;
+ STDMETHOD(GetCurrentSystemServer)(
+ THIS_
+ __out PULONG64 Server
+ ) PURE;
+ STDMETHOD(GetSystemByServer)(
+ THIS_
+ __in ULONG64 Server,
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(GetCurrentSystemServerName)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+};
+
+#undef INTERFACE
+#define INTERFACE IDebugSystemObjects4
+DECLARE_INTERFACE_(IDebugSystemObjects4, IUnknown)
+{
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ ) PURE;
+ STDMETHOD_(ULONG, AddRef)(
+ THIS
+ ) PURE;
+ STDMETHOD_(ULONG, Release)(
+ THIS
+ ) PURE;
+
+ // IDebugSystemObjects.
+
+ // In user mode debugging the debugger
+ // tracks all threads and processes and
+ // enumerates them through the following
+ // methods. When enumerating threads
+ // the threads are enumerated for the current
+ // process.
+ // Kernel mode debugging currently is
+ // limited to enumerating only the threads
+ // assigned to processors, not all of
+ // the threads in the system. Process
+ // enumeration is limited to a single
+ // virtual process representing kernel space.
+
+ // Returns the ID of the thread on which
+ // the last event occurred.
+ STDMETHOD(GetEventThread)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(GetEventProcess)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+
+ // 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.
+ STDMETHOD(GetCurrentThreadId)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(SetCurrentThreadId)(
+ THIS_
+ __in ULONG Id
+ ) PURE;
+ // The current process is the process
+ // that owns the current thread.
+ STDMETHOD(GetCurrentProcessId)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ // Setting the current process automatically
+ // sets the current thread to the thread that
+ // was last current in that process.
+ STDMETHOD(SetCurrentProcessId)(
+ THIS_
+ __in ULONG Id
+ ) PURE;
+
+ // Gets the number of threads in the current process.
+ STDMETHOD(GetNumberThreads)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ // Gets thread count information for all processes
+ // and the largest number of threads in a single process.
+ STDMETHOD(GetTotalNumberThreads)(
+ THIS_
+ __out PULONG Total,
+ __out PULONG LargestProcess
+ ) PURE;
+ STDMETHOD(GetThreadIdsByIndex)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount_opt(Count) PULONG Ids,
+ __out_ecount_opt(Count) PULONG SysIds
+ ) PURE;
+ // Gets the debugger ID for the thread
+ // currently running on the given
+ // processor. Only works in kernel
+ // debugging.
+ STDMETHOD(GetThreadIdByProcessor)(
+ THIS_
+ __in ULONG Processor,
+ __out PULONG Id
+ ) PURE;
+ // Returns the offset of the current threads
+ // system data structure. When kernel debugging
+ // this is the offset of the KTHREAD.
+ // When user debugging it is the offset
+ // of the current TEB.
+ STDMETHOD(GetCurrentThreadDataOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger thread ID for the given
+ // system thread data structure.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ STDMETHOD(GetThreadIdByDataOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the offset of the current threads
+ // TEB. In user mode this is equivalent to
+ // the threads data offset.
+ STDMETHOD(GetCurrentThreadTeb)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger thread ID for the given TEB.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ STDMETHOD(GetThreadIdByTeb)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the system unique ID for the current thread.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetCurrentThreadSystemId)(
+ THIS_
+ __out PULONG SysId
+ ) PURE;
+ // 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.
+ STDMETHOD(GetThreadIdBySystemId)(
+ THIS_
+ __in ULONG SysId,
+ __out PULONG Id
+ ) PURE;
+ // Returns the handle of the current thread.
+ // In kernel mode the value returned is the
+ // index of the processor the thread is
+ // executing on plus one.
+ STDMETHOD(GetCurrentThreadHandle)(
+ THIS_
+ __out PULONG64 Handle
+ ) PURE;
+ // Looks up a debugger thread ID for the given handle.
+ // Currently when kernel debugging this will fail
+ // if the thread is not executing on a processor.
+ STDMETHOD(GetThreadIdByHandle)(
+ THIS_
+ __in ULONG64 Handle,
+ __out PULONG Id
+ ) PURE;
+
+ // Currently kernel mode sessions will only have
+ // a single process representing kernel space.
+ STDMETHOD(GetNumberProcesses)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetProcessIdsByIndex)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount_opt(Count) PULONG Ids,
+ __out_ecount_opt(Count) PULONG SysIds
+ ) PURE;
+ // Returns the offset of the current processs
+ // system data structure. When kernel debugging
+ // this is the offset of the KPROCESS of
+ // the process that owns the current thread.
+ // When user debugging it is the offset
+ // of the current PEB.
+ STDMETHOD(GetCurrentProcessDataOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger process ID for the given
+ // system process data structure.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetProcessIdByDataOffset)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the offset of the current processs
+ // PEB. In user mode this is equivalent to
+ // the processs data offset.
+ STDMETHOD(GetCurrentProcessPeb)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ // Looks up a debugger process ID for the given PEB.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetProcessIdByPeb)(
+ THIS_
+ __in ULONG64 Offset,
+ __out PULONG Id
+ ) PURE;
+ // Returns the system unique ID for the current process.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetCurrentProcessSystemId)(
+ THIS_
+ __out PULONG SysId
+ ) PURE;
+ // Looks up a debugger process ID for the given
+ // system process ID.
+ // Not currently supported when kernel debugging.
+ STDMETHOD(GetProcessIdBySystemId)(
+ THIS_
+ __in ULONG SysId,
+ __out PULONG Id
+ ) PURE;
+ // Returns the handle of the current process.
+ // In kernel mode this is the kernel processs
+ // artificial handle used for symbol operations
+ // and so can only be used with dbghelp APIs.
+ STDMETHOD(GetCurrentProcessHandle)(
+ THIS_
+ __out PULONG64 Handle
+ ) PURE;
+ // Looks up a debugger process ID for the given handle.
+ STDMETHOD(GetProcessIdByHandle)(
+ THIS_
+ __in ULONG64 Handle,
+ __out PULONG Id
+ ) PURE;
+ // Retrieve the name of the executable loaded
+ // in the process. This may fail if no executable
+ // was identified.
+ STDMETHOD(GetCurrentProcessExecutableName)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ExeSize
+ ) PURE;
+
+ // IDebugSystemObjects2.
+
+ // Return the number of seconds that the current
+ // process has been running.
+ STDMETHOD(GetCurrentProcessUpTime)(
+ THIS_
+ __out PULONG UpTime
+ ) PURE;
+
+ // During kernel sessions the debugger retrieves
+ // some information from the system thread and process
+ // running on the current processor. For example,
+ // the debugger will retrieve virtual memory translation
+ // information for when the debugger needs to
+ // carry out its own virtual to physical translations.
+ // Occasionally it can be interesting to perform
+ // similar operations but on a process which isnt
+ // currently running. The follow methods allow a caller
+ // to override the data offsets used by the debugger
+ // so that other system threads and processes can
+ // be used instead. These values are defaulted to
+ // the thread and process running on the current
+ // processor each time the debuggee executes or
+ // the current processor changes.
+ // The thread and process settings are independent so
+ // it is possible to refer to a thread in a process
+ // other than the current process and vice versa.
+ // Setting an offset of zero will reload the
+ // default value.
+ STDMETHOD(GetImplicitThreadDataOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(SetImplicitThreadDataOffset)(
+ THIS_
+ __in ULONG64 Offset
+ ) PURE;
+ STDMETHOD(GetImplicitProcessDataOffset)(
+ THIS_
+ __out PULONG64 Offset
+ ) PURE;
+ STDMETHOD(SetImplicitProcessDataOffset)(
+ THIS_
+ __in ULONG64 Offset
+ ) PURE;
+
+ // IDebugSystemObjects3.
+
+ STDMETHOD(GetEventSystem)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+
+ STDMETHOD(GetCurrentSystemId)(
+ THIS_
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(SetCurrentSystemId)(
+ THIS_
+ __in ULONG Id
+ ) PURE;
+
+ STDMETHOD(GetNumberSystems)(
+ THIS_
+ __out PULONG Number
+ ) PURE;
+ STDMETHOD(GetSystemIdsByIndex)(
+ THIS_
+ __in ULONG Start,
+ __in ULONG Count,
+ __out_ecount(Count) PULONG Ids
+ ) PURE;
+ STDMETHOD(GetTotalNumberThreadsAndProcesses)(
+ THIS_
+ __out PULONG TotalThreads,
+ __out PULONG TotalProcesses,
+ __out PULONG LargestProcessThreads,
+ __out PULONG LargestSystemThreads,
+ __out PULONG LargestSystemProcesses
+ ) PURE;
+ STDMETHOD(GetCurrentSystemServer)(
+ THIS_
+ __out PULONG64 Server
+ ) PURE;
+ STDMETHOD(GetSystemByServer)(
+ THIS_
+ __in ULONG64 Server,
+ __out PULONG Id
+ ) PURE;
+ STDMETHOD(GetCurrentSystemServerName)(
+ THIS_
+ __out_ecount_opt(BufferSize) PSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+
+ // IDebugSystemObjects4.
+
+ STDMETHOD(GetCurrentProcessExecutableNameWide)(
+ THIS_
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG ExeSize
+ ) PURE;
+
+ STDMETHOD(GetCurrentSystemServerNameWide)(
+ THIS_
+ __out_ecount_opt(BufferSize) PWSTR Buffer,
+ __in ULONG BufferSize,
+ __out_opt PULONG NameSize
+ ) PURE;
+};
+
+//----------------------------------------------------------------------------
+//
+// Debugger/debuggee communication.
+//
+// A distinguished exception, DBG_COMMAND_EXCEPTION (0x40010009),
+// can be used by a debuggee to communicate with the debugger.
+// The arguments of the exception must be:
+// 1. Exception ID.
+// 2. Command code.
+// 3. Size of argument.
+// 4. Pointer to argument.
+//
+// The arguments depend on the command code.
+//
+//----------------------------------------------------------------------------
+
+#define DEBUG_COMMAND_EXCEPTION_ID 0xdbe00dbe
+
+// Invalid command code.
+#define DEBUG_CMDEX_INVALID 0x00000000
+
+//
+// The debugger can collect strings for display at the
+// next event. A debuggee can use this to register information
+// about a program situation before places where an event
+// may occur, such as a risky operation or assertion.
+// The strings are automatically flushed on the next
+// event continuation. Strings are kept on a per-thread basis.
+//
+// When adding, the argument is the string to add.
+// Reset has no arguments and clears all strings.
+//
+#define DEBUG_CMDEX_ADD_EVENT_STRING 0x00000001
+#define DEBUG_CMDEX_RESET_EVENT_STRINGS 0x00000002
+
+#ifndef DEBUG_NO_IMPLEMENTATION
+
+FORCEINLINE void
+DebugCommandException(ULONG Command, ULONG ArgSize, PVOID Arg)
+{
+ ULONG_PTR ExArgs[4];
+
+ ExArgs[0] = DEBUG_COMMAND_EXCEPTION_ID;
+ ExArgs[1] = Command;
+ ExArgs[2] = ArgSize;
+ ExArgs[3] = (ULONG_PTR)Arg;
+ RaiseException(DBG_COMMAND_EXCEPTION, 0, 4, ExArgs);
+}
+
+#endif // #ifndef DEBUG_NO_IMPLEMENTATION
+
+//----------------------------------------------------------------------------
+//
+// Extension callbacks.
+//
+//----------------------------------------------------------------------------
+
+// Returns a version with the major version in
+// the high word and the minor version in the low word.
+#define DEBUG_EXTENSION_VERSION(Major, Minor) \
+ ((((Major) & 0xffff) << 16) | ((Minor) & 0xffff))
+
+//
+// Descriptive flags returned from extension initialization.
+//
+
+// Extension has a !help command which can give
+// per-command help.
+#define DEBUG_EXTINIT_HAS_COMMAND_HELP 0x00000001
+
+// Initialization routine. Called once when the extension DLL
+// is loaded. Returns a version and returns flags detailing
+// overall qualities of the extension DLL.
+// A session may or may not be active at the time the DLL
+// is loaded so initialization routines should not expect
+// to be able to query session information.
+typedef HRESULT (CALLBACK* PDEBUG_EXTENSION_INITIALIZE)
+ (__out PULONG Version, __out PULONG Flags);
+// Exit routine. Called once just before the extension DLL is
+// unloaded. As with initialization, a session may or
+// may not be active at the time of the call.
+typedef void (CALLBACK* PDEBUG_EXTENSION_UNINITIALIZE)
+ (void);
+
+// A debuggee has been discovered for the session. It
+// is not necessarily halted.
+#define DEBUG_NOTIFY_SESSION_ACTIVE 0x00000000
+// The session no longer has a debuggee.
+#define DEBUG_NOTIFY_SESSION_INACTIVE 0x00000001
+// The debuggee is halted and accessible.
+#define DEBUG_NOTIFY_SESSION_ACCESSIBLE 0x00000002
+// The debuggee is running or inaccessible.
+#define DEBUG_NOTIFY_SESSION_INACCESSIBLE 0x00000003
+
+typedef void (CALLBACK* PDEBUG_EXTENSION_NOTIFY)
+ (__in ULONG Notify, __in ULONG64 Argument);
+
+// A PDEBUG_EXTENSION_CALL function can return this code
+// to indicate that it was unable to handle the request
+// and that the search for an extension function should
+// continue down the extension DLL chain.
+// Taken from STATUS_VALIDATE_CONTINUE.
+#define DEBUG_EXTENSION_CONTINUE_SEARCH \
+ HRESULT_FROM_NT(0xC0000271L)
+
+// A PDEBUG_EXTENSION_CALL function can return this code
+// to indicate that the engine should unload and reload
+// the extension binary. This allows extensions to implement
+// auto-update functionality.
+#define DEBUG_EXTENSION_RELOAD_EXTENSION \
+ HRESULT_FROM_NT(0xC00000EEL)
+
+// Every routine in an extension DLL has the following prototype.
+// The extension may be called from multiple clients so it
+// should not cache the client value between calls.
+typedef HRESULT (CALLBACK* PDEBUG_EXTENSION_CALL)
+ (__in PDEBUG_CLIENT Client, __in_opt PCSTR Args);
+
+//
+// KnownStructOutput[Ex] flags
+//
+
+// Return names of supported structs.
+#define DEBUG_KNOWN_STRUCT_GET_NAMES 1
+// Return value output for type.
+#define DEBUG_KNOWN_STRUCT_GET_SINGLE_LINE_OUTPUT 2
+// Return S_OK if suppressing type name.
+#define DEBUG_KNOWN_STRUCT_SUPPRESS_TYPE_NAME 3
+
+// Extensions may export this callback in order to dump structs that
+// are well known to them. The engine calls this to inject extension
+// output into dt's struct dump.
+typedef HRESULT (CALLBACK* PDEBUG_EXTENSION_KNOWN_STRUCT)
+ (__in ULONG Flags,
+ __in ULONG64 Offset,
+ __in_opt PSTR TypeName,
+ __out_ecount_opt(*BufferChars) PSTR Buffer,
+ __inout_opt PULONG BufferChars);
+typedef HRESULT (CALLBACK* PDEBUG_EXTENSION_KNOWN_STRUCT_EX)
+ (__in PDEBUG_CLIENT Client,
+ __in ULONG Flags,
+ __in ULONG64 Offset,
+ __in_opt PCSTR TypeName,
+ __out_ecount_opt(*BufferChars) PSTR Buffer,
+ __inout_opt PULONG BufferChars);
+
+// Backwards compatibility with old, incorrect name.
+typedef PDEBUG_EXTENSION_KNOWN_STRUCT PDEBUG_ENTENSION_KNOWNSTRUCT;
+
+//
+// Extensions can provide pseudo-register values that
+// operate similiarly to the debugger's built-in $teb, etc.
+//
+
+#define DEBUG_EXT_QVALUE_DEFAULT 0x00000000
+
+typedef HRESULT (CALLBACK* PDEBUG_EXTENSION_QUERY_VALUE_NAMES)
+ (__in PDEBUG_CLIENT Client,
+ __in ULONG Flags,
+ __out_ecount(BufferChars) PWSTR Buffer,
+ __in ULONG BufferChars,
+ __out PULONG BufferNeeded);
+
+#define DEBUG_EXT_PVALUE_DEFAULT 0x00000000
+
+#define DEBUG_EXT_PVTYPE_IS_VALUE 0x00000000
+#define DEBUG_EXT_PVTYPE_IS_POINTER 0x00000001
+
+typedef HRESULT (CALLBACK* PDEBUG_EXTENSION_PROVIDE_VALUE)
+ (__in PDEBUG_CLIENT Client,
+ __in ULONG Flags,
+ __in PCWSTR Name,
+ __out PULONG64 Value,
+ __out PULONG64 TypeModBase,
+ __out PULONG TypeId,
+ __out PULONG TypeFlags);
+
+//----------------------------------------------------------------------------
+//
+// Extension functions.
+//
+// Extension functions differ from extension callbacks in that
+// they are arbitrary functions exported from an extension DLL
+// for other code callers instead of for human invocation from
+// debugger commands. Extension function pointers are retrieved
+// for an extension DLL with IDebugControl::GetExtensionFunction.
+//
+// Extension function names must begin with _EFN_. Other than that
+// they can have any name and prototype. Extension functions
+// must be public exports of their extension DLL. They should
+// have a typedef for their function pointer prototype in an
+// extension header so that callers have a header file to include
+// with a type that allows a correctly-formed invocation of the
+// extension function.
+//
+// The engine does not perform any validation of calls to
+// extension functions. Once the extension function pointer
+// is retrieved with GetExtensionFunction all calls go
+// directly between the caller and the extension function and
+// are not mediated by the engine.
+//
+//----------------------------------------------------------------------------
+
+#ifdef __cplusplus
+};
+
+//----------------------------------------------------------------------------
+//
+// C++ implementation helper classes.
+//
+//----------------------------------------------------------------------------
+
+#if !defined(DEBUG_NO_IMPLEMENTATION) && !defined(_M_CEE_PURE)
+
+//
+// DebugBaseEventCallbacks provides a do-nothing base implementation
+// of IDebugEventCallbacks. A program can derive their own
+// event callbacks class from DebugBaseEventCallbacks and implement
+// only the methods they are interested in. Programs must be
+// careful to implement GetInterestMask appropriately.
+//
+class DebugBaseEventCallbacks : public IDebugEventCallbacks
+{
+public:
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ )
+ {
+ *Interface = NULL;
+
+#if _MSC_VER >= 1100
+ if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) ||
+ IsEqualIID(InterfaceId, __uuidof(IDebugEventCallbacks)))
+#else
+ if (IsEqualIID(InterfaceId, IID_IUnknown) ||
+ IsEqualIID(InterfaceId, IID_IDebugEventCallbacks))
+#endif
+ {
+ *Interface = (IDebugEventCallbacks *)this;
+ AddRef();
+ return S_OK;
+ }
+ else
+ {
+ return E_NOINTERFACE;
+ }
+ }
+
+ // IDebugEventCallbacks.
+
+ STDMETHOD(Breakpoint)(
+ THIS_
+ __in PDEBUG_BREAKPOINT Bp
+ )
+ {
+ UNREFERENCED_PARAMETER(Bp);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(Exception)(
+ THIS_
+ __in PEXCEPTION_RECORD64 Exception,
+ __in ULONG FirstChance
+ )
+ {
+ UNREFERENCED_PARAMETER(Exception);
+ UNREFERENCED_PARAMETER(FirstChance);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(CreateThread)(
+ THIS_
+ __in ULONG64 Handle,
+ __in ULONG64 DataOffset,
+ __in ULONG64 StartOffset
+ )
+ {
+ UNREFERENCED_PARAMETER(Handle);
+ UNREFERENCED_PARAMETER(DataOffset);
+ UNREFERENCED_PARAMETER(StartOffset);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(ExitThread)(
+ THIS_
+ __in ULONG ExitCode
+ )
+ {
+ UNREFERENCED_PARAMETER(ExitCode);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(CreateProcess)(
+ THIS_
+ __in ULONG64 ImageFileHandle,
+ __in ULONG64 Handle,
+ __in ULONG64 BaseOffset,
+ __in ULONG ModuleSize,
+ __in PCSTR ModuleName,
+ __in PCSTR ImageName,
+ __in ULONG CheckSum,
+ __in ULONG TimeDateStamp,
+ __in ULONG64 InitialThreadHandle,
+ __in ULONG64 ThreadDataOffset,
+ __in ULONG64 StartOffset
+ )
+ {
+ UNREFERENCED_PARAMETER(ImageFileHandle);
+ UNREFERENCED_PARAMETER(Handle);
+ UNREFERENCED_PARAMETER(BaseOffset);
+ UNREFERENCED_PARAMETER(ModuleSize);
+ UNREFERENCED_PARAMETER(ModuleName);
+ UNREFERENCED_PARAMETER(ImageName);
+ UNREFERENCED_PARAMETER(CheckSum);
+ UNREFERENCED_PARAMETER(TimeDateStamp);
+ UNREFERENCED_PARAMETER(InitialThreadHandle);
+ UNREFERENCED_PARAMETER(ThreadDataOffset);
+ UNREFERENCED_PARAMETER(StartOffset);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(ExitProcess)(
+ THIS_
+ __in ULONG ExitCode
+ )
+ {
+ UNREFERENCED_PARAMETER(ExitCode);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(LoadModule)(
+ THIS_
+ __in ULONG64 ImageFileHandle,
+ __in ULONG64 BaseOffset,
+ __in ULONG ModuleSize,
+ __in PCSTR ModuleName,
+ __in PCSTR ImageName,
+ __in ULONG CheckSum,
+ __in ULONG TimeDateStamp
+ )
+ {
+ UNREFERENCED_PARAMETER(ImageFileHandle);
+ UNREFERENCED_PARAMETER(BaseOffset);
+ UNREFERENCED_PARAMETER(ModuleSize);
+ UNREFERENCED_PARAMETER(ModuleName);
+ UNREFERENCED_PARAMETER(ImageName);
+ UNREFERENCED_PARAMETER(CheckSum);
+ UNREFERENCED_PARAMETER(TimeDateStamp);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(UnloadModule)(
+ THIS_
+ __in PCSTR ImageBaseName,
+ __in ULONG64 BaseOffset
+ )
+ {
+ UNREFERENCED_PARAMETER(ImageBaseName);
+ UNREFERENCED_PARAMETER(BaseOffset);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(SystemError)(
+ THIS_
+ __in ULONG Error,
+ __in ULONG Level
+ )
+ {
+ UNREFERENCED_PARAMETER(Error);
+ UNREFERENCED_PARAMETER(Level);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(SessionStatus)(
+ THIS_
+ __in ULONG Status
+ )
+ {
+ UNREFERENCED_PARAMETER(Status);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(ChangeDebuggeeState)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG64 Argument
+ )
+ {
+ UNREFERENCED_PARAMETER(Flags);
+ UNREFERENCED_PARAMETER(Argument);
+ return S_OK;
+ }
+ STDMETHOD(ChangeEngineState)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG64 Argument
+ )
+ {
+ UNREFERENCED_PARAMETER(Flags);
+ UNREFERENCED_PARAMETER(Argument);
+ return S_OK;
+ }
+ STDMETHOD(ChangeSymbolState)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG64 Argument
+ )
+ {
+ UNREFERENCED_PARAMETER(Flags);
+ UNREFERENCED_PARAMETER(Argument);
+ return S_OK;
+ }
+};
+
+class DebugBaseEventCallbacksWide : public IDebugEventCallbacksWide
+{
+public:
+ // IUnknown.
+ STDMETHOD(QueryInterface)(
+ THIS_
+ __in REFIID InterfaceId,
+ __out PVOID* Interface
+ )
+ {
+ *Interface = NULL;
+
+#if _MSC_VER >= 1100
+ if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) ||
+ IsEqualIID(InterfaceId, __uuidof(IDebugEventCallbacksWide)))
+#else
+ if (IsEqualIID(InterfaceId, IID_IUnknown) ||
+ IsEqualIID(InterfaceId, IID_IDebugEventCallbacksWide))
+#endif
+ {
+ *Interface = (IDebugEventCallbacksWide *)this;
+ AddRef();
+ return S_OK;
+ }
+ else
+ {
+ return E_NOINTERFACE;
+ }
+ }
+
+ // IDebugEventCallbacksWide.
+
+ STDMETHOD(Breakpoint)(
+ THIS_
+ __in PDEBUG_BREAKPOINT2 Bp
+ )
+ {
+ UNREFERENCED_PARAMETER(Bp);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(Exception)(
+ THIS_
+ __in PEXCEPTION_RECORD64 Exception,
+ __in ULONG FirstChance
+ )
+ {
+ UNREFERENCED_PARAMETER(Exception);
+ UNREFERENCED_PARAMETER(FirstChance);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(CreateThread)(
+ THIS_
+ __in ULONG64 Handle,
+ __in ULONG64 DataOffset,
+ __in ULONG64 StartOffset
+ )
+ {
+ UNREFERENCED_PARAMETER(Handle);
+ UNREFERENCED_PARAMETER(DataOffset);
+ UNREFERENCED_PARAMETER(StartOffset);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(ExitThread)(
+ THIS_
+ __in ULONG ExitCode
+ )
+ {
+ UNREFERENCED_PARAMETER(ExitCode);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(CreateProcess)(
+ THIS_
+ __in ULONG64 ImageFileHandle,
+ __in ULONG64 Handle,
+ __in ULONG64 BaseOffset,
+ __in ULONG ModuleSize,
+ __in PCWSTR ModuleName,
+ __in PCWSTR ImageName,
+ __in ULONG CheckSum,
+ __in ULONG TimeDateStamp,
+ __in ULONG64 InitialThreadHandle,
+ __in ULONG64 ThreadDataOffset,
+ __in ULONG64 StartOffset
+ )
+ {
+ UNREFERENCED_PARAMETER(ImageFileHandle);
+ UNREFERENCED_PARAMETER(Handle);
+ UNREFERENCED_PARAMETER(BaseOffset);
+ UNREFERENCED_PARAMETER(ModuleSize);
+ UNREFERENCED_PARAMETER(ModuleName);
+ UNREFERENCED_PARAMETER(ImageName);
+ UNREFERENCED_PARAMETER(CheckSum);
+ UNREFERENCED_PARAMETER(TimeDateStamp);
+ UNREFERENCED_PARAMETER(InitialThreadHandle);
+ UNREFERENCED_PARAMETER(ThreadDataOffset);
+ UNREFERENCED_PARAMETER(StartOffset);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(ExitProcess)(
+ THIS_
+ __in ULONG ExitCode
+ )
+ {
+ UNREFERENCED_PARAMETER(ExitCode);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(LoadModule)(
+ THIS_
+ __in ULONG64 ImageFileHandle,
+ __in ULONG64 BaseOffset,
+ __in ULONG ModuleSize,
+ __in PCWSTR ModuleName,
+ __in PCWSTR ImageName,
+ __in ULONG CheckSum,
+ __in ULONG TimeDateStamp
+ )
+ {
+ UNREFERENCED_PARAMETER(ImageFileHandle);
+ UNREFERENCED_PARAMETER(BaseOffset);
+ UNREFERENCED_PARAMETER(ModuleSize);
+ UNREFERENCED_PARAMETER(ModuleName);
+ UNREFERENCED_PARAMETER(ImageName);
+ UNREFERENCED_PARAMETER(CheckSum);
+ UNREFERENCED_PARAMETER(TimeDateStamp);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(UnloadModule)(
+ THIS_
+ __in PCWSTR ImageBaseName,
+ __in ULONG64 BaseOffset
+ )
+ {
+ UNREFERENCED_PARAMETER(ImageBaseName);
+ UNREFERENCED_PARAMETER(BaseOffset);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(SystemError)(
+ THIS_
+ __in ULONG Error,
+ __in ULONG Level
+ )
+ {
+ UNREFERENCED_PARAMETER(Error);
+ UNREFERENCED_PARAMETER(Level);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(SessionStatus)(
+ THIS_
+ __in ULONG Status
+ )
+ {
+ UNREFERENCED_PARAMETER(Status);
+ return DEBUG_STATUS_NO_CHANGE;
+ }
+ STDMETHOD(ChangeDebuggeeState)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG64 Argument
+ )
+ {
+ UNREFERENCED_PARAMETER(Flags);
+ UNREFERENCED_PARAMETER(Argument);
+ return S_OK;
+ }
+ STDMETHOD(ChangeEngineState)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG64 Argument
+ )
+ {
+ UNREFERENCED_PARAMETER(Flags);
+ UNREFERENCED_PARAMETER(Argument);
+ return S_OK;
+ }
+ STDMETHOD(ChangeSymbolState)(
+ THIS_
+ __in ULONG Flags,
+ __in ULONG64 Argument
+ )
+ {
+ UNREFERENCED_PARAMETER(Flags);
+ UNREFERENCED_PARAMETER(Argument);
+ return S_OK;
+ }
+};
+
+#endif // #ifndef DEBUG_NO_IMPLEMENTATION
+
+#ifdef DEBUG_UNICODE_MACROS
+
+#ifdef UNICODE
+
+#define IDebugEventCallbacksT IDebugEventCallbacksWide
+#define IID_IDebugEventCallbacksT IID_IDebugEventCallbacksWide
+#define IDebugOutputCallbacksT IDebugOutputCallbacksWide
+#define IID_IDebugOutputCallbacksT IID_IDebugOutputCallbacksWide
+#define DebugBaseEventCallbacksT DebugBaseEventCallbacksWide
+
+#define DebugConnectT DebugConnectWide
+#define GetSourceFileInformationT GetSourceFileInformationWide
+#define FindSourceFileAndTokenT FindSourceFileAndTokenWide
+#define GetSymbolInformationT GetSymbolInformationWide
+#define GetCommandT GetCommandWide
+#define SetCommandT SetCommandWide
+#define GetOffsetExpressionT GetOffsetExpressionWide
+#define SetOffsetExpressionT SetOffsetExpressionWide
+#define GetRunningProcessSystemIdByExecutableNameT GetRunningProcessSystemIdByExecutableNameWide
+#define GetRunningProcessDescriptionT GetRunningProcessDescriptionWide
+#define CreateProcessT CreateProcessWide
+#define CreateProcessAndAttachT CreateProcessAndAttachWide
+#define AddDumpInformationFileT AddDumpInformationFileWide
+#define GetDumpFileT GetDumpFileWide
+#define AttachKernelT AttachKernelWide
+#define GetKernelConnectionOptionsT GetKernelConnectionOptionsWide
+#define SetKernelConnectionOptionsT SetKernelConnectionOptionsWide
+#define StartProcessServerT StartProcessServerWide
+#define ConnectProcessServerT ConnectProcessServerWide
+#define StartServerT StartServerWide
+#define OutputServersT OutputServersWide
+#define GetOutputCallbacksT GetOutputCallbacksWide
+#define SetOutputCallbacksT SetOutputCallbacksWide
+#define GetOutputLinePrefixT GetOutputLinePrefixWide
+#define SetOutputLinePrefixT SetOutputLinePrefixWide
+#define GetIdentityT GetIdentityWide
+#define OutputIdentityT OutputIdentityWide
+#define GetEventCallbacksT GetEventCallbacksWide
+#define SetEventCallbacksT SetEventCallbacksWide
+#define CreateProcess2T CreateProcess2Wide
+#define CreateProcessAndAttach2T CreateProcessAndAttach2Wide
+#define PushOutputLinePrefixT PushOutputLinePrefixWide
+#define GetQuitLockStringT GetQuitLockStringWide
+#define SetQuitLockStringT SetQuitLockStringWide
+#define GetLogFileT GetLogFileWide
+#define OpenLogFileT OpenLogFileWide
+#define InputT InputWide
+#define ReturnInputT ReturnInputWide
+#define OutputT OutputWide
+#define OutputVaListT OutputVaListWide
+#define ControlledOutputT ControlledOutputWide
+#define ControlledOutputVaListT ControlledOutputVaListWide
+#define OutputPromptT OutputPromptWide
+#define OutputPromptVaListT OutputPromptVaListWide
+#define GetPromptTextT GetPromptTextWide
+#define AssembleT AssembleWide
+#define DisassembleT DisassembleWide
+#define GetProcessorTypeNamesT GetProcessorTypeNamesWide
+#define GetTextMacroT GetTextMacroWide
+#define SetTextMacroT SetTextMacroWide
+#define EvaluateT EvaluateWide
+#define ExecuteT ExecuteWide
+#define ExecuteCommandFileT ExecuteCommandFileWide
+#define AddExtensionT AddExtensionWide
+#define GetExtensionByPathT GetExtensionByPathWide
+#define CallExtensionT CallExtensionWide
+#define GetExtensionFunctionT GetExtensionFunctionWide
+#define GetEventFilterTextT GetEventFilterTextWide
+#define GetEventFilterCommandT GetEventFilterCommandWide
+#define SetEventFilterCommandT SetEventFilterCommandWide
+#define GetSpecificFilterArgumentT GetSpecificFilterArgumentWide
+#define SetSpecificFilterArgumentT SetSpecificFilterArgumentWide
+#define GetExceptionFilterSecondCommandT GetExceptionFilterSecondCommandWide
+#define SetExceptionFilterSecondCommandT SetExceptionFilterSecondCommandWide
+#define GetLastEventInformationT GetLastEventInformationWide
+#define GetTextReplacementT GetTextReplacementWide
+#define SetTextReplacementT SetTextReplacementWide
+#define SetExpressionSyntaxByNameT SetExpressionSyntaxByNameWide
+#define GetExpressionSyntaxNamesT GetExpressionSyntaxNamesWide
+#define GetEventIndexDescriptionT GetEventIndexDescriptionWide
+#define GetLogFile2T GetLogFile2Wide
+#define OpenLogFile2T OpenLogFile2Wide
+#define GetSystemVersionStringT GetSystemVersionStringWide
+#define ReadMultiByteStringVirtualT ReadMultiByteStringVirtualWide
+#define ReadUnicodeStringVirtualT ReadUnicodeStringVirtualWide
+#define GetDescriptionT GetDescriptionWide
+#define GetIndexByNameT GetIndexByNameWide
+#define GetPseudoDescriptionT GetPseudoDescriptionWide
+#define GetPseudoIndexByNameT GetPseudoIndexByNameWide
+#define AddSymbolT AddSymbolWide
+#define RemoveSymbolByNameT RemoveSymbolByNameWide
+#define GetSymbolNameT GetSymbolNameWide
+#define WriteSymbolT WriteSymbolWide
+#define OutputAsTypeT OutputAsTypeWide
+#define GetSymbolTypeNameT GetSymbolTypeNameWide
+#define GetSymbolValueTextT GetSymbolValueTextWide
+#define GetNameByOffsetT GetNameByOffsetWide
+#define GetOffsetByNameT GetOffsetByNameWide
+#define GetNearNameByOffsetT GetNearNameByOffsetWide
+#define GetLineByOffsetT GetLineByOffsetWide
+#define GetOffsetByLineT GetOffsetByLineWide
+#define GetModuleByModuleNameT GetModuleByModuleNameWide
+#define GetModuleByModuleName2T GetModuleByModuleName2Wide
+#define GetSymbolModuleT GetSymbolModuleWide
+#define GetTypeNameT GetTypeNameWide
+#define GetTypeIdT GetTypeIdWide
+#define GetFieldOffsetT GetFieldOffsetWide
+#define GetSymbolTypeIdT GetSymbolTypeIdWide
+#define StartSymbolMatchT StartSymbolMatchWide
+#define GetNextSymbolMatchT GetNextSymbolMatchWide
+#define ReloadT ReloadWide
+#define GetSymbolPathT GetSymbolPathWide
+#define SetSymbolPathT SetSymbolPathWide
+#define AppendSymbolPathT AppendSymbolPathWide
+#define GetImagePathT GetImagePathWide
+#define SetImagePathT SetImagePathWide
+#define AppendImagePathT AppendImagePathWide
+#define GetSourcePathT GetSourcePathWide
+#define GetSourcePathElementT GetSourcePathElementWide
+#define SetSourcePathT SetSourcePathWide
+#define AppendSourcePathT AppendSourcePathWide
+#define FindSourceFileT FindSourceFileWide
+#define GetSourceFileLineOffsetsT GetSourceFileLineOffsetsWide
+#define GetModuleVersionInformationT GetModuleVersionInformationWide
+#define GetModuleNameStringT GetModuleNameStringWide
+#define GetConstantNameT GetConstantNameWide
+#define GetFieldNameT GetFieldNameWide
+#define GetFieldTypeAndOffsetT GetFieldTypeAndOffsetWide
+#define GetSymbolEntriesByNameT GetSymbolEntriesByNameWide
+#define GetSymbolEntryStringT GetSymbolEntryStringWide
+#define GetSourceEntriesByLineT GetSourceEntriesByLineWide
+#define GetSourceEntryStringT GetSourceEntryStringWide
+#define GetCurrentProcessExecutableNameT GetCurrentProcessExecutableNameWide
+#define GetCurrentSystemServerNameT GetCurrentSystemServerNameWide
+
+#else // #ifdef UNICODE
+
+#define IDebugEventCallbacksT IDebugEventCallbacks
+#define IID_IDebugEventCallbacksT IID_IDebugEventCallbacks
+#define IDebugOutputCallbacksT IDebugOutputCallbacks
+#define IID_IDebugOutputCallbacksT IID_IDebugOutputCallbacks
+#define DebugBaseEventCallbacksT DebugBaseEventCallbacks
+
+#define DebugConnectT DebugConnect
+#define GetSourceFileInformationT GetSourceFileInformation
+#define FindSourceFileAndTokenT FindSourceFileAndToken
+#define GetSymbolInformationT GetSymbolInformation
+#define GetCommandT GetCommand
+#define SetCommandT SetCommand
+#define GetOffsetExpressionT GetOffsetExpression
+#define SetOffsetExpressionT SetOffsetExpression
+#define GetRunningProcessSystemIdByExecutableNameT GetRunningProcessSystemIdByExecutableName
+#define GetRunningProcessDescriptionT GetRunningProcessDescription
+#define CreateProcessT CreateProcess
+#define CreateProcessAndAttachT CreateProcessAndAttach
+#define AddDumpInformationFileT AddDumpInformationFile
+#define GetDumpFileT GetDumpFile
+#define AttachKernelT AttachKernel
+#define GetKernelConnectionOptionsT GetKernelConnectionOptions
+#define SetKernelConnectionOptionsT SetKernelConnectionOptions
+#define StartProcessServerT StartProcessServer
+#define ConnectProcessServerT ConnectProcessServer
+#define StartServerT StartServer
+#define OutputServersT OutputServers
+#define GetOutputCallbacksT GetOutputCallbacks
+#define SetOutputCallbacksT SetOutputCallbacks
+#define GetOutputLinePrefixT GetOutputLinePrefix
+#define SetOutputLinePrefixT SetOutputLinePrefix
+#define GetIdentityT GetIdentity
+#define OutputIdentityT OutputIdentity
+#define GetEventCallbacksT GetEventCallbacks
+#define SetEventCallbacksT SetEventCallbacks
+#define CreateProcess2T CreateProcess2
+#define CreateProcessAndAttach2T CreateProcessAndAttach2
+#define PushOutputLinePrefixT PushOutputLinePrefix
+#define GetQuitLockStringT GetQuitLockString
+#define SetQuitLockStringT SetQuitLockString
+#define GetLogFileT GetLogFile
+#define OpenLogFileT OpenLogFile
+#define InputT Input
+#define ReturnInputT ReturnInput
+#define OutputT Output
+#define OutputVaListT OutputVaList
+#define ControlledOutputT ControlledOutput
+#define ControlledOutputVaListT ControlledOutputVaList
+#define OutputPromptT OutputPrompt
+#define OutputPromptVaListT OutputPromptVaList
+#define GetPromptTextT GetPromptText
+#define AssembleT Assemble
+#define DisassembleT Disassemble
+#define GetProcessorTypeNamesT GetProcessorTypeNames
+#define GetTextMacroT GetTextMacro
+#define SetTextMacroT SetTextMacro
+#define EvaluateT Evaluate
+#define ExecuteT Execute
+#define ExecuteCommandFileT ExecuteCommandFile
+#define AddExtensionT AddExtension
+#define GetExtensionByPathT GetExtensionByPath
+#define CallExtensionT CallExtension
+#define GetExtensionFunctionT GetExtensionFunction
+#define GetEventFilterTextT GetEventFilterText
+#define GetEventFilterCommandT GetEventFilterCommand
+#define SetEventFilterCommandT SetEventFilterCommand
+#define GetSpecificFilterArgumentT GetSpecificFilterArgument
+#define SetSpecificFilterArgumentT SetSpecificFilterArgument
+#define GetExceptionFilterSecondCommandT GetExceptionFilterSecondCommand
+#define SetExceptionFilterSecondCommandT SetExceptionFilterSecondCommand
+#define GetLastEventInformationT GetLastEventInformation
+#define GetTextReplacementT GetTextReplacement
+#define SetTextReplacementT SetTextReplacement
+#define SetExpressionSyntaxByNameT SetExpressionSyntaxByName
+#define GetExpressionSyntaxNamesT GetExpressionSyntaxNames
+#define GetEventIndexDescriptionT GetEventIndexDescription
+#define GetLogFile2T GetLogFile2
+#define OpenLogFile2T OpenLogFile2
+#define GetSystemVersionStringT GetSystemVersionString
+#define ReadMultiByteStringVirtualT ReadMultiByteStringVirtual
+#define ReadUnicodeStringVirtualT ReadUnicodeStringVirtual
+#define GetDescriptionT GetDescription
+#define GetIndexByNameT GetIndexByName
+#define GetPseudoDescriptionT GetPseudoDescription
+#define GetPseudoIndexByNameT GetPseudoIndexByName
+#define AddSymbolT AddSymbol
+#define RemoveSymbolByNameT RemoveSymbolByName
+#define GetSymbolNameT GetSymbolName
+#define WriteSymbolT WriteSymbol
+#define OutputAsTypeT OutputAsType
+#define GetSymbolTypeNameT GetSymbolTypeName
+#define GetSymbolValueTextT GetSymbolValueText
+#define GetNameByOffsetT GetNameByOffset
+#define GetOffsetByNameT GetOffsetByName
+#define GetNearNameByOffsetT GetNearNameByOffset
+#define GetLineByOffsetT GetLineByOffset
+#define GetOffsetByLineT GetOffsetByLine
+#define GetModuleByModuleNameT GetModuleByModuleName
+#define GetModuleByModuleName2T GetModuleByModuleName2
+#define GetSymbolModuleT GetSymbolModule
+#define GetTypeNameT GetTypeName
+#define GetTypeIdT GetTypeId
+#define GetFieldOffsetT GetFieldOffset
+#define GetSymbolTypeIdT GetSymbolTypeId
+#define StartSymbolMatchT StartSymbolMatch
+#define GetNextSymbolMatchT GetNextSymbolMatch
+#define ReloadT Reload
+#define GetSymbolPathT GetSymbolPath
+#define SetSymbolPathT SetSymbolPath
+#define AppendSymbolPathT AppendSymbolPath
+#define GetImagePathT GetImagePath
+#define SetImagePathT SetImagePath
+#define AppendImagePathT AppendImagePath
+#define GetSourcePathT GetSourcePath
+#define GetSourcePathElementT GetSourcePathElement
+#define SetSourcePathT SetSourcePath
+#define AppendSourcePathT AppendSourcePath
+#define FindSourceFileT FindSourceFile
+#define GetSourceFileLineOffsetsT GetSourceFileLineOffsets
+#define GetModuleVersionInformationT GetModuleVersionInformation
+#define GetModuleNameStringT GetModuleNameString
+#define GetConstantNameT GetConstantName
+#define GetFieldNameT GetFieldName
+#define GetFieldTypeAndOffsetT GetFieldTypeAndOffset
+#define GetSymbolEntriesByNameT GetSymbolEntriesByName
+#define GetSymbolEntryStringT GetSymbolEntryString
+#define GetSourceEntriesByLineT GetSourceEntriesByLine
+#define GetSourceEntryStringT GetSourceEntryString
+#define GetCurrentProcessExecutableNameT GetCurrentProcessExecutableName
+#define GetCurrentSystemServerNameT GetCurrentSystemServerName
+
+#endif // #ifdef UNICODE
+
+#endif // #ifdef DEBUG_UNICODE_MACROS
+
+#endif // #ifdef __cplusplus
+
+#endif // #ifndef __DBGENG_H__
diff --git a/src/ToolBox/SOS/Strike/inc/dbghelp.h b/src/ToolBox/SOS/Strike/inc/dbghelp.h
new file mode 100644
index 0000000000..8075929bf0
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/inc/dbghelp.h
@@ -0,0 +1,4540 @@
+// 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.
+
+/*++ BUILD Version: 0000 Increment this if a change has global effects
+
+
+
+Module Name:
+
+ dbghelp.h
+
+Abstract:
+
+ This module defines the prototypes and constants required for the image
+ help routines.
+
+ Contains debugging support routines that are redistributable.
+
+Revision History:
+
+--*/
+
+#ifndef _DBGHELP_
+#define _DBGHELP_
+
+#if _MSC_VER > 1020
+#pragma once
+#endif
+
+
+// As a general principal always call the 64 bit version
+// of every API, if a choice exists. The 64 bit version
+// works great on 32 bit platforms, and is forward
+// compatible to 64 bit platforms.
+
+#ifdef _WIN64
+#ifndef _IMAGEHLP64
+#define _IMAGEHLP64
+#endif
+#endif
+
+// For those without specstrings.h
+// Since there are different versions of this header, I need to
+// individually test each item and define it if it is not around.
+
+#ifndef __in
+ #define __in
+#endif
+#ifndef __out
+ #define __out
+#endif
+#ifndef __inout
+ #define __inout
+#endif
+#ifndef __in_opt
+ #define __in_opt
+#endif
+#ifndef __out_opt
+ #define __out_opt
+#endif
+#ifndef __inout_opt
+ #define __inout_opt
+#endif
+#ifndef __in_ecount
+ #define __in_ecount(x)
+#endif
+#ifndef __out_ecount
+ #define __out_ecount(x)
+#endif
+#ifndef __inout_ecount
+ #define __inout_ecount(x)
+#endif
+#ifndef __in_bcount
+ #define __in_bcount(x)
+#endif
+#ifndef __out_bcount
+ #define __out_bcount(x)
+#endif
+#ifndef __inout_bcount
+ #define __inout_bcount(x)
+#endif
+#ifndef __out_xcount
+ #define __out_xcount(x)
+#endif
+#ifndef __deref_opt_out
+ #define __deref_opt_out
+#endif
+#ifndef __deref_out
+ #define __deref_out
+#endif
+#ifndef __out_ecount_opt
+ #define __out_ecount_opt(x)
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _IMAGEHLP_SOURCE_
+ #define IMAGEAPI __stdcall
+ #define DBHLP_DEPRECIATED
+#else
+ #define IMAGEAPI DECLSPEC_IMPORT __stdcall
+ #if (_MSC_VER >= 1300) && !defined(MIDL_PASS)
+ #define DBHLP_DEPRECIATED __declspec(deprecated)
+ #else
+ #define DBHLP_DEPRECIATED
+ #endif
+#endif
+
+#define DBHLPAPI IMAGEAPI
+
+#define IMAGE_SEPARATION (64*1024)
+
+// Observant readers may notice that 2 new fields,
+// 'fReadOnly' and 'Version' have been added to
+// the LOADED_IMAGE structure after 'fDOSImage'.
+// This does not change the size of the structure
+// from previous headers. That is because while
+// 'fDOSImage' is a byte, it is padded by the
+// compiler to 4 bytes. So the 2 new fields are
+// slipped into the extra space.
+
+typedef struct _LOADED_IMAGE {
+ PSTR ModuleName;
+ HANDLE hFile;
+ PUCHAR MappedAddress;
+#ifdef _IMAGEHLP64
+ PIMAGE_NT_HEADERS64 FileHeader;
+#else
+ PIMAGE_NT_HEADERS32 FileHeader;
+#endif
+ PIMAGE_SECTION_HEADER LastRvaSection;
+ ULONG NumberOfSections;
+ PIMAGE_SECTION_HEADER Sections;
+ ULONG Characteristics;
+ BOOLEAN fSystemImage;
+ BOOLEAN fDOSImage;
+ BOOLEAN fReadOnly;
+ UCHAR Version;
+ LIST_ENTRY Links;
+ ULONG SizeOfImage;
+} LOADED_IMAGE, *PLOADED_IMAGE;
+
+#define MAX_SYM_NAME 2000
+
+
+// Error codes set by dbghelp functions. Call GetLastError
+// to see them.
+// Dbghelp also sets error codes found in winerror.h
+
+#define ERROR_IMAGE_NOT_STRIPPED 0x8800 // the image is not stripped. No dbg file available.
+#define ERROR_NO_DBG_POINTER 0x8801 // image is stripped but there is no pointer to a dbg file
+#define ERROR_NO_PDB_POINTER 0x8802 // image does not point to a pdb file
+
+typedef BOOL
+(CALLBACK *PFIND_DEBUG_FILE_CALLBACK)(
+ __in HANDLE FileHandle,
+ __in PCSTR FileName,
+ __in PVOID CallerData
+ );
+
+HANDLE
+IMAGEAPI
+SymFindDebugInfoFile(
+ __in HANDLE hProcess,
+ __in PCSTR FileName,
+ __out_ecount(MAX_PATH + 1) PSTR DebugFilePath,
+ __in_opt PFIND_DEBUG_FILE_CALLBACK Callback,
+ __in_opt PVOID CallerData
+ );
+
+typedef BOOL
+(CALLBACK *PFIND_DEBUG_FILE_CALLBACKW)(
+ __in HANDLE FileHandle,
+ __in PCWSTR FileName,
+ __in PVOID CallerData
+ );
+
+HANDLE
+IMAGEAPI
+SymFindDebugInfoFileW(
+ __in HANDLE hProcess,
+ __in PCWSTR FileName,
+ __out_ecount(MAX_PATH + 1) PWSTR DebugFilePath,
+ __in_opt PFIND_DEBUG_FILE_CALLBACKW Callback,
+ __in_opt PVOID CallerData
+ );
+
+HANDLE
+IMAGEAPI
+FindDebugInfoFile (
+ __in PCSTR FileName,
+ __in PCSTR SymbolPath,
+ __out_ecount(MAX_PATH + 1) PSTR DebugFilePath
+ );
+
+HANDLE
+IMAGEAPI
+FindDebugInfoFileEx (
+ __in PCSTR FileName,
+ __in PCSTR SymbolPath,
+ __out_ecount(MAX_PATH + 1) PSTR DebugFilePath,
+ __in_opt PFIND_DEBUG_FILE_CALLBACK Callback,
+ __in_opt PVOID CallerData
+ );
+
+HANDLE
+IMAGEAPI
+FindDebugInfoFileExW (
+ __in PCWSTR FileName,
+ __in PCWSTR SymbolPath,
+ __out_ecount(MAX_PATH + 1) PWSTR DebugFilePath,
+ __in_opt PFIND_DEBUG_FILE_CALLBACKW Callback,
+ __in_opt PVOID CallerData
+ );
+
+typedef BOOL
+(CALLBACK *PFINDFILEINPATHCALLBACK)(
+ PCSTR filename,
+ PVOID context
+ );
+
+BOOL
+IMAGEAPI
+SymFindFileInPath(
+ __in HANDLE hprocess,
+ __in_opt PCSTR SearchPath,
+ __in PCSTR FileName,
+ __in_opt PVOID id,
+ __in DWORD two,
+ __in DWORD three,
+ __in DWORD flags,
+ __out_ecount(MAX_PATH + 1) PSTR FoundFile,
+ __in_opt PFINDFILEINPATHCALLBACK callback,
+ __in_opt PVOID context
+ );
+
+typedef BOOL
+(CALLBACK *PFINDFILEINPATHCALLBACKW)(
+ __in PCWSTR filename,
+ __in PVOID context
+ );
+
+BOOL
+IMAGEAPI
+SymFindFileInPathW(
+ __in HANDLE hprocess,
+ __in_opt PCWSTR SearchPath,
+ __in PCWSTR FileName,
+ __in_opt PVOID id,
+ __in DWORD two,
+ __in DWORD three,
+ __in DWORD flags,
+ __out_ecount(MAX_PATH + 1) PWSTR FoundFile,
+ __in_opt PFINDFILEINPATHCALLBACKW callback,
+ __in_opt PVOID context
+ );
+
+typedef BOOL
+(CALLBACK *PFIND_EXE_FILE_CALLBACK)(
+ __in HANDLE FileHandle,
+ __in PCSTR FileName,
+ __in_opt PVOID CallerData
+ );
+
+HANDLE
+IMAGEAPI
+SymFindExecutableImage(
+ __in HANDLE hProcess,
+ __in PCSTR FileName,
+ __out_ecount(MAX_PATH + 1) PSTR ImageFilePath,
+ __in PFIND_EXE_FILE_CALLBACK Callback,
+ __in PVOID CallerData
+ );
+
+typedef BOOL
+(CALLBACK *PFIND_EXE_FILE_CALLBACKW)(
+ __in HANDLE FileHandle,
+ __in PCWSTR FileName,
+ __in_opt PVOID CallerData
+ );
+
+HANDLE
+IMAGEAPI
+SymFindExecutableImageW(
+ __in HANDLE hProcess,
+ __in PCWSTR FileName,
+ __out_ecount(MAX_PATH + 1) PWSTR ImageFilePath,
+ __in PFIND_EXE_FILE_CALLBACKW Callback,
+ __in PVOID CallerData
+ );
+
+HANDLE
+IMAGEAPI
+FindExecutableImage(
+ __in PCSTR FileName,
+ __in PCSTR SymbolPath,
+ __out_ecount(MAX_PATH + 1) PSTR ImageFilePath
+ );
+
+HANDLE
+IMAGEAPI
+FindExecutableImageEx(
+ __in PCSTR FileName,
+ __in PCSTR SymbolPath,
+ __out_ecount(MAX_PATH + 1) PSTR ImageFilePath,
+ __in_opt PFIND_EXE_FILE_CALLBACK Callback,
+ __in_opt PVOID CallerData
+ );
+
+HANDLE
+IMAGEAPI
+FindExecutableImageExW(
+ __in PCWSTR FileName,
+ __in PCWSTR SymbolPath,
+ __out_ecount(MAX_PATH + 1) PWSTR ImageFilePath,
+ __in_opt PFIND_EXE_FILE_CALLBACKW Callback,
+ __in PVOID CallerData
+ );
+
+PIMAGE_NT_HEADERS
+IMAGEAPI
+ImageNtHeader (
+ __in PVOID Base
+ );
+
+PVOID
+IMAGEAPI
+ImageDirectoryEntryToDataEx (
+ __in PVOID Base,
+ __in BOOLEAN MappedAsImage,
+ __in USHORT DirectoryEntry,
+ __out PULONG Size,
+ __out_opt PIMAGE_SECTION_HEADER *FoundHeader
+ );
+
+PVOID
+IMAGEAPI
+ImageDirectoryEntryToData (
+ __in PVOID Base,
+ __in BOOLEAN MappedAsImage,
+ __in USHORT DirectoryEntry,
+ __out PULONG Size
+ );
+
+PIMAGE_SECTION_HEADER
+IMAGEAPI
+ImageRvaToSection(
+ __in PIMAGE_NT_HEADERS NtHeaders,
+ __in PVOID Base,
+ __in ULONG Rva
+ );
+
+PVOID
+IMAGEAPI
+ImageRvaToVa(
+ __in PIMAGE_NT_HEADERS NtHeaders,
+ __in PVOID Base,
+ __in ULONG Rva,
+ __in_opt OUT PIMAGE_SECTION_HEADER *LastRvaSection
+ );
+
+#ifndef _WIN64
+// This api won't be ported to Win64 - Fix your code.
+
+typedef struct _IMAGE_DEBUG_INFORMATION {
+ LIST_ENTRY List;
+ DWORD ReservedSize;
+ PVOID ReservedMappedBase;
+ USHORT ReservedMachine;
+ USHORT ReservedCharacteristics;
+ DWORD ReservedCheckSum;
+ DWORD ImageBase;
+ DWORD SizeOfImage;
+
+ DWORD ReservedNumberOfSections;
+ PIMAGE_SECTION_HEADER ReservedSections;
+
+ DWORD ReservedExportedNamesSize;
+ PSTR ReservedExportedNames;
+
+ DWORD ReservedNumberOfFunctionTableEntries;
+ PIMAGE_FUNCTION_ENTRY ReservedFunctionTableEntries;
+ DWORD ReservedLowestFunctionStartingAddress;
+ DWORD ReservedHighestFunctionEndingAddress;
+
+ DWORD ReservedNumberOfFpoTableEntries;
+ PFPO_DATA ReservedFpoTableEntries;
+
+ DWORD SizeOfCoffSymbols;
+ PIMAGE_COFF_SYMBOLS_HEADER CoffSymbols;
+
+ DWORD ReservedSizeOfCodeViewSymbols;
+ PVOID ReservedCodeViewSymbols;
+
+ PSTR ImageFilePath;
+ PSTR ImageFileName;
+ PSTR ReservedDebugFilePath;
+
+ DWORD ReservedTimeDateStamp;
+
+ BOOL ReservedRomImage;
+ PIMAGE_DEBUG_DIRECTORY ReservedDebugDirectory;
+ DWORD ReservedNumberOfDebugDirectories;
+
+ DWORD ReservedOriginalFunctionTableBaseAddress;
+
+ DWORD Reserved[ 2 ];
+
+} IMAGE_DEBUG_INFORMATION, *PIMAGE_DEBUG_INFORMATION;
+
+
+PIMAGE_DEBUG_INFORMATION
+IMAGEAPI
+MapDebugInformation(
+ __in_opt HANDLE FileHandle,
+ __in PCSTR FileName,
+ __in_opt PCSTR SymbolPath,
+ __in ULONG ImageBase
+ );
+
+BOOL
+IMAGEAPI
+UnmapDebugInformation(
+ __out_xcount(unknown) PIMAGE_DEBUG_INFORMATION DebugInfo
+ );
+
+#endif
+
+BOOL
+IMAGEAPI
+SearchTreeForFile(
+ __in PCSTR RootPath,
+ __in PCSTR InputPathName,
+ __out_ecount(MAX_PATH + 1) PSTR OutputPathBuffer
+ );
+
+BOOL
+IMAGEAPI
+SearchTreeForFileW(
+ __in PCWSTR RootPath,
+ __in PCWSTR InputPathName,
+ __out_ecount(MAX_PATH + 1) PWSTR OutputPathBuffer
+ );
+
+typedef BOOL
+(CALLBACK *PENUMDIRTREE_CALLBACK)(
+ __in PCSTR FilePath,
+ __in_opt PVOID CallerData
+ );
+
+BOOL
+IMAGEAPI
+EnumDirTree(
+ __in_opt HANDLE hProcess,
+ __in PCSTR RootPath,
+ __in PCSTR InputPathName,
+ __out_ecount_opt(MAX_PATH + 1) PSTR OutputPathBuffer,
+ __in_opt PENUMDIRTREE_CALLBACK cb,
+ __in_opt PVOID data
+ );
+
+typedef BOOL
+(CALLBACK *PENUMDIRTREE_CALLBACKW)(
+ __in PCWSTR FilePath,
+ __in_opt PVOID CallerData
+ );
+
+BOOL
+IMAGEAPI
+EnumDirTreeW(
+ __in_opt HANDLE hProcess,
+ __in PCWSTR RootPath,
+ __in PCWSTR InputPathName,
+ __out_ecount_opt(MAX_PATH + 1) PWSTR OutputPathBuffer,
+ __in_opt PENUMDIRTREE_CALLBACKW cb,
+ __in_opt PVOID data
+ );
+
+BOOL
+IMAGEAPI
+MakeSureDirectoryPathExists(
+ __in PCSTR DirPath
+ );
+
+//
+// UnDecorateSymbolName Flags
+//
+
+#define UNDNAME_COMPLETE (0x0000) // Enable full undecoration
+#define UNDNAME_NO_LEADING_UNDERSCORES (0x0001) // Remove leading underscores from MS extended keywords
+#define UNDNAME_NO_MS_KEYWORDS (0x0002) // Disable expansion of MS extended keywords
+#define UNDNAME_NO_FUNCTION_RETURNS (0x0004) // Disable expansion of return type for primary declaration
+#define UNDNAME_NO_ALLOCATION_MODEL (0x0008) // Disable expansion of the declaration model
+#define UNDNAME_NO_ALLOCATION_LANGUAGE (0x0010) // Disable expansion of the declaration language specifier
+#define UNDNAME_NO_MS_THISTYPE (0x0020) // NYI Disable expansion of MS keywords on the 'this' type for primary declaration
+#define UNDNAME_NO_CV_THISTYPE (0x0040) // NYI Disable expansion of CV modifiers on the 'this' type for primary declaration
+#define UNDNAME_NO_THISTYPE (0x0060) // Disable all modifiers on the 'this' type
+#define UNDNAME_NO_ACCESS_SPECIFIERS (0x0080) // Disable expansion of access specifiers for members
+#define UNDNAME_NO_THROW_SIGNATURES (0x0100) // Disable expansion of 'throw-signatures' for functions and pointers to functions
+#define UNDNAME_NO_MEMBER_TYPE (0x0200) // Disable expansion of 'static' or 'virtual'ness of members
+#define UNDNAME_NO_RETURN_UDT_MODEL (0x0400) // Disable expansion of MS model for UDT returns
+#define UNDNAME_32_BIT_DECODE (0x0800) // Undecorate 32-bit decorated names
+#define UNDNAME_NAME_ONLY (0x1000) // Crack only the name for primary declaration;
+ // return just [scope::]name. Does expand template params
+#define UNDNAME_NO_ARGUMENTS (0x2000) // Don't undecorate arguments to function
+#define UNDNAME_NO_SPECIAL_SYMS (0x4000) // Don't undecorate special names (v-table, vcall, vector xxx, metatype, etc)
+
+DWORD
+IMAGEAPI
+WINAPI
+UnDecorateSymbolName(
+ __in PCSTR name,
+ __out_ecount(maxStringLength) PSTR outputString,
+ __in DWORD maxStringLength,
+ __in DWORD flags
+ );
+
+DWORD
+IMAGEAPI
+WINAPI
+UnDecorateSymbolNameW(
+ __in PCWSTR name,
+ __out_ecount(maxStringLength) PWSTR outputString,
+ __in DWORD maxStringLength,
+ __in DWORD flags
+ );
+
+//
+// these values are used for synthesized file types
+// that can be passed in as image headers instead of
+// the standard ones from ntimage.h
+//
+
+#define DBHHEADER_DEBUGDIRS 0x1
+#define DBHHEADER_CVMISC 0x2
+
+typedef struct _MODLOAD_DATA {
+ DWORD ssize; // size of this struct
+ DWORD ssig; // signature identifying the passed data
+ PVOID data; // pointer to passed data
+ DWORD size; // size of passed data
+ DWORD flags; // options
+} MODLOAD_DATA, *PMODLOAD_DATA;
+
+typedef struct _MODLOAD_CVMISC {
+ DWORD oCV; // ofset to the codeview record
+ size_t cCV; // size of the codeview record
+ DWORD oMisc; // offset to the misc record
+ size_t cMisc; // size of the misc record
+ DWORD dtImage; // datetime stamp of the image
+ DWORD cImage; // size of the image
+} MODLOAD_CVMISC, *PMODLOAD_CVMISC;
+
+//
+// StackWalking API
+//
+
+typedef enum {
+ AddrMode1616,
+ AddrMode1632,
+ AddrModeReal,
+ AddrModeFlat
+} ADDRESS_MODE;
+
+typedef struct _tagADDRESS64 {
+ DWORD64 Offset;
+ WORD Segment;
+ ADDRESS_MODE Mode;
+} ADDRESS64, *LPADDRESS64;
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define ADDRESS ADDRESS64
+#define LPADDRESS LPADDRESS64
+#else
+typedef struct _tagADDRESS {
+ DWORD Offset;
+ WORD Segment;
+ ADDRESS_MODE Mode;
+} ADDRESS, *LPADDRESS;
+
+__inline
+void
+Address32To64(
+ __in LPADDRESS a32,
+ __out LPADDRESS64 a64
+ )
+{
+ a64->Offset = (ULONG64)(LONG64)(LONG)a32->Offset;
+ a64->Segment = a32->Segment;
+ a64->Mode = a32->Mode;
+}
+
+__inline
+void
+Address64To32(
+ __in LPADDRESS64 a64,
+ __out LPADDRESS a32
+ )
+{
+ a32->Offset = (ULONG)a64->Offset;
+ a32->Segment = a64->Segment;
+ a32->Mode = a64->Mode;
+}
+#endif
+
+//
+// This structure is included in the STACKFRAME structure,
+// and is used to trace through usermode callbacks in a thread's
+// kernel stack. The values must be copied by the kernel debugger
+// from the DBGKD_GET_VERSION and WAIT_STATE_CHANGE packets.
+//
+
+//
+// New KDHELP structure for 64 bit system support.
+// This structure is preferred in new code.
+//
+typedef struct _KDHELP64 {
+
+ //
+ // address of kernel thread object, as provided in the
+ // WAIT_STATE_CHANGE packet.
+ //
+ DWORD64 Thread;
+
+ //
+ // offset in thread object to pointer to the current callback frame
+ // in kernel stack.
+ //
+ DWORD ThCallbackStack;
+
+ //
+ // offset in thread object to pointer to the current callback backing
+ // store frame in kernel stack.
+ //
+ DWORD ThCallbackBStore;
+
+ //
+ // offsets to values in frame:
+ //
+ // address of next callback frame
+ DWORD NextCallback;
+
+ // address of saved frame pointer (if applicable)
+ DWORD FramePointer;
+
+
+ //
+ // Address of the kernel function that calls out to user mode
+ //
+ DWORD64 KiCallUserMode;
+
+ //
+ // Address of the user mode dispatcher function
+ //
+ DWORD64 KeUserCallbackDispatcher;
+
+ //
+ // Lowest kernel mode address
+ //
+ DWORD64 SystemRangeStart;
+
+ //
+ // Address of the user mode exception dispatcher function.
+ // Added in API version 10.
+ //
+ DWORD64 KiUserExceptionDispatcher;
+
+ //
+ // Stack bounds, added in API version 11.
+ //
+ DWORD64 StackBase;
+ DWORD64 StackLimit;
+
+ DWORD64 Reserved[5];
+
+} KDHELP64, *PKDHELP64;
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define KDHELP KDHELP64
+#define PKDHELP PKDHELP64
+#else
+typedef struct _KDHELP {
+
+ //
+ // address of kernel thread object, as provided in the
+ // WAIT_STATE_CHANGE packet.
+ //
+ DWORD Thread;
+
+ //
+ // offset in thread object to pointer to the current callback frame
+ // in kernel stack.
+ //
+ DWORD ThCallbackStack;
+
+ //
+ // offsets to values in frame:
+ //
+ // address of next callback frame
+ DWORD NextCallback;
+
+ // address of saved frame pointer (if applicable)
+ DWORD FramePointer;
+
+ //
+ // Address of the kernel function that calls out to user mode
+ //
+ DWORD KiCallUserMode;
+
+ //
+ // Address of the user mode dispatcher function
+ //
+ DWORD KeUserCallbackDispatcher;
+
+ //
+ // Lowest kernel mode address
+ //
+ DWORD SystemRangeStart;
+
+ //
+ // offset in thread object to pointer to the current callback backing
+ // store frame in kernel stack.
+ //
+ DWORD ThCallbackBStore;
+
+ //
+ // Address of the user mode exception dispatcher function.
+ // Added in API version 10.
+ //
+ DWORD KiUserExceptionDispatcher;
+
+ //
+ // Stack bounds, added in API version 11.
+ //
+ DWORD StackBase;
+ DWORD StackLimit;
+
+ DWORD Reserved[5];
+
+} KDHELP, *PKDHELP;
+
+__inline
+void
+KdHelp32To64(
+ __in PKDHELP p32,
+ __out PKDHELP64 p64
+ )
+{
+ p64->Thread = p32->Thread;
+ p64->ThCallbackStack = p32->ThCallbackStack;
+ p64->NextCallback = p32->NextCallback;
+ p64->FramePointer = p32->FramePointer;
+ p64->KiCallUserMode = p32->KiCallUserMode;
+ p64->KeUserCallbackDispatcher = p32->KeUserCallbackDispatcher;
+ p64->SystemRangeStart = p32->SystemRangeStart;
+ p64->KiUserExceptionDispatcher = p32->KiUserExceptionDispatcher;
+ p64->StackBase = p32->StackBase;
+ p64->StackLimit = p32->StackLimit;
+}
+#endif
+
+typedef struct _tagSTACKFRAME64 {
+ ADDRESS64 AddrPC; // program counter
+ ADDRESS64 AddrReturn; // return address
+ ADDRESS64 AddrFrame; // frame pointer
+ ADDRESS64 AddrStack; // stack pointer
+ ADDRESS64 AddrBStore; // backing store pointer
+ PVOID FuncTableEntry; // pointer to pdata/fpo or NULL
+ DWORD64 Params[4]; // possible arguments to the function
+ BOOL Far; // WOW far call
+ BOOL Virtual; // is this a virtual frame?
+ DWORD64 Reserved[3];
+ KDHELP64 KdHelp;
+} STACKFRAME64, *LPSTACKFRAME64;
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define STACKFRAME STACKFRAME64
+#define LPSTACKFRAME LPSTACKFRAME64
+#else
+typedef struct _tagSTACKFRAME {
+ ADDRESS AddrPC; // program counter
+ ADDRESS AddrReturn; // return address
+ ADDRESS AddrFrame; // frame pointer
+ ADDRESS AddrStack; // stack pointer
+ PVOID FuncTableEntry; // pointer to pdata/fpo or NULL
+ DWORD Params[4]; // possible arguments to the function
+ BOOL Far; // WOW far call
+ BOOL Virtual; // is this a virtual frame?
+ DWORD Reserved[3];
+ KDHELP KdHelp;
+ ADDRESS AddrBStore; // backing store pointer
+} STACKFRAME, *LPSTACKFRAME;
+#endif
+
+
+typedef
+BOOL
+(__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)(
+ __in HANDLE hProcess,
+ __in DWORD64 qwBaseAddress,
+ __out_bcount(nSize) PVOID lpBuffer,
+ __in DWORD nSize,
+ __out LPDWORD lpNumberOfBytesRead
+ );
+
+typedef
+PVOID
+(__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64)(
+ __in HANDLE ahProcess,
+ __in DWORD64 AddrBase
+ );
+
+typedef
+DWORD64
+(__stdcall *PGET_MODULE_BASE_ROUTINE64)(
+ __in HANDLE hProcess,
+ __in DWORD64 Address
+ );
+
+typedef
+DWORD64
+(__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)(
+ __in HANDLE hProcess,
+ __in HANDLE hThread,
+ __in LPADDRESS64 lpaddr
+ );
+
+BOOL
+IMAGEAPI
+StackWalk64(
+ __in DWORD MachineType,
+ __in HANDLE hProcess,
+ __in HANDLE hThread,
+ __inout LPSTACKFRAME64 StackFrame,
+ __inout PVOID ContextRecord,
+ __in_opt PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
+ __in_opt PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
+ __in_opt PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
+ __in_opt PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+
+#define PREAD_PROCESS_MEMORY_ROUTINE PREAD_PROCESS_MEMORY_ROUTINE64
+#define PFUNCTION_TABLE_ACCESS_ROUTINE PFUNCTION_TABLE_ACCESS_ROUTINE64
+#define PGET_MODULE_BASE_ROUTINE PGET_MODULE_BASE_ROUTINE64
+#define PTRANSLATE_ADDRESS_ROUTINE PTRANSLATE_ADDRESS_ROUTINE64
+
+#define StackWalk StackWalk64
+
+#else
+
+typedef
+BOOL
+(__stdcall *PREAD_PROCESS_MEMORY_ROUTINE)(
+ __in HANDLE hProcess,
+ __in DWORD lpBaseAddress,
+ __out_bcount(nSize) PVOID lpBuffer,
+ __in DWORD nSize,
+ __out PDWORD lpNumberOfBytesRead
+ );
+
+typedef
+PVOID
+(__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE)(
+ __in HANDLE hProcess,
+ __in DWORD AddrBase
+ );
+
+typedef
+DWORD
+(__stdcall *PGET_MODULE_BASE_ROUTINE)(
+ __in HANDLE hProcess,
+ __in DWORD Address
+ );
+
+typedef
+DWORD
+(__stdcall *PTRANSLATE_ADDRESS_ROUTINE)(
+ __in HANDLE hProcess,
+ __in HANDLE hThread,
+ __out LPADDRESS lpaddr
+ );
+
+BOOL
+IMAGEAPI
+StackWalk(
+ DWORD MachineType,
+ __in HANDLE hProcess,
+ __in HANDLE hThread,
+ __inout LPSTACKFRAME StackFrame,
+ __inout PVOID ContextRecord,
+ __in_opt PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine,
+ __in_opt PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine,
+ __in_opt PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine,
+ __in_opt PTRANSLATE_ADDRESS_ROUTINE TranslateAddress
+ );
+
+#endif
+
+
+#define API_VERSION_NUMBER 11
+
+typedef struct API_VERSION {
+ USHORT MajorVersion;
+ USHORT MinorVersion;
+ USHORT Revision;
+ USHORT Reserved;
+} API_VERSION, *LPAPI_VERSION;
+
+LPAPI_VERSION
+IMAGEAPI
+ImagehlpApiVersion(
+ VOID
+ );
+
+LPAPI_VERSION
+IMAGEAPI
+ImagehlpApiVersionEx(
+ __in LPAPI_VERSION AppVersion
+ );
+
+DWORD
+IMAGEAPI
+GetTimestampForLoadedLibrary(
+ __in HMODULE Module
+ );
+
+//
+// typedefs for function pointers
+//
+typedef BOOL
+(CALLBACK *PSYM_ENUMMODULES_CALLBACK64)(
+ __in PCSTR ModuleName,
+ __in DWORD64 BaseOfDll,
+ __in_opt PVOID UserContext
+ );
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMMODULES_CALLBACKW64)(
+ __in PCWSTR ModuleName,
+ __in DWORD64 BaseOfDll,
+ __in_opt PVOID UserContext
+ );
+
+typedef BOOL
+(CALLBACK *PENUMLOADED_MODULES_CALLBACK64)(
+ __in PCSTR ModuleName,
+ __in DWORD64 ModuleBase,
+ __in ULONG ModuleSize,
+ __in_opt PVOID UserContext
+ );
+
+typedef BOOL
+(CALLBACK *PENUMLOADED_MODULES_CALLBACKW64)(
+ __in PCWSTR ModuleName,
+ __in DWORD64 ModuleBase,
+ __in ULONG ModuleSize,
+ __in_opt PVOID UserContext
+ );
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMSYMBOLS_CALLBACK64)(
+ __in PCSTR SymbolName,
+ __in DWORD64 SymbolAddress,
+ __in ULONG SymbolSize,
+ __in_opt PVOID UserContext
+ );
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMSYMBOLS_CALLBACK64W)(
+ __in PCWSTR SymbolName,
+ __in DWORD64 SymbolAddress,
+ __in ULONG SymbolSize,
+ __in_opt PVOID UserContext
+ );
+
+typedef BOOL
+(CALLBACK *PSYMBOL_REGISTERED_CALLBACK64)(
+ __in HANDLE hProcess,
+ __in ULONG ActionCode,
+ __in_opt ULONG64 CallbackData,
+ __in_opt ULONG64 UserContext
+ );
+
+typedef
+PVOID
+(CALLBACK *PSYMBOL_FUNCENTRY_CALLBACK)(
+ __in HANDLE hProcess,
+ __in DWORD AddrBase,
+ __in_opt PVOID UserContext
+ );
+
+typedef
+PVOID
+(CALLBACK *PSYMBOL_FUNCENTRY_CALLBACK64)(
+ __in HANDLE hProcess,
+ __in ULONG64 AddrBase,
+ __in ULONG64 UserContext
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+
+#define PSYM_ENUMMODULES_CALLBACK PSYM_ENUMMODULES_CALLBACK64
+#define PSYM_ENUMSYMBOLS_CALLBACK PSYM_ENUMSYMBOLS_CALLBACK64
+#define PSYM_ENUMSYMBOLS_CALLBACKW PSYM_ENUMSYMBOLS_CALLBACK64W
+#define PENUMLOADED_MODULES_CALLBACK PENUMLOADED_MODULES_CALLBACK64
+#define PSYMBOL_REGISTERED_CALLBACK PSYMBOL_REGISTERED_CALLBACK64
+#define PSYMBOL_FUNCENTRY_CALLBACK PSYMBOL_FUNCENTRY_CALLBACK64
+
+#else
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMMODULES_CALLBACK)(
+ __in PCSTR ModuleName,
+ __in ULONG BaseOfDll,
+ __in_opt PVOID UserContext
+ );
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMSYMBOLS_CALLBACK)(
+ __in PCSTR SymbolName,
+ __in ULONG SymbolAddress,
+ __in ULONG SymbolSize,
+ __in_opt PVOID UserContext
+ );
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMSYMBOLS_CALLBACKW)(
+ __in PCWSTR SymbolName,
+ __in ULONG SymbolAddress,
+ __in ULONG SymbolSize,
+ __in_opt PVOID UserContext
+ );
+
+typedef BOOL
+(CALLBACK *PENUMLOADED_MODULES_CALLBACK)(
+ __in PCSTR ModuleName,
+ __in ULONG ModuleBase,
+ __in ULONG ModuleSize,
+ __in_opt PVOID UserContext
+ );
+
+typedef BOOL
+(CALLBACK *PSYMBOL_REGISTERED_CALLBACK)(
+ __in HANDLE hProcess,
+ __in ULONG ActionCode,
+ __in_opt PVOID CallbackData,
+ __in_opt PVOID UserContext
+ );
+
+#endif
+
+
+// values found in SYMBOL_INFO.Tag
+//
+// This was taken from cvconst.h and should
+// not override any values found there.
+//
+// #define _NO_CVCONST_H_ if you don't
+// have access to that file...
+
+#ifdef _NO_CVCONST_H
+
+// DIA enums
+
+enum SymTagEnum
+{
+ SymTagNull,
+ SymTagExe,
+ SymTagCompiland,
+ SymTagCompilandDetails,
+ SymTagCompilandEnv,
+ SymTagFunction,
+ SymTagBlock,
+ SymTagData,
+ SymTagAnnotation,
+ SymTagLabel,
+ SymTagPublicSymbol,
+ SymTagUDT,
+ SymTagEnum,
+ SymTagFunctionType,
+ SymTagPointerType,
+ SymTagArrayType,
+ SymTagBaseType,
+ SymTagTypedef,
+ SymTagBaseClass,
+ SymTagFriend,
+ SymTagFunctionArgType,
+ SymTagFuncDebugStart,
+ SymTagFuncDebugEnd,
+ SymTagUsingNamespace,
+ SymTagVTableShape,
+ SymTagVTable,
+ SymTagCustom,
+ SymTagThunk,
+ SymTagCustomType,
+ SymTagManagedType,
+ SymTagDimension,
+ SymTagMax
+};
+
+#endif
+
+//
+// flags found in SYMBOL_INFO.Flags
+//
+
+#define SYMFLAG_VALUEPRESENT 0x00000001
+#define SYMFLAG_REGISTER 0x00000008
+#define SYMFLAG_REGREL 0x00000010
+#define SYMFLAG_FRAMEREL 0x00000020
+#define SYMFLAG_PARAMETER 0x00000040
+#define SYMFLAG_LOCAL 0x00000080
+#define SYMFLAG_CONSTANT 0x00000100
+#define SYMFLAG_EXPORT 0x00000200
+#define SYMFLAG_FORWARDER 0x00000400
+#define SYMFLAG_FUNCTION 0x00000800
+#define SYMFLAG_VIRTUAL 0x00001000
+#define SYMFLAG_THUNK 0x00002000
+#define SYMFLAG_TLSREL 0x00004000
+#define SYMFLAG_SLOT 0x00008000
+#define SYMFLAG_ILREL 0x00010000
+#define SYMFLAG_METADATA 0x00020000
+#define SYMFLAG_CLR_TOKEN 0x00040000
+
+// this resets SymNext/Prev to the beginning
+// of the module passed in the address field
+
+#define SYMFLAG_RESET 0x80000000
+
+//
+// symbol type enumeration
+//
+typedef enum {
+ SymNone = 0,
+ SymCoff,
+ SymCv,
+ SymPdb,
+ SymExport,
+ SymDeferred,
+ SymSym, // .sym file
+ SymDia,
+ SymVirtual,
+ NumSymTypes
+} SYM_TYPE;
+
+//
+// symbol data structure
+//
+
+typedef struct _IMAGEHLP_SYMBOL64 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_SYMBOL64)
+ DWORD64 Address; // virtual address including dll base address
+ DWORD Size; // estimated size of symbol, can be zero
+ DWORD Flags; // info about the symbols, see the SYMF defines
+ DWORD MaxNameLength; // maximum size of symbol name in 'Name'
+ CHAR Name[1]; // symbol name (null terminated string)
+} IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64;
+
+typedef struct _IMAGEHLP_SYMBOL64_PACKAGE {
+ IMAGEHLP_SYMBOL64 sym;
+ CHAR name[MAX_SYM_NAME + 1];
+} IMAGEHLP_SYMBOL64_PACKAGE, *PIMAGEHLP_SYMBOL64_PACKAGE;
+
+typedef struct _IMAGEHLP_SYMBOLW64 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_SYMBOLW64)
+ DWORD64 Address; // virtual address including dll base address
+ DWORD Size; // estimated size of symbol, can be zero
+ DWORD Flags; // info about the symbols, see the SYMF defines
+ DWORD MaxNameLength; // maximum size of symbol name in 'Name'
+ WCHAR Name[1]; // symbol name (null terminated string)
+} IMAGEHLP_SYMBOLW64, *PIMAGEHLP_SYMBOLW64;
+
+typedef struct _IMAGEHLP_SYMBOLW64_PACKAGE {
+ IMAGEHLP_SYMBOLW64 sym;
+ WCHAR name[MAX_SYM_NAME + 1];
+} IMAGEHLP_SYMBOLW64_PACKAGE, *PIMAGEHLP_SYMBOLW64_PACKAGE;
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+
+ #define IMAGEHLP_SYMBOL IMAGEHLP_SYMBOL64
+ #define PIMAGEHLP_SYMBOL PIMAGEHLP_SYMBOL64
+ #define IMAGEHLP_SYMBOL_PACKAGE IMAGEHLP_SYMBOL64_PACKAGE
+ #define PIMAGEHLP_SYMBOL_PACKAGE PIMAGEHLP_SYMBOL64_PACKAGE
+ #define IMAGEHLP_SYMBOLW IMAGEHLP_SYMBOLW64
+ #define PIMAGEHLP_SYMBOLW PIMAGEHLP_SYMBOLW64
+ #define IMAGEHLP_SYMBOLW_PACKAGE IMAGEHLP_SYMBOLW64_PACKAGE
+ #define PIMAGEHLP_SYMBOLW_PACKAGE PIMAGEHLP_SYMBOLW64_PACKAGE
+
+#else
+
+ typedef struct _IMAGEHLP_SYMBOL {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_SYMBOL)
+ DWORD Address; // virtual address including dll base address
+ DWORD Size; // estimated size of symbol, can be zero
+ DWORD Flags; // info about the symbols, see the SYMF defines
+ DWORD MaxNameLength; // maximum size of symbol name in 'Name'
+ CHAR Name[1]; // symbol name (null terminated string)
+ } IMAGEHLP_SYMBOL, *PIMAGEHLP_SYMBOL;
+
+ typedef struct _IMAGEHLP_SYMBOL_PACKAGE {
+ IMAGEHLP_SYMBOL sym;
+ CHAR name[MAX_SYM_NAME + 1];
+ } IMAGEHLP_SYMBOL_PACKAGE, *PIMAGEHLP_SYMBOL_PACKAGE;
+
+ typedef struct _IMAGEHLP_SYMBOLW {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_SYMBOLW)
+ DWORD Address; // virtual address including dll base address
+ DWORD Size; // estimated size of symbol, can be zero
+ DWORD Flags; // info about the symbols, see the SYMF defines
+ DWORD MaxNameLength; // maximum size of symbol name in 'Name'
+ WCHAR Name[1]; // symbol name (null terminated string)
+ } IMAGEHLP_SYMBOLW, *PIMAGEHLP_SYMBOLW;
+
+ typedef struct _IMAGEHLP_SYMBOLW_PACKAGE {
+ IMAGEHLP_SYMBOLW sym;
+ WCHAR name[MAX_SYM_NAME + 1];
+ } IMAGEHLP_SYMBOLW_PACKAGE, *PIMAGEHLP_SYMBOLW_PACKAGE;
+
+#endif
+
+//
+// module data structure
+//
+
+typedef struct _IMAGEHLP_MODULE64 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64)
+ DWORD64 BaseOfImage; // base load address of module
+ DWORD ImageSize; // virtual size of the loaded module
+ DWORD TimeDateStamp; // date/time stamp from pe header
+ DWORD CheckSum; // checksum from the pe header
+ DWORD NumSyms; // number of symbols in the symbol table
+ SYM_TYPE SymType; // type of symbols loaded
+ CHAR ModuleName[32]; // module name
+ CHAR ImageName[256]; // image name
+ CHAR LoadedImageName[256]; // symbol file name
+ // new elements: 07-Jun-2002
+ CHAR LoadedPdbName[256]; // pdb file name
+ DWORD CVSig; // Signature of the CV record in the debug directories
+ CHAR CVData[MAX_PATH * 3]; // Contents of the CV record
+ DWORD PdbSig; // Signature of PDB
+ GUID PdbSig70; // Signature of PDB (VC 7 and up)
+ DWORD PdbAge; // DBI age of pdb
+ BOOL PdbUnmatched; // loaded an unmatched pdb
+ BOOL DbgUnmatched; // loaded an unmatched dbg
+ BOOL LineNumbers; // we have line number information
+ BOOL GlobalSymbols; // we have internal symbol information
+ BOOL TypeInfo; // we have type information
+ // new elements: 17-Dec-2003
+ BOOL SourceIndexed; // pdb supports source server
+ BOOL Publics; // contains public symbols
+} IMAGEHLP_MODULE64, *PIMAGEHLP_MODULE64;
+
+typedef struct _IMAGEHLP_MODULEW64 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64)
+ DWORD64 BaseOfImage; // base load address of module
+ DWORD ImageSize; // virtual size of the loaded module
+ DWORD TimeDateStamp; // date/time stamp from pe header
+ DWORD CheckSum; // checksum from the pe header
+ DWORD NumSyms; // number of symbols in the symbol table
+ SYM_TYPE SymType; // type of symbols loaded
+ WCHAR ModuleName[32]; // module name
+ WCHAR ImageName[256]; // image name
+ // new elements: 07-Jun-2002
+ WCHAR LoadedImageName[256]; // symbol file name
+ WCHAR LoadedPdbName[256]; // pdb file name
+ DWORD CVSig; // Signature of the CV record in the debug directories
+ WCHAR CVData[MAX_PATH * 3]; // Contents of the CV record
+ DWORD PdbSig; // Signature of PDB
+ GUID PdbSig70; // Signature of PDB (VC 7 and up)
+ DWORD PdbAge; // DBI age of pdb
+ BOOL PdbUnmatched; // loaded an unmatched pdb
+ BOOL DbgUnmatched; // loaded an unmatched dbg
+ BOOL LineNumbers; // we have line number information
+ BOOL GlobalSymbols; // we have internal symbol information
+ BOOL TypeInfo; // we have type information
+ // new elements: 17-Dec-2003
+ BOOL SourceIndexed; // pdb supports source server
+ BOOL Publics; // contains public symbols
+} IMAGEHLP_MODULEW64, *PIMAGEHLP_MODULEW64;
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define IMAGEHLP_MODULE IMAGEHLP_MODULE64
+#define PIMAGEHLP_MODULE PIMAGEHLP_MODULE64
+#define IMAGEHLP_MODULEW IMAGEHLP_MODULEW64
+#define PIMAGEHLP_MODULEW PIMAGEHLP_MODULEW64
+#else
+typedef struct _IMAGEHLP_MODULE {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE)
+ DWORD BaseOfImage; // base load address of module
+ DWORD ImageSize; // virtual size of the loaded module
+ DWORD TimeDateStamp; // date/time stamp from pe header
+ DWORD CheckSum; // checksum from the pe header
+ DWORD NumSyms; // number of symbols in the symbol table
+ SYM_TYPE SymType; // type of symbols loaded
+ CHAR ModuleName[32]; // module name
+ CHAR ImageName[256]; // image name
+ CHAR LoadedImageName[256]; // symbol file name
+} IMAGEHLP_MODULE, *PIMAGEHLP_MODULE;
+
+typedef struct _IMAGEHLP_MODULEW {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE)
+ DWORD BaseOfImage; // base load address of module
+ DWORD ImageSize; // virtual size of the loaded module
+ DWORD TimeDateStamp; // date/time stamp from pe header
+ DWORD CheckSum; // checksum from the pe header
+ DWORD NumSyms; // number of symbols in the symbol table
+ SYM_TYPE SymType; // type of symbols loaded
+ WCHAR ModuleName[32]; // module name
+ WCHAR ImageName[256]; // image name
+ WCHAR LoadedImageName[256]; // symbol file name
+} IMAGEHLP_MODULEW, *PIMAGEHLP_MODULEW;
+#endif
+
+//
+// source file line data structure
+//
+
+typedef struct _IMAGEHLP_LINE64 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_LINE64)
+ PVOID Key; // internal
+ DWORD LineNumber; // line number in file
+ PCHAR FileName; // full filename
+ DWORD64 Address; // first instruction of line
+} IMAGEHLP_LINE64, *PIMAGEHLP_LINE64;
+
+typedef struct _IMAGEHLP_LINEW64 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_LINE64)
+ PVOID Key; // internal
+ DWORD LineNumber; // line number in file
+ PWSTR FileName; // full filename
+ DWORD64 Address; // first instruction of line
+} IMAGEHLP_LINEW64, *PIMAGEHLP_LINEW64;
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define IMAGEHLP_LINE IMAGEHLP_LINE64
+#define PIMAGEHLP_LINE PIMAGEHLP_LINE64
+#else
+typedef struct _IMAGEHLP_LINE {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_LINE)
+ PVOID Key; // internal
+ DWORD LineNumber; // line number in file
+ PCHAR FileName; // full filename
+ DWORD Address; // first instruction of line
+} IMAGEHLP_LINE, *PIMAGEHLP_LINE;
+
+typedef struct _IMAGEHLP_LINEW {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_LINE64)
+ PVOID Key; // internal
+ DWORD LineNumber; // line number in file
+ PCHAR FileName; // full filename
+ DWORD64 Address; // first instruction of line
+} IMAGEHLP_LINEW, *PIMAGEHLP_LINEW;
+#endif
+
+//
+// source file structure
+//
+
+typedef struct _SOURCEFILE {
+ DWORD64 ModBase; // base address of loaded module
+ PCHAR FileName; // full filename of source
+} SOURCEFILE, *PSOURCEFILE;
+
+typedef struct _SOURCEFILEW {
+ DWORD64 ModBase; // base address of loaded module
+ PWSTR FileName; // full filename of source
+} SOURCEFILEW, *PSOURCEFILEW;
+
+//
+// data structures used for registered symbol callbacks
+//
+
+#define CBA_DEFERRED_SYMBOL_LOAD_START 0x00000001
+#define CBA_DEFERRED_SYMBOL_LOAD_COMPLETE 0x00000002
+#define CBA_DEFERRED_SYMBOL_LOAD_FAILURE 0x00000003
+#define CBA_SYMBOLS_UNLOADED 0x00000004
+#define CBA_DUPLICATE_SYMBOL 0x00000005
+#define CBA_READ_MEMORY 0x00000006
+#define CBA_DEFERRED_SYMBOL_LOAD_CANCEL 0x00000007
+#define CBA_SET_OPTIONS 0x00000008
+#define CBA_EVENT 0x00000010
+#define CBA_DEFERRED_SYMBOL_LOAD_PARTIAL 0x00000020
+#define CBA_DEBUG_INFO 0x10000000
+#define CBA_SRCSRV_INFO 0x20000000
+#define CBA_SRCSRV_EVENT 0x40000000
+
+typedef struct _IMAGEHLP_CBA_READ_MEMORY {
+ DWORD64 addr; // address to read from
+ PVOID buf; // buffer to read to
+ DWORD bytes; // amount of bytes to read
+ DWORD *bytesread; // pointer to store amount of bytes read
+} IMAGEHLP_CBA_READ_MEMORY, *PIMAGEHLP_CBA_READ_MEMORY;
+
+enum {
+ sevInfo = 0,
+ sevProblem,
+ sevAttn,
+ sevFatal,
+ sevMax // unused
+};
+
+#define EVENT_SRCSPEW_START 100
+#define EVENT_SRCSPEW 100
+#define EVENT_SRCSPEW_END 199
+
+typedef struct _IMAGEHLP_CBA_EVENT {
+ DWORD severity; // values from sevInfo to sevFatal
+ DWORD code; // numerical code IDs the error
+ PCHAR desc; // may contain a text description of the error
+ PVOID object; // value dependant upon the error code
+} IMAGEHLP_CBA_EVENT, *PIMAGEHLP_CBA_EVENT;
+
+typedef struct _IMAGEHLP_CBA_EVENTW {
+ DWORD severity; // values from sevInfo to sevFatal
+ DWORD code; // numerical code IDs the error
+ PCWSTR desc; // may contain a text description of the error
+ PVOID object; // value dependant upon the error code
+} IMAGEHLP_CBA_EVENTW, *PIMAGEHLP_CBA_EVENTW;
+
+typedef struct _IMAGEHLP_DEFERRED_SYMBOL_LOAD64 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_DEFERRED_SYMBOL_LOAD64)
+ DWORD64 BaseOfImage; // base load address of module
+ DWORD CheckSum; // checksum from the pe header
+ DWORD TimeDateStamp; // date/time stamp from pe header
+ CHAR FileName[MAX_PATH]; // symbols file or image name
+ BOOLEAN Reparse; // load failure reparse
+ HANDLE hFile; // file handle, if passed
+ DWORD Flags; //
+} IMAGEHLP_DEFERRED_SYMBOL_LOAD64, *PIMAGEHLP_DEFERRED_SYMBOL_LOAD64;
+
+typedef struct _IMAGEHLP_DEFERRED_SYMBOL_LOADW64 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_DEFERRED_SYMBOL_LOADW64)
+ DWORD64 BaseOfImage; // base load address of module
+ DWORD CheckSum; // checksum from the pe header
+ DWORD TimeDateStamp; // date/time stamp from pe header
+ WCHAR FileName[MAX_PATH + 1]; // symbols file or image name
+ BOOLEAN Reparse; // load failure reparse
+ HANDLE hFile; // file handle, if passed
+ DWORD Flags; //
+} IMAGEHLP_DEFERRED_SYMBOL_LOADW64, *PIMAGEHLP_DEFERRED_SYMBOL_LOADW64;
+
+#define DSLFLAG_MISMATCHED_PDB 0x1
+#define DSLFLAG_MISMATCHED_DBG 0x2
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define IMAGEHLP_DEFERRED_SYMBOL_LOAD IMAGEHLP_DEFERRED_SYMBOL_LOAD64
+#define PIMAGEHLP_DEFERRED_SYMBOL_LOAD PIMAGEHLP_DEFERRED_SYMBOL_LOAD64
+#else
+typedef struct _IMAGEHLP_DEFERRED_SYMBOL_LOAD {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_DEFERRED_SYMBOL_LOAD)
+ DWORD BaseOfImage; // base load address of module
+ DWORD CheckSum; // checksum from the pe header
+ DWORD TimeDateStamp; // date/time stamp from pe header
+ CHAR FileName[MAX_PATH]; // symbols file or image name
+ BOOLEAN Reparse; // load failure reparse
+ HANDLE hFile; // file handle, if passed
+} IMAGEHLP_DEFERRED_SYMBOL_LOAD, *PIMAGEHLP_DEFERRED_SYMBOL_LOAD;
+#endif
+
+typedef struct _IMAGEHLP_DUPLICATE_SYMBOL64 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_DUPLICATE_SYMBOL64)
+ DWORD NumberOfDups; // number of duplicates in the Symbol array
+ PIMAGEHLP_SYMBOL64 Symbol; // array of duplicate symbols
+ DWORD SelectedSymbol; // symbol selected (-1 to start)
+} IMAGEHLP_DUPLICATE_SYMBOL64, *PIMAGEHLP_DUPLICATE_SYMBOL64;
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define IMAGEHLP_DUPLICATE_SYMBOL IMAGEHLP_DUPLICATE_SYMBOL64
+#define PIMAGEHLP_DUPLICATE_SYMBOL PIMAGEHLP_DUPLICATE_SYMBOL64
+#else
+typedef struct _IMAGEHLP_DUPLICATE_SYMBOL {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_DUPLICATE_SYMBOL)
+ DWORD NumberOfDups; // number of duplicates in the Symbol array
+ PIMAGEHLP_SYMBOL Symbol; // array of duplicate symbols
+ DWORD SelectedSymbol; // symbol selected (-1 to start)
+} IMAGEHLP_DUPLICATE_SYMBOL, *PIMAGEHLP_DUPLICATE_SYMBOL;
+#endif
+
+// If dbghelp ever needs to display graphical UI, it will use this as the parent window.
+
+BOOL
+IMAGEAPI
+SymSetParentWindow(
+ __in HWND hwnd
+ );
+
+PCHAR
+IMAGEAPI
+SymSetHomeDirectory(
+ __in_opt HANDLE hProcess,
+ __in_opt PCSTR dir
+ );
+
+PWSTR
+IMAGEAPI
+SymSetHomeDirectoryW(
+ __in_opt HANDLE hProcess,
+ __in_opt PCWSTR dir
+ );
+
+PCHAR
+IMAGEAPI
+SymGetHomeDirectory(
+ __in DWORD type,
+ __out_ecount(size) PSTR dir,
+ __in size_t size
+ );
+
+PWSTR
+IMAGEAPI
+SymGetHomeDirectoryW(
+ __in DWORD type,
+ __out_ecount(size) PWSTR dir,
+ __in size_t size
+ );
+
+typedef enum {
+ hdBase = 0, // root directory for dbghelp
+ hdSym, // where symbols are stored
+ hdSrc, // where source is stored
+ hdMax // end marker
+};
+
+typedef struct _OMAP {
+ ULONG rva;
+ ULONG rvaTo;
+} OMAP, *POMAP;
+
+BOOL
+IMAGEAPI
+SymGetOmaps(
+ __in HANDLE hProcess,
+ __in DWORD64 BaseOfDll,
+ __out POMAP *OmapTo,
+ __out PDWORD64 cOmapTo,
+ __out POMAP *OmapFrom,
+ __out PDWORD64 cOmapFrom
+ );
+
+//
+// options that are set/returned by SymSetOptions() & SymGetOptions()
+// these are used as a mask
+//
+#define SYMOPT_CASE_INSENSITIVE 0x00000001
+#define SYMOPT_UNDNAME 0x00000002
+#define SYMOPT_DEFERRED_LOADS 0x00000004
+#define SYMOPT_NO_CPP 0x00000008
+#define SYMOPT_LOAD_LINES 0x00000010
+#define SYMOPT_OMAP_FIND_NEAREST 0x00000020
+#define SYMOPT_LOAD_ANYTHING 0x00000040
+#define SYMOPT_IGNORE_CVREC 0x00000080
+#define SYMOPT_NO_UNQUALIFIED_LOADS 0x00000100
+#define SYMOPT_FAIL_CRITICAL_ERRORS 0x00000200
+#define SYMOPT_EXACT_SYMBOLS 0x00000400
+#define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 0x00000800
+#define SYMOPT_IGNORE_NT_SYMPATH 0x00001000
+#define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000
+#define SYMOPT_PUBLICS_ONLY 0x00004000
+#define SYMOPT_NO_PUBLICS 0x00008000
+#define SYMOPT_AUTO_PUBLICS 0x00010000
+#define SYMOPT_NO_IMAGE_SEARCH 0x00020000
+#define SYMOPT_SECURE 0x00040000
+#define SYMOPT_NO_PROMPTS 0x00080000
+#define SYMOPT_OVERWRITE 0x00100000
+#define SYMOPT_IGNORE_IMAGEDIR 0x00200000
+#define SYMOPT_FLAT_DIRECTORY 0x00400000
+#define SYMOPT_FAVOR_COMPRESSED 0x00800000
+#define SYMOPT_ALLOW_ZERO_ADDRESS 0x01000000
+#define SYMOPT_DISABLE_SYMSRV_AUTODETECT 0x02000000
+
+#define SYMOPT_DEBUG 0x80000000
+
+DWORD
+IMAGEAPI
+SymSetOptions(
+ __in DWORD SymOptions
+ );
+
+DWORD
+IMAGEAPI
+SymGetOptions(
+ VOID
+ );
+
+BOOL
+IMAGEAPI
+SymCleanup(
+ __in HANDLE hProcess
+ );
+
+BOOL
+IMAGEAPI
+SymMatchString(
+ __in PCSTR string,
+ __in PCSTR expression,
+ __in BOOL fCase
+ );
+
+BOOL
+IMAGEAPI
+SymMatchStringA(
+ __in PCSTR string,
+ __in PCSTR expression,
+ __in BOOL fCase
+ );
+
+BOOL
+IMAGEAPI
+SymMatchStringW(
+ __in PCWSTR string,
+ __in PCWSTR expression,
+ __in BOOL fCase
+ );
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMSOURCEFILES_CALLBACK)(
+ __in PSOURCEFILE pSourceFile,
+ __in_opt PVOID UserContext
+ );
+
+// for backwards compatibility - don't use this
+#define PSYM_ENUMSOURCFILES_CALLBACK PSYM_ENUMSOURCEFILES_CALLBACK
+
+BOOL
+IMAGEAPI
+SymEnumSourceFiles(
+ __in HANDLE hProcess,
+ __in ULONG64 ModBase,
+ __in_opt PCSTR Mask,
+ __in PSYM_ENUMSOURCEFILES_CALLBACK cbSrcFiles,
+ __in_opt PVOID UserContext
+ );
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMSOURCEFILES_CALLBACKW)(
+ __in PSOURCEFILEW pSourceFile,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymEnumSourceFilesW(
+ __in HANDLE hProcess,
+ __in ULONG64 ModBase,
+ __in_opt PCWSTR Mask,
+ __in PSYM_ENUMSOURCEFILES_CALLBACKW cbSrcFiles,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymEnumerateModules64(
+ __in HANDLE hProcess,
+ __in PSYM_ENUMMODULES_CALLBACK64 EnumModulesCallback,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymEnumerateModulesW64(
+ __in HANDLE hProcess,
+ __in PSYM_ENUMMODULES_CALLBACKW64 EnumModulesCallback,
+ __in_opt PVOID UserContext
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymEnumerateModules SymEnumerateModules64
+#else
+BOOL
+IMAGEAPI
+SymEnumerateModules(
+ __in HANDLE hProcess,
+ __in PSYM_ENUMMODULES_CALLBACK EnumModulesCallback,
+ __in_opt PVOID UserContext
+ );
+#endif
+
+BOOL
+IMAGEAPI
+EnumerateLoadedModulesEx(
+ __in HANDLE hProcess,
+ __in PENUMLOADED_MODULES_CALLBACK64 EnumLoadedModulesCallback,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+EnumerateLoadedModulesExW(
+ __in HANDLE hProcess,
+ __in PENUMLOADED_MODULES_CALLBACKW64 EnumLoadedModulesCallback,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+EnumerateLoadedModules64(
+ __in HANDLE hProcess,
+ __in PENUMLOADED_MODULES_CALLBACK64 EnumLoadedModulesCallback,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+EnumerateLoadedModulesW64(
+ __in HANDLE hProcess,
+ __in PENUMLOADED_MODULES_CALLBACKW64 EnumLoadedModulesCallback,
+ __in_opt PVOID UserContext
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define EnumerateLoadedModules EnumerateLoadedModules64
+#else
+BOOL
+IMAGEAPI
+EnumerateLoadedModules(
+ __in HANDLE hProcess,
+ __in PENUMLOADED_MODULES_CALLBACK EnumLoadedModulesCallback,
+ __in_opt PVOID UserContext
+ );
+#endif
+
+PVOID
+IMAGEAPI
+SymFunctionTableAccess64(
+ __in HANDLE hProcess,
+ __in DWORD64 AddrBase
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymFunctionTableAccess SymFunctionTableAccess64
+#else
+PVOID
+IMAGEAPI
+SymFunctionTableAccess(
+ __in HANDLE hProcess,
+ __in DWORD AddrBase
+ );
+#endif
+
+BOOL
+IMAGEAPI
+SymGetUnwindInfo(
+ __in HANDLE hProcess,
+ __in DWORD64 Address,
+ __out_bcount_opt(*Size) PVOID Buffer,
+ __inout PULONG Size
+ );
+
+BOOL
+IMAGEAPI
+SymGetModuleInfo64(
+ __in HANDLE hProcess,
+ __in DWORD64 qwAddr,
+ __out PIMAGEHLP_MODULE64 ModuleInfo
+ );
+
+BOOL
+IMAGEAPI
+SymGetModuleInfoW64(
+ __in HANDLE hProcess,
+ __in DWORD64 qwAddr,
+ __out PIMAGEHLP_MODULEW64 ModuleInfo
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetModuleInfo SymGetModuleInfo64
+#define SymGetModuleInfoW SymGetModuleInfoW64
+#else
+BOOL
+IMAGEAPI
+SymGetModuleInfo(
+ __in HANDLE hProcess,
+ __in DWORD dwAddr,
+ __out PIMAGEHLP_MODULE ModuleInfo
+ );
+
+BOOL
+IMAGEAPI
+SymGetModuleInfoW(
+ __in HANDLE hProcess,
+ __in DWORD dwAddr,
+ __out PIMAGEHLP_MODULEW ModuleInfo
+ );
+#endif
+
+DWORD64
+IMAGEAPI
+SymGetModuleBase64(
+ __in HANDLE hProcess,
+ __in DWORD64 qwAddr
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetModuleBase SymGetModuleBase64
+#else
+DWORD
+IMAGEAPI
+SymGetModuleBase(
+ __in HANDLE hProcess,
+ __in DWORD dwAddr
+ );
+#endif
+
+typedef struct _SRCCODEINFO {
+ DWORD SizeOfStruct; // set to sizeof(SRCCODEINFO)
+ PVOID Key; // not used
+ DWORD64 ModBase; // base address of module this applies to
+ CHAR Obj[MAX_PATH + 1]; // the object file within the module
+ CHAR FileName[MAX_PATH + 1]; // full filename
+ DWORD LineNumber; // line number in file
+ DWORD64 Address; // first instruction of line
+} SRCCODEINFO, *PSRCCODEINFO;
+
+typedef struct _SRCCODEINFOW {
+ DWORD SizeOfStruct; // set to sizeof(SRCCODEINFO)
+ PVOID Key; // not used
+ DWORD64 ModBase; // base address of module this applies to
+ WCHAR Obj[MAX_PATH + 1]; // the object file within the module
+ WCHAR FileName[MAX_PATH + 1]; // full filename
+ DWORD LineNumber; // line number in file
+ DWORD64 Address; // first instruction of line
+} SRCCODEINFOW, *PSRCCODEINFOW;
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMLINES_CALLBACK)(
+ __in PSRCCODEINFO LineInfo,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymEnumLines(
+ __in HANDLE hProcess,
+ __in ULONG64 Base,
+ __in_opt PCSTR Obj,
+ __in_opt PCSTR File,
+ __in PSYM_ENUMLINES_CALLBACK EnumLinesCallback,
+ __in_opt PVOID UserContext
+ );
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMLINES_CALLBACKW)(
+ __in PSRCCODEINFOW LineInfo,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymEnumLinesW(
+ __in HANDLE hProcess,
+ __in ULONG64 Base,
+ __in_opt PCWSTR Obj,
+ __in_opt PCWSTR File,
+ __in PSYM_ENUMLINES_CALLBACKW EnumLinesCallback,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymGetLineFromAddr64(
+ __in HANDLE hProcess,
+ __in DWORD64 qwAddr,
+ __out PDWORD pdwDisplacement,
+ __out PIMAGEHLP_LINE64 Line64
+ );
+
+BOOL
+IMAGEAPI
+SymGetLineFromAddrW64(
+ __in HANDLE hProcess,
+ __in DWORD64 dwAddr,
+ __out PDWORD pdwDisplacement,
+ __out PIMAGEHLP_LINEW64 Line
+ );
+
+BOOL
+IMAGEAPI
+SymEnumSourceLines(
+ __in HANDLE hProcess,
+ __in ULONG64 Base,
+ __in_opt PCSTR Obj,
+ __in_opt PCSTR File,
+ __in_opt DWORD Line,
+ __in DWORD Flags,
+ __in PSYM_ENUMLINES_CALLBACK EnumLinesCallback,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymEnumSourceLinesW(
+ __in HANDLE hProcess,
+ __in ULONG64 Base,
+ __in_opt PCWSTR Obj,
+ __in_opt PCWSTR File,
+ __in_opt DWORD Line,
+ __in DWORD Flags,
+ __in PSYM_ENUMLINES_CALLBACKW EnumLinesCallback,
+ __in_opt PVOID UserContext
+ );
+
+// flags for SymEnumSourceLines
+
+#define ESLFLAG_FULLPATH 0x1
+#define ESLFLAG_NEAREST 0x2
+#define ESLFLAG_PREV 0x4
+#define ESLFLAG_NEXT 0x8
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetLineFromAddr SymGetLineFromAddr64
+#define SymGetLineFromAddrW SymGetLineFromAddrW64
+#else
+BOOL
+IMAGEAPI
+SymGetLineFromAddr(
+ __in HANDLE hProcess,
+ __in DWORD dwAddr,
+ __out PDWORD pdwDisplacement,
+ __out PIMAGEHLP_LINE Line
+ );
+
+BOOL
+IMAGEAPI
+SymGetLineFromAddrW(
+ __in HANDLE hProcess,
+ __in DWORD dwAddr,
+ __out PDWORD pdwDisplacement,
+ __out PIMAGEHLP_LINEW Line
+ );
+#endif
+
+BOOL
+IMAGEAPI
+SymGetLineFromName64(
+ __in HANDLE hProcess,
+ __in_opt PCSTR ModuleName,
+ __in_opt PCSTR FileName,
+ __in DWORD dwLineNumber,
+ __out PLONG plDisplacement,
+ __inout PIMAGEHLP_LINE64 Line
+ );
+
+BOOL
+IMAGEAPI
+SymGetLineFromNameW64(
+ __in HANDLE hProcess,
+ __in_opt PCWSTR ModuleName,
+ __in_opt PCWSTR FileName,
+ __in DWORD dwLineNumber,
+ __out PLONG plDisplacement,
+ __inout PIMAGEHLP_LINEW64 Line
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetLineFromName SymGetLineFromName64
+#else
+BOOL
+IMAGEAPI
+SymGetLineFromName(
+ __in HANDLE hProcess,
+ __in_opt PCSTR ModuleName,
+ __in_opt PCSTR FileName,
+ __in DWORD dwLineNumber,
+ __out PLONG plDisplacement,
+ __inout PIMAGEHLP_LINE Line
+ );
+#endif
+
+BOOL
+IMAGEAPI
+SymGetLineNext64(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_LINE64 Line
+ );
+
+BOOL
+IMAGEAPI
+SymGetLineNextW64(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_LINEW64 Line
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetLineNext SymGetLineNext64
+#else
+BOOL
+IMAGEAPI
+SymGetLineNext(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_LINE Line
+ );
+
+BOOL
+IMAGEAPI
+SymGetLineNextW(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_LINEW Line
+ );
+#endif
+
+BOOL
+IMAGEAPI
+SymGetLinePrev64(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_LINE64 Line
+ );
+
+BOOL
+IMAGEAPI
+SymGetLinePrevW64(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_LINEW64 Line
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetLinePrev SymGetLinePrev64
+#else
+BOOL
+IMAGEAPI
+SymGetLinePrev(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_LINE Line
+ );
+
+BOOL
+IMAGEAPI
+SymGetLinePrevW(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_LINEW Line
+ );
+#endif
+
+ULONG
+IMAGEAPI
+SymGetFileLineOffsets64(
+ __in HANDLE hProcess,
+ __in_opt PCSTR ModuleName,
+ __in PCSTR FileName,
+ __out_ecount(BufferLines) PDWORD64 Buffer,
+ __in ULONG BufferLines
+ );
+
+BOOL
+IMAGEAPI
+SymMatchFileName(
+ __in PCSTR FileName,
+ __in PCSTR Match,
+ __deref_opt_out PSTR *FileNameStop,
+ __deref_opt_out PSTR *MatchStop
+ );
+
+BOOL
+IMAGEAPI
+SymMatchFileNameW(
+ __in PCWSTR FileName,
+ __in PCWSTR Match,
+ __deref_opt_out PWSTR *FileNameStop,
+ __deref_opt_out PWSTR *MatchStop
+ );
+
+BOOL
+IMAGEAPI
+SymGetSourceFile(
+ __in HANDLE hProcess,
+ __in ULONG64 Base,
+ __in_opt PCSTR Params,
+ __in PCSTR FileSpec,
+ __out_ecount(Size) PSTR FilePath,
+ __in DWORD Size
+ );
+
+BOOL
+IMAGEAPI
+SymGetSourceFileW(
+ __in HANDLE hProcess,
+ __in ULONG64 Base,
+ __in_opt PCWSTR Params,
+ __in PCWSTR FileSpec,
+ __out_ecount(Size) PWSTR FilePath,
+ __in DWORD Size
+ );
+
+BOOL
+IMAGEAPI
+SymGetSourceFileToken(
+ __in HANDLE hProcess,
+ __in ULONG64 Base,
+ __in PCSTR FileSpec,
+ __deref_out PVOID *Token,
+ __out DWORD *Size
+ );
+
+BOOL
+IMAGEAPI
+SymGetSourceFileTokenW(
+ __in HANDLE hProcess,
+ __in ULONG64 Base,
+ __in PCWSTR FileSpec,
+ __deref_out PVOID *Token,
+ __out DWORD *Size
+ );
+
+BOOL
+IMAGEAPI
+SymGetSourceFileFromToken(
+ __in HANDLE hProcess,
+ __in PVOID Token,
+ __in_opt PCSTR Params,
+ __out_ecount(Size) PSTR FilePath,
+ __in DWORD Size
+ );
+
+BOOL
+IMAGEAPI
+SymGetSourceFileFromTokenW(
+ __in HANDLE hProcess,
+ __in PVOID Token,
+ __in_opt PCWSTR Params,
+ __out_ecount(Size) PWSTR FilePath,
+ __in DWORD Size
+ );
+
+BOOL
+IMAGEAPI
+SymGetSourceVarFromToken(
+ __in HANDLE hProcess,
+ __in PVOID Token,
+ __in_opt PCSTR Params,
+ __in PCSTR VarName,
+ __out_ecount(Size) PSTR Value,
+ __in DWORD Size
+ );
+
+BOOL
+IMAGEAPI
+SymGetSourceVarFromTokenW(
+ __in HANDLE hProcess,
+ __in PVOID Token,
+ __in_opt PCWSTR Params,
+ __in PCWSTR VarName,
+ __out_ecount(Size) PWSTR Value,
+ __in DWORD Size
+ );
+
+typedef BOOL (CALLBACK *PENUMSOURCEFILETOKENSCALLBACK)(__in PVOID token, __in size_t size);
+
+BOOL
+IMAGEAPI
+SymEnumSourceFileTokens(
+ __in HANDLE hProcess,
+ __in ULONG64 Base,
+ __in PENUMSOURCEFILETOKENSCALLBACK Callback
+ );
+
+BOOL
+IMAGEAPI
+SymInitialize(
+ __in HANDLE hProcess,
+ __in_opt PCSTR UserSearchPath,
+ __in BOOL fInvadeProcess
+ );
+
+BOOL
+IMAGEAPI
+SymInitializeW(
+ __in HANDLE hProcess,
+ __in_opt PCWSTR UserSearchPath,
+ __in BOOL fInvadeProcess
+ );
+
+BOOL
+IMAGEAPI
+SymGetSearchPath(
+ __in HANDLE hProcess,
+ __out_ecount(SearchPathLength) PSTR SearchPath,
+ __in DWORD SearchPathLength
+ );
+
+BOOL
+IMAGEAPI
+SymGetSearchPathW(
+ __in HANDLE hProcess,
+ __out_ecount(SearchPathLength) PWSTR SearchPath,
+ __in DWORD SearchPathLength
+ );
+
+BOOL
+IMAGEAPI
+SymSetSearchPath(
+ __in HANDLE hProcess,
+ __in_opt PCSTR SearchPath
+ );
+
+BOOL
+IMAGEAPI
+SymSetSearchPathW(
+ __in HANDLE hProcess,
+ __in_opt PCWSTR SearchPath
+ );
+
+#define SLMFLAG_VIRTUAL 0x1
+#define SLMFLAG_ALT_INDEX 0x2
+#define SLMFLAG_NO_SYMBOLS 0x4
+
+DWORD64
+IMAGEAPI
+SymLoadModuleEx(
+ __in HANDLE hProcess,
+ __in_opt HANDLE hFile,
+ __in_opt PCSTR ImageName,
+ __in_opt PCSTR ModuleName,
+ __in DWORD64 BaseOfDll,
+ __in DWORD DllSize,
+ __in_opt PMODLOAD_DATA Data,
+ __in_opt DWORD Flags
+ );
+
+DWORD64
+IMAGEAPI
+SymLoadModuleExW(
+ __in HANDLE hProcess,
+ __in_opt HANDLE hFile,
+ __in_opt PCWSTR ImageName,
+ __in_opt PCWSTR ModuleName,
+ __in DWORD64 BaseOfDll,
+ __in DWORD DllSize,
+ __in_opt PMODLOAD_DATA Data,
+ __in_opt DWORD Flags
+ );
+
+BOOL
+IMAGEAPI
+SymUnloadModule64(
+ __in HANDLE hProcess,
+ __in DWORD64 BaseOfDll
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymUnloadModule SymUnloadModule64
+#else
+BOOL
+IMAGEAPI
+SymUnloadModule(
+ __in HANDLE hProcess,
+ __in DWORD BaseOfDll
+ );
+#endif
+
+BOOL
+IMAGEAPI
+SymUnDName64(
+ __in PIMAGEHLP_SYMBOL64 sym, // Symbol to undecorate
+ __out_ecount(UnDecNameLength) PSTR UnDecName, // Buffer to store undecorated name in
+ __in DWORD UnDecNameLength // Size of the buffer
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymUnDName SymUnDName64
+#else
+BOOL
+IMAGEAPI
+SymUnDName(
+ __in PIMAGEHLP_SYMBOL sym, // Symbol to undecorate
+ __out_ecount(UnDecNameLength) PSTR UnDecName, // Buffer to store undecorated name in
+ __in DWORD UnDecNameLength // Size of the buffer
+ );
+#endif
+
+BOOL
+IMAGEAPI
+SymRegisterCallback64(
+ __in HANDLE hProcess,
+ __in PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
+ __in ULONG64 UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymRegisterCallbackW64(
+ __in HANDLE hProcess,
+ __in PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
+ __in ULONG64 UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymRegisterFunctionEntryCallback64(
+ __in HANDLE hProcess,
+ __in PSYMBOL_FUNCENTRY_CALLBACK64 CallbackFunction,
+ __in ULONG64 UserContext
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymRegisterCallback SymRegisterCallback64
+#define SymRegisterFunctionEntryCallback SymRegisterFunctionEntryCallback64
+#else
+BOOL
+IMAGEAPI
+SymRegisterCallback(
+ __in HANDLE hProcess,
+ __in PSYMBOL_REGISTERED_CALLBACK CallbackFunction,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymRegisterFunctionEntryCallback(
+ __in HANDLE hProcess,
+ __in PSYMBOL_FUNCENTRY_CALLBACK CallbackFunction,
+ __in_opt PVOID UserContext
+ );
+#endif
+
+
+typedef struct _IMAGEHLP_SYMBOL_SRC {
+ DWORD sizeofstruct;
+ DWORD type;
+ char file[MAX_PATH];
+} IMAGEHLP_SYMBOL_SRC, *PIMAGEHLP_SYMBOL_SRC;
+
+typedef struct _MODULE_TYPE_INFO { // AKA TYPTYP
+ USHORT dataLength;
+ USHORT leaf;
+ BYTE data[1];
+} MODULE_TYPE_INFO, *PMODULE_TYPE_INFO;
+
+typedef struct _SYMBOL_INFO {
+ ULONG SizeOfStruct;
+ ULONG TypeIndex; // Type Index of symbol
+ ULONG64 Reserved[2];
+ ULONG Index;
+ ULONG Size;
+ ULONG64 ModBase; // Base Address of module comtaining this symbol
+ ULONG Flags;
+ ULONG64 Value; // Value of symbol, ValuePresent should be 1
+ ULONG64 Address; // Address of symbol including base address of module
+ ULONG Register; // register holding value or pointer to value
+ ULONG Scope; // scope of the symbol
+ ULONG Tag; // pdb classification
+ ULONG NameLen; // Actual length of name
+ ULONG MaxNameLen;
+ CHAR Name[1]; // Name of symbol
+} SYMBOL_INFO, *PSYMBOL_INFO;
+
+typedef struct _SYMBOL_INFO_PACKAGE {
+ SYMBOL_INFO si;
+ CHAR name[MAX_SYM_NAME + 1];
+} SYMBOL_INFO_PACKAGE, *PSYMBOL_INFO_PACKAGE;
+
+typedef struct _SYMBOL_INFOW {
+ ULONG SizeOfStruct;
+ ULONG TypeIndex; // Type Index of symbol
+ ULONG64 Reserved[2];
+ ULONG Index;
+ ULONG Size;
+ ULONG64 ModBase; // Base Address of module comtaining this symbol
+ ULONG Flags;
+ ULONG64 Value; // Value of symbol, ValuePresent should be 1
+ ULONG64 Address; // Address of symbol including base address of module
+ ULONG Register; // register holding value or pointer to value
+ ULONG Scope; // scope of the symbol
+ ULONG Tag; // pdb classification
+ ULONG NameLen; // Actual length of name
+ ULONG MaxNameLen;
+ WCHAR Name[1]; // Name of symbol
+} SYMBOL_INFOW, *PSYMBOL_INFOW;
+
+typedef struct _SYMBOL_INFO_PACKAGEW {
+ SYMBOL_INFOW si;
+ WCHAR name[MAX_SYM_NAME + 1];
+} SYMBOL_INFO_PACKAGEW, *PSYMBOL_INFO_PACKAGEW;
+
+typedef struct _IMAGEHLP_STACK_FRAME
+{
+ ULONG64 InstructionOffset;
+ ULONG64 ReturnOffset;
+ ULONG64 FrameOffset;
+ ULONG64 StackOffset;
+ ULONG64 BackingStoreOffset;
+ ULONG64 FuncTableEntry;
+ ULONG64 Params[4];
+ ULONG64 Reserved[5];
+ BOOL Virtual;
+ ULONG Reserved2;
+} IMAGEHLP_STACK_FRAME, *PIMAGEHLP_STACK_FRAME;
+
+typedef VOID IMAGEHLP_CONTEXT, *PIMAGEHLP_CONTEXT;
+
+
+BOOL
+IMAGEAPI
+SymSetContext(
+ __in HANDLE hProcess,
+ __in PIMAGEHLP_STACK_FRAME StackFrame,
+ __in_opt PIMAGEHLP_CONTEXT Context
+ );
+
+BOOL
+IMAGEAPI
+SymSetScopeFromAddr(
+ __in HANDLE hProcess,
+ __in ULONG64 Address
+ );
+
+BOOL
+IMAGEAPI
+SymSetScopeFromIndex(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in DWORD Index
+ );
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMPROCESSES_CALLBACK)(
+ __in HANDLE hProcess,
+ __in PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymEnumProcesses(
+ __in PSYM_ENUMPROCESSES_CALLBACK EnumProcessesCallback,
+ __in PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymFromAddr(
+ __in HANDLE hProcess,
+ __in DWORD64 Address,
+ __out_opt PDWORD64 Displacement,
+ __inout PSYMBOL_INFO Symbol
+ );
+
+BOOL
+IMAGEAPI
+SymFromAddrW(
+ __in HANDLE hProcess,
+ __in DWORD64 Address,
+ __out_opt PDWORD64 Displacement,
+ __inout PSYMBOL_INFOW Symbol
+ );
+
+BOOL
+IMAGEAPI
+SymFromToken(
+ __in HANDLE hProcess,
+ __in DWORD64 Base,
+ __in DWORD Token,
+ __inout PSYMBOL_INFO Symbol
+ );
+
+BOOL
+IMAGEAPI
+SymFromTokenW(
+ __in HANDLE hProcess,
+ __in DWORD64 Base,
+ __in DWORD Token,
+ __inout PSYMBOL_INFOW Symbol
+ );
+
+BOOL
+IMAGEAPI
+SymNext(
+ __in HANDLE hProcess,
+ __inout PSYMBOL_INFO si
+ );
+
+BOOL
+IMAGEAPI
+SymNextW(
+ __in HANDLE hProcess,
+ __inout PSYMBOL_INFOW siw
+ );
+
+BOOL
+IMAGEAPI
+SymPrev(
+ __in HANDLE hProcess,
+ __inout PSYMBOL_INFO si
+ );
+
+BOOL
+IMAGEAPI
+SymPrevW(
+ __in HANDLE hProcess,
+ __inout PSYMBOL_INFOW siw
+ );
+
+// While SymFromName will provide a symbol from a name,
+// SymEnumSymbols can provide the same matching information
+// for ALL symbols with a matching name, even regular
+// expressions. That way you can search across modules
+// and differentiate between identically named symbols.
+
+BOOL
+IMAGEAPI
+SymFromName(
+ __in HANDLE hProcess,
+ __in PCSTR Name,
+ __inout PSYMBOL_INFO Symbol
+ );
+
+BOOL
+IMAGEAPI
+SymFromNameW(
+ __in HANDLE hProcess,
+ __in PCWSTR Name,
+ __inout PSYMBOL_INFOW Symbol
+ );
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMERATESYMBOLS_CALLBACK)(
+ __in PSYMBOL_INFO pSymInfo,
+ __in ULONG SymbolSize,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymEnumSymbols(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in_opt PCSTR Mask,
+ __in PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback,
+ __in_opt PVOID UserContext
+ );
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMERATESYMBOLS_CALLBACKW)(
+ __in PSYMBOL_INFOW pSymInfo,
+ __in ULONG SymbolSize,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymEnumSymbolsW(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in_opt PCWSTR Mask,
+ __in PSYM_ENUMERATESYMBOLS_CALLBACKW EnumSymbolsCallback,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymEnumSymbolsForAddr(
+ __in HANDLE hProcess,
+ __in DWORD64 Address,
+ __in PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymEnumSymbolsForAddrW(
+ __in HANDLE hProcess,
+ __in DWORD64 Address,
+ __in PSYM_ENUMERATESYMBOLS_CALLBACKW EnumSymbolsCallback,
+ __in_opt PVOID UserContext
+ );
+
+#define SYMSEARCH_MASKOBJS 0x01 // used internally to implement other APIs
+#define SYMSEARCH_RECURSE 0X02 // recurse scopes
+#define SYMSEARCH_GLOBALSONLY 0X04 // search only for global symbols
+#define SYMSEARCH_ALLITEMS 0X08 // search for everything in the pdb, not just normal scoped symbols
+
+BOOL
+IMAGEAPI
+SymSearch(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in_opt DWORD Index,
+ __in_opt DWORD SymTag,
+ __in_opt PCSTR Mask,
+ __in_opt DWORD64 Address,
+ __in PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback,
+ __in_opt PVOID UserContext,
+ __in DWORD Options
+ );
+
+BOOL
+IMAGEAPI
+SymSearchW(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in_opt DWORD Index,
+ __in_opt DWORD SymTag,
+ __in_opt PCWSTR Mask,
+ __in_opt DWORD64 Address,
+ __in PSYM_ENUMERATESYMBOLS_CALLBACKW EnumSymbolsCallback,
+ __in_opt PVOID UserContext,
+ __in DWORD Options
+ );
+
+BOOL
+IMAGEAPI
+SymGetScope(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in DWORD Index,
+ __inout PSYMBOL_INFO Symbol
+ );
+
+BOOL
+IMAGEAPI
+SymGetScopeW(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in DWORD Index,
+ __inout PSYMBOL_INFOW Symbol
+ );
+
+BOOL
+IMAGEAPI
+SymFromIndex(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in DWORD Index,
+ __inout PSYMBOL_INFO Symbol
+ );
+
+BOOL
+IMAGEAPI
+SymFromIndexW(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in DWORD Index,
+ __inout PSYMBOL_INFOW Symbol
+ );
+
+typedef enum _IMAGEHLP_SYMBOL_TYPE_INFO {
+ TI_GET_SYMTAG,
+ TI_GET_SYMNAME,
+ TI_GET_LENGTH,
+ TI_GET_TYPE,
+ TI_GET_TYPEID,
+ TI_GET_BASETYPE,
+ TI_GET_ARRAYINDEXTYPEID,
+ TI_FINDCHILDREN,
+ TI_GET_DATAKIND,
+ TI_GET_ADDRESSOFFSET,
+ TI_GET_OFFSET,
+ TI_GET_VALUE,
+ TI_GET_COUNT,
+ TI_GET_CHILDRENCOUNT,
+ TI_GET_BITPOSITION,
+ TI_GET_VIRTUALBASECLASS,
+ TI_GET_VIRTUALTABLESHAPEID,
+ TI_GET_VIRTUALBASEPOINTEROFFSET,
+ TI_GET_CLASSPARENTID,
+ TI_GET_NESTED,
+ TI_GET_SYMINDEX,
+ TI_GET_LEXICALPARENT,
+ TI_GET_ADDRESS,
+ TI_GET_THISADJUST,
+ TI_GET_UDTKIND,
+ TI_IS_EQUIV_TO,
+ TI_GET_CALLING_CONVENTION,
+ TI_IS_CLOSE_EQUIV_TO,
+ TI_GTIEX_REQS_VALID,
+ TI_GET_VIRTUALBASEOFFSET,
+ TI_GET_VIRTUALBASEDISPINDEX,
+ TI_GET_IS_REFERENCE,
+ TI_GET_INDIRECTVIRTUALBASECLASS,
+ IMAGEHLP_SYMBOL_TYPE_INFO_MAX,
+} IMAGEHLP_SYMBOL_TYPE_INFO;
+
+typedef struct _TI_FINDCHILDREN_PARAMS {
+ ULONG Count;
+ ULONG Start;
+ ULONG ChildId[1];
+} TI_FINDCHILDREN_PARAMS;
+
+BOOL
+IMAGEAPI
+SymGetTypeInfo(
+ __in HANDLE hProcess,
+ __in DWORD64 ModBase,
+ __in ULONG TypeId,
+ __in IMAGEHLP_SYMBOL_TYPE_INFO GetType,
+ __out PVOID pInfo
+ );
+
+#define IMAGEHLP_GET_TYPE_INFO_UNCACHED 0x00000001
+#define IMAGEHLP_GET_TYPE_INFO_CHILDREN 0x00000002
+
+typedef struct _IMAGEHLP_GET_TYPE_INFO_PARAMS {
+ IN ULONG SizeOfStruct;
+ IN ULONG Flags;
+ IN ULONG NumIds;
+ IN PULONG TypeIds;
+ IN ULONG64 TagFilter;
+ IN ULONG NumReqs;
+ IN IMAGEHLP_SYMBOL_TYPE_INFO* ReqKinds;
+ IN PULONG_PTR ReqOffsets;
+ IN PULONG ReqSizes;
+ IN ULONG_PTR ReqStride;
+ IN ULONG_PTR BufferSize;
+ OUT PVOID Buffer;
+ OUT ULONG EntriesMatched;
+ OUT ULONG EntriesFilled;
+ OUT ULONG64 TagsFound;
+ OUT ULONG64 AllReqsValid;
+ IN ULONG NumReqsValid;
+ OUT PULONG64 ReqsValid OPTIONAL;
+} IMAGEHLP_GET_TYPE_INFO_PARAMS, *PIMAGEHLP_GET_TYPE_INFO_PARAMS;
+
+BOOL
+IMAGEAPI
+SymGetTypeInfoEx(
+ __in HANDLE hProcess,
+ __in DWORD64 ModBase,
+ __inout PIMAGEHLP_GET_TYPE_INFO_PARAMS Params
+ );
+
+BOOL
+IMAGEAPI
+SymEnumTypes(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymEnumTypesW(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in PSYM_ENUMERATESYMBOLS_CALLBACKW EnumSymbolsCallback,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymEnumTypesByName(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in_opt PCSTR mask,
+ __in PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymEnumTypesByNameW(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in_opt PCWSTR mask,
+ __in PSYM_ENUMERATESYMBOLS_CALLBACKW EnumSymbolsCallback,
+ __in_opt PVOID UserContext
+ );
+
+BOOL
+IMAGEAPI
+SymGetTypeFromName(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in PCSTR Name,
+ __inout PSYMBOL_INFO Symbol
+ );
+
+BOOL
+IMAGEAPI
+SymGetTypeFromNameW(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in PCWSTR Name,
+ __inout PSYMBOL_INFOW Symbol
+ );
+
+BOOL
+IMAGEAPI
+SymAddSymbol(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in PCSTR Name,
+ __in DWORD64 Address,
+ __in DWORD Size,
+ __in DWORD Flags
+ );
+
+BOOL
+IMAGEAPI
+SymAddSymbolW(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in PCWSTR Name,
+ __in DWORD64 Address,
+ __in DWORD Size,
+ __in DWORD Flags
+ );
+
+BOOL
+IMAGEAPI
+SymDeleteSymbol(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in_opt PCSTR Name,
+ __in DWORD64 Address,
+ __in DWORD Flags
+ );
+
+BOOL
+IMAGEAPI
+SymDeleteSymbolW(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in_opt PCWSTR Name,
+ __in DWORD64 Address,
+ __in DWORD Flags
+ );
+
+BOOL
+IMAGEAPI
+SymRefreshModuleList(
+ __in HANDLE hProcess
+ );
+
+BOOL
+IMAGEAPI
+SymAddSourceStream(
+ __in HANDLE hProcess,
+ __in ULONG64 Base,
+ __in_opt PCSTR StreamFile,
+ __in_bcount_opt(Size) PBYTE Buffer,
+ __in size_t Size
+ );
+
+typedef BOOL (WINAPI *SYMADDSOURCESTREAM)(HANDLE, ULONG64, PCSTR, PBYTE, size_t);
+
+BOOL
+IMAGEAPI
+SymAddSourceStreamA(
+ __in HANDLE hProcess,
+ __in ULONG64 Base,
+ __in_opt PCSTR StreamFile,
+ __in_bcount_opt(Size) PBYTE Buffer,
+ __in size_t Size
+ );
+
+typedef BOOL (WINAPI *SYMADDSOURCESTREAMA)(HANDLE, ULONG64, PCSTR, PBYTE, size_t);
+
+BOOL
+IMAGEAPI
+SymAddSourceStreamW(
+ __in HANDLE hProcess,
+ __in ULONG64 Base,
+ __in_opt PCWSTR FileSpec,
+ __in_bcount_opt(Size) PBYTE Buffer,
+ __in size_t Size
+ );
+
+BOOL
+IMAGEAPI
+SymSrvIsStoreW(
+ __in_opt HANDLE hProcess,
+ __in PCWSTR path
+ );
+
+BOOL
+IMAGEAPI
+SymSrvIsStore(
+ __in_opt HANDLE hProcess,
+ __in PCSTR path
+ );
+
+PCSTR
+IMAGEAPI
+SymSrvDeltaName(
+ __in HANDLE hProcess,
+ __in_opt PCSTR SymPath,
+ __in PCSTR Type,
+ __in PCSTR File1,
+ __in PCSTR File2
+ );
+
+PCWSTR
+IMAGEAPI
+SymSrvDeltaNameW(
+ __in HANDLE hProcess,
+ __in_opt PCWSTR SymPath,
+ __in PCWSTR Type,
+ __in PCWSTR File1,
+ __in PCWSTR File2
+ );
+
+PCSTR
+IMAGEAPI
+SymSrvGetSupplement(
+ __in HANDLE hProcess,
+ __in_opt PCSTR SymPath,
+ __in PCSTR Node,
+ __in PCSTR File
+ );
+
+PCWSTR
+IMAGEAPI
+SymSrvGetSupplementW(
+ __in HANDLE hProcess,
+ __in_opt PCWSTR SymPath,
+ __in PCWSTR Node,
+ __in PCWSTR File
+ );
+
+BOOL
+IMAGEAPI
+SymSrvGetFileIndexes(
+ __in PCSTR File,
+ __out GUID *Id,
+ __out PDWORD Val1,
+ __out_opt PDWORD Val2,
+ __in DWORD Flags
+ );
+
+BOOL
+IMAGEAPI
+SymSrvGetFileIndexesW(
+ __in PCWSTR File,
+ __out GUID *Id,
+ __out PDWORD Val1,
+ __out_opt PDWORD Val2,
+ __in DWORD Flags
+ );
+
+BOOL
+IMAGEAPI
+SymSrvGetFileIndexStringW(
+ __in HANDLE hProcess,
+ __in_opt PCWSTR SrvPath,
+ __in PCWSTR File,
+ __out_ecount(Size) PWSTR Index,
+ __in size_t Size,
+ __in DWORD Flags
+ );
+
+BOOL
+IMAGEAPI
+SymSrvGetFileIndexString(
+ __in HANDLE hProcess,
+ __in_opt PCSTR SrvPath,
+ __in PCSTR File,
+ __out_ecount(Size) PSTR Index,
+ __in size_t Size,
+ __in DWORD Flags
+ );
+
+typedef struct {
+ DWORD sizeofstruct;
+ char file[MAX_PATH +1];
+ BOOL stripped;
+ DWORD timestamp;
+ DWORD size;
+ char dbgfile[MAX_PATH +1];
+ char pdbfile[MAX_PATH + 1];
+ GUID guid;
+ DWORD sig;
+ DWORD age;
+} SYMSRV_INDEX_INFO, *PSYMSRV_INDEX_INFO;
+
+typedef struct {
+ DWORD sizeofstruct;
+ WCHAR file[MAX_PATH +1];
+ BOOL stripped;
+ DWORD timestamp;
+ DWORD size;
+ WCHAR dbgfile[MAX_PATH +1];
+ WCHAR pdbfile[MAX_PATH + 1];
+ GUID guid;
+ DWORD sig;
+ DWORD age;
+} SYMSRV_INDEX_INFOW, *PSYMSRV_INDEX_INFOW;
+
+BOOL
+IMAGEAPI
+SymSrvGetFileIndexInfo(
+ __in PCSTR File,
+ __out PSYMSRV_INDEX_INFO Info,
+ __in DWORD Flags
+ );
+
+BOOL
+IMAGEAPI
+SymSrvGetFileIndexInfoW(
+ __in PCWSTR File,
+ __out PSYMSRV_INDEX_INFOW Info,
+ __in DWORD Flags
+ );
+
+PCSTR
+IMAGEAPI
+SymSrvStoreSupplement(
+ __in HANDLE hProcess,
+ __in_opt PCSTR SrvPath,
+ __in PCSTR Node,
+ __in PCSTR File,
+ __in DWORD Flags
+ );
+
+PCWSTR
+IMAGEAPI
+SymSrvStoreSupplementW(
+ __in HANDLE hProcess,
+ __in_opt PCWSTR SymPath,
+ __in PCWSTR Node,
+ __in PCWSTR File,
+ __in DWORD Flags
+ );
+
+PCSTR
+IMAGEAPI
+SymSrvStoreFile(
+ __in HANDLE hProcess,
+ __in_opt PCSTR SrvPath,
+ __in PCSTR File,
+ __in DWORD Flags
+ );
+
+PCWSTR
+IMAGEAPI
+SymSrvStoreFileW(
+ __in HANDLE hProcess,
+ __in_opt PCWSTR SrvPath,
+ __in PCWSTR File,
+ __in DWORD Flags
+ );
+
+// used by SymGetSymbolFile's "Type" parameter
+
+typedef enum {
+ sfImage = 0,
+ sfDbg,
+ sfPdb,
+ sfMpd,
+ sfMax
+};
+
+BOOL
+IMAGEAPI
+SymGetSymbolFile(
+ __in_opt HANDLE hProcess,
+ __in_opt PCSTR SymPath,
+ __in PCSTR ImageFile,
+ __in DWORD Type,
+ __out_ecount(cSymbolFile) PSTR SymbolFile,
+ __in size_t cSymbolFile,
+ __out_ecount(cDbgFile) PSTR DbgFile,
+ __in size_t cDbgFile
+ );
+
+BOOL
+IMAGEAPI
+SymGetSymbolFileW(
+ __in_opt HANDLE hProcess,
+ __in_opt PCWSTR SymPath,
+ __in PCWSTR ImageFile,
+ __in DWORD Type,
+ __out_ecount(cSymbolFile) PWSTR SymbolFile,
+ __in size_t cSymbolFile,
+ __out_ecount(cDbgFile) PWSTR DbgFile,
+ __in size_t cDbgFile
+ );
+
+//
+// Full user-mode dump creation.
+//
+
+typedef BOOL (WINAPI *PDBGHELP_CREATE_USER_DUMP_CALLBACK)(
+ __in DWORD DataType,
+ __in PVOID* Data,
+ __out LPDWORD DataLength,
+ __in_opt PVOID UserData
+ );
+
+BOOL
+WINAPI
+DbgHelpCreateUserDump(
+ __in_opt LPCSTR FileName,
+ __in PDBGHELP_CREATE_USER_DUMP_CALLBACK Callback,
+ __in_opt PVOID UserData
+ );
+
+BOOL
+WINAPI
+DbgHelpCreateUserDumpW(
+ __in_opt LPCWSTR FileName,
+ __in PDBGHELP_CREATE_USER_DUMP_CALLBACK Callback,
+ __in_opt PVOID UserData
+ );
+
+// -----------------------------------------------------------------
+// The following 4 legacy APIs are fully supported, but newer
+// ones are recommended. SymFromName and SymFromAddr provide
+// much more detailed info on the returned symbol.
+
+BOOL
+IMAGEAPI
+SymGetSymFromAddr64(
+ __in HANDLE hProcess,
+ __in DWORD64 qwAddr,
+ __out_opt PDWORD64 pdwDisplacement,
+ __inout PIMAGEHLP_SYMBOL64 Symbol
+ );
+
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetSymFromAddr SymGetSymFromAddr64
+#else
+BOOL
+IMAGEAPI
+SymGetSymFromAddr(
+ __in HANDLE hProcess,
+ __in DWORD dwAddr,
+ __out_opt PDWORD pdwDisplacement,
+ __inout PIMAGEHLP_SYMBOL Symbol
+ );
+#endif
+
+// While following two APIs will provide a symbol from a name,
+// SymEnumSymbols can provide the same matching information
+// for ALL symbols with a matching name, even regular
+// expressions. That way you can search across modules
+// and differentiate between identically named symbols.
+
+BOOL
+IMAGEAPI
+SymGetSymFromName64(
+ __in HANDLE hProcess,
+ __in PCSTR Name,
+ __inout PIMAGEHLP_SYMBOL64 Symbol
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetSymFromName SymGetSymFromName64
+#else
+BOOL
+IMAGEAPI
+SymGetSymFromName(
+ __in HANDLE hProcess,
+ __in PCSTR Name,
+ __inout PIMAGEHLP_SYMBOL Symbol
+ );
+#endif
+
+
+// Symbol server exports
+
+typedef BOOL (WINAPI *PSYMBOLSERVERPROC)(PCSTR, PCSTR, PVOID, DWORD, DWORD, PSTR);
+typedef BOOL (WINAPI *PSYMBOLSERVERPROCA)(PCSTR, PCSTR, PVOID, DWORD, DWORD, PSTR);
+typedef BOOL (WINAPI *PSYMBOLSERVERPROCW)(PCWSTR, PCWSTR, PVOID, DWORD, DWORD, PWSTR);
+typedef BOOL (WINAPI *PSYMBOLSERVERBYINDEXPROC)(PCSTR, PCSTR, PCSTR, PSTR);
+typedef BOOL (WINAPI *PSYMBOLSERVERBYINDEXPROCA)(PCSTR, PCSTR, PCSTR, PSTR);
+typedef BOOL (WINAPI *PSYMBOLSERVERBYINDEXPROCW)(PCWSTR, PCWSTR, PCWSTR, PWSTR);
+typedef BOOL (WINAPI *PSYMBOLSERVEROPENPROC)(VOID);
+typedef BOOL (WINAPI *PSYMBOLSERVERCLOSEPROC)(VOID);
+typedef BOOL (WINAPI *PSYMBOLSERVERSETOPTIONSPROC)(UINT_PTR, ULONG64);
+typedef BOOL (WINAPI *PSYMBOLSERVERSETOPTIONSWPROC)(UINT_PTR, ULONG64);
+typedef BOOL (CALLBACK WINAPI *PSYMBOLSERVERCALLBACKPROC)(UINT_PTR action, ULONG64 data, ULONG64 context);
+typedef UINT_PTR (WINAPI *PSYMBOLSERVERGETOPTIONSPROC)();
+typedef BOOL (WINAPI *PSYMBOLSERVERPINGPROC)(PCSTR);
+typedef BOOL (WINAPI *PSYMBOLSERVERPINGPROCA)(PCSTR);
+typedef BOOL (WINAPI *PSYMBOLSERVERPINGPROCW)(PCWSTR);
+typedef BOOL (WINAPI *PSYMBOLSERVERGETVERSION)(LPAPI_VERSION);
+typedef BOOL (WINAPI *PSYMBOLSERVERDELTANAME)(PCSTR, PVOID, DWORD, DWORD, PVOID, DWORD, DWORD, PSTR, size_t);
+typedef BOOL (WINAPI *PSYMBOLSERVERDELTANAMEW)(PCWSTR, PVOID, DWORD, DWORD, PVOID, DWORD, DWORD, PWSTR, size_t);
+typedef BOOL (WINAPI *PSYMBOLSERVERGETSUPPLEMENT)(PCSTR, PCSTR, PCSTR, PSTR, size_t);
+typedef BOOL (WINAPI *PSYMBOLSERVERGETSUPPLEMENTW)(PCWSTR, PCWSTR, PCWSTR, PWSTR, size_t);
+typedef BOOL (WINAPI *PSYMBOLSERVERSTORESUPPLEMENT)(PCSTR, PCSTR, PCSTR, PSTR, size_t, DWORD);
+typedef BOOL (WINAPI *PSYMBOLSERVERSTORESUPPLEMENTW)(PCWSTR, PCWSTR, PCWSTR, PWSTR, size_t, DWORD);
+typedef BOOL (WINAPI *PSYMBOLSERVERGETINDEXSTRING)(PVOID, DWORD, DWORD, PSTR, size_t);
+typedef BOOL (WINAPI *PSYMBOLSERVERGETINDEXSTRINGW)(PVOID, DWORD, DWORD, PWSTR, size_t);
+typedef BOOL (WINAPI *PSYMBOLSERVERSTOREFILE)(PCSTR, PCSTR, PVOID, DWORD, DWORD, PSTR, size_t, DWORD);
+typedef BOOL (WINAPI *PSYMBOLSERVERSTOREFILEW)(PCWSTR, PCWSTR, PVOID, DWORD, DWORD, PWSTR, size_t, DWORD);
+typedef BOOL (WINAPI *PSYMBOLSERVERISSTORE)(PCSTR);
+typedef BOOL (WINAPI *PSYMBOLSERVERISSTOREW)(PCWSTR);
+typedef DWORD (WINAPI *PSYMBOLSERVERVERSION)();
+typedef BOOL (CALLBACK WINAPI *PSYMBOLSERVERMESSAGEPROC)(UINT_PTR action, ULONG64 data, ULONG64 context);
+
+#define SYMSRV_VERSION 2
+
+#define SSRVOPT_CALLBACK 0x00000001
+#define SSRVOPT_DWORD 0x00000002
+#define SSRVOPT_DWORDPTR 0x00000004
+#define SSRVOPT_GUIDPTR 0x00000008
+#define SSRVOPT_OLDGUIDPTR 0x00000010
+#define SSRVOPT_UNATTENDED 0x00000020
+#define SSRVOPT_NOCOPY 0x00000040
+#define SSRVOPT_GETPATH 0x00000040
+#define SSRVOPT_PARENTWIN 0x00000080
+#define SSRVOPT_PARAMTYPE 0x00000100
+#define SSRVOPT_SECURE 0x00000200
+#define SSRVOPT_TRACE 0x00000400
+#define SSRVOPT_SETCONTEXT 0x00000800
+#define SSRVOPT_PROXY 0x00001000
+#define SSRVOPT_DOWNSTREAM_STORE 0x00002000
+#define SSRVOPT_OVERWRITE 0x00004000
+#define SSRVOPT_RESETTOU 0x00008000
+#define SSRVOPT_CALLBACKW 0x00010000
+#define SSRVOPT_FLAT_DEFAULT_STORE 0x00020000
+#define SSRVOPT_PROXYW 0x00040000
+#define SSRVOPT_MESSAGE 0x00080000
+#define SSRVOPT_SERVICE 0x00100000 // deprecated
+#define SSRVOPT_FAVOR_COMPRESSED 0x00200000
+#define SSRVOPT_STRING 0x00400000
+#define SSRVOPT_WINHTTP 0x00800000
+#define SSRVOPT_WININET 0x01000000
+
+#define SSRVOPT_MAX 0x0100000
+
+#define SSRVOPT_RESET ((ULONG_PTR)-1)
+
+
+#define NUM_SSRVOPTS 30
+
+#define SSRVACTION_TRACE 1
+#define SSRVACTION_QUERYCANCEL 2
+#define SSRVACTION_EVENT 3
+#define SSRVACTION_EVENTW 4
+#define SSRVACTION_SIZE 5
+
+#define SYMSTOREOPT_COMPRESS 0x01
+#define SYMSTOREOPT_OVERWRITE 0x02
+#define SYMSTOREOPT_RETURNINDEX 0x04
+#define SYMSTOREOPT_POINTER 0x08
+#define SYMSTOREOPT_ALT_INDEX 0x10
+#define SYMSTOREOPT_UNICODE 0x20
+#define SYMSTOREOPT_PASS_IF_EXISTS 0x40
+
+#ifdef DBGHELP_TRANSLATE_TCHAR
+ #define SymInitialize SymInitializeW
+ #define SymAddSymbol SymAddSymbolW
+ #define SymDeleteSymbol SymDeleteSymbolW
+ #define SearchTreeForFile SearchTreeForFileW
+ #define UnDecorateSymbolName UnDecorateSymbolNameW
+ #define SymGetLineFromName64 SymGetLineFromNameW64
+ #define SymGetLineFromAddr64 SymGetLineFromAddrW64
+ #define SymGetLineNext64 SymGetLineNextW64
+ #define SymGetLinePrev64 SymGetLinePrevW64
+ #define SymFromName SymFromNameW
+ #define SymFindExecutableImage SymFindExecutableImageW
+ #define FindExecutableImageEx FindExecutableImageExW
+ #define SymSearch SymSearchW
+ #define SymEnumLines SymEnumLinesW
+ #define SymEnumSourceLines SymEnumSourceLinesW
+ #define SymGetTypeFromName SymGetTypeFromNameW
+ #define SymEnumSymbolsForAddr SymEnumSymbolsForAddrW
+ #define SymFromAddr SymFromAddrW
+ #define SymMatchString SymMatchStringW
+ #define SymEnumSourceFiles SymEnumSourceFilesW
+ #define SymEnumSymbols SymEnumSymbolsW
+ #define SymLoadModuleEx SymLoadModuleExW
+ #define SymSetSearchPath SymSetSearchPathW
+ #define SymGetSearchPath SymGetSearchPathW
+ #define EnumDirTree EnumDirTreeW
+ #define SymFromToken SymFromTokenW
+ #define SymFromIndex SymFromIndexW
+ #define SymGetScope SymGetScopeW
+ #define SymNext SymNextW
+ #define SymPrev SymPrevW
+ #define SymEnumTypes SymEnumTypesW
+ #define SymEnumTypesByName SymEnumTypesByNameW
+ #define SymRegisterCallback64 SymRegisterCallbackW64
+ #define SymFindDebugInfoFile SymFindDebugInfoFileW
+ #define FindDebugInfoFileEx FindDebugInfoFileExW
+ #define SymFindFileInPath SymFindFileInPathW
+ #define SymEnumerateModules64 SymEnumerateModulesW64
+ #define SymSetHomeDirectory SymSetHomeDirectoryW
+ #define SymGetHomeDirectory SymGetHomeDirectoryW
+ #define SymGetSourceFile SymGetSourceFileW
+ #define SymGetSourceFileToken SymGetSourceFileTokenW
+ #define SymGetSourceFileFromToken SymGetSourceFileFromTokenW
+ #define SymGetSourceVarFromToken SymGetSourceVarFromTokenW
+ #define SymGetSourceFileToken SymGetSourceFileTokenW
+ #define SymGetFileLineOffsets64 SymGetFileLineOffsetsW64
+ #define SymFindFileInPath SymFindFileInPathW
+ #define SymMatchFileName SymMatchFileNameW
+ #define SymGetSourceFileFromToken SymGetSourceFileFromTokenW
+ #define SymGetSourceVarFromToken SymGetSourceVarFromTokenW
+ #define SymGetModuleInfo64 SymGetModuleInfoW64
+ #define SymSrvIsStore SymSrvIsStoreW
+ #define SymSrvDeltaName SymSrvDeltaNameW
+ #define SymSrvGetSupplement SymSrvGetSupplementW
+ #define SymSrvStoreSupplement SymSrvStoreSupplementW
+ #define SymSrvGetFileIndexes SymSrvGetFileIndexes
+ #define SymSrvGetFileIndexString SymSrvGetFileIndexStringW
+ #define SymSrvStoreFile SymSrvStoreFileW
+ #define SymGetSymbolFile SymGetSymbolFileW
+ #define EnumerateLoadedModules64 EnumerateLoadedModulesW64
+ #define EnumerateLoadedModulesEx EnumerateLoadedModulesExW
+ #define SymSrvGetFileIndexInfo SymSrvGetFileIndexInfoW
+
+ #define IMAGEHLP_LINE64 IMAGEHLP_LINEW64
+ #define PIMAGEHLP_LINE64 PIMAGEHLP_LINEW64
+ #define SYMBOL_INFO SYMBOL_INFOW
+ #define PSYMBOL_INFO PSYMBOL_INFOW
+ #define SYMBOL_INFO_PACKAGE SYMBOL_INFO_PACKAGEW
+ #define PSYMBOL_INFO_PACKAGE PSYMBOL_INFO_PACKAGEW
+ #define FIND_EXE_FILE_CALLBACK FIND_EXE_FILE_CALLBACKW
+ #define PFIND_EXE_FILE_CALLBACK PFIND_EXE_FILE_CALLBACKW
+ #define SYM_ENUMERATESYMBOLS_CALLBACK SYM_ENUMERATESYMBOLS_CALLBACKW
+ #define PSYM_ENUMERATESYMBOLS_CALLBACK PSYM_ENUMERATESYMBOLS_CALLBACKW
+ #define SRCCODEINFO SRCCODEINFOW
+ #define PSRCCODEINFO PSRCCODEINFOW
+ #define SOURCEFILE SOURCEFILEW
+ #define PSOURCEFILE PSOURCEFILEW
+ #define SYM_ENUMSOURECFILES_CALLBACK SYM_ENUMSOURCEFILES_CALLBACKW
+ #define PSYM_ENUMSOURCEFILES_CALLBACK PSYM_ENUMSOURECFILES_CALLBACKW
+ #define IMAGEHLP_CBA_EVENT IMAGEHLP_CBA_EVENTW
+ #define PIMAGEHLP_CBA_EVENT PIMAGEHLP_CBA_EVENTW
+ #define PENUMDIRTREE_CALLBACK PENUMDIRTREE_CALLBACKW
+ #define IMAGEHLP_DEFERRED_SYMBOL_LOAD64 IMAGEHLP_DEFERRED_SYMBOL_LOADW64
+ #define PIMAGEHLP_DEFERRED_SYMBOL_LOAD64 PIMAGEHLP_DEFERRED_SYMBOL_LOADW64
+ #define PFIND_DEBUG_FILE_CALLBACK PFIND_DEBUG_FILE_CALLBACKW
+ #define PFINDFILEINPATHCALLBACK PFINDFILEINPATHCALLBACKW
+ #define IMAGEHLP_MODULE64 IMAGEHLP_MODULEW64
+ #define PIMAGEHLP_MODULE64 PIMAGEHLP_MODULEW64
+ #define SYMSRV_INDEX_INFO SYMSRV_INDEX_INFOW
+ #define PSYMSRV_INDEX_INFO PSYMSRV_INDEX_INFOW
+
+ #define PSYMBOLSERVERPROC PSYMBOLSERVERPROCW
+ #define PSYMBOLSERVERPINGPROC PSYMBOLSERVERPINGPROCW
+#endif
+
+// -----------------------------------------------------------------
+// The following APIs exist only for backwards compatibility
+// with a pre-release version documented in an MSDN release.
+
+// You should use SymFindFileInPath if you want to maintain
+// future compatibility.
+
+DBHLP_DEPRECIATED
+BOOL
+IMAGEAPI
+FindFileInPath(
+ __in HANDLE hprocess,
+ __in PCSTR SearchPath,
+ __in PCSTR FileName,
+ __in PVOID id,
+ __in DWORD two,
+ __in DWORD three,
+ __in DWORD flags,
+ __out_ecount(MAX_PATH + 1) PSTR FilePath
+ );
+
+// You should use SymFindFileInPath if you want to maintain
+// future compatibility.
+
+DBHLP_DEPRECIATED
+BOOL
+IMAGEAPI
+FindFileInSearchPath(
+ __in HANDLE hprocess,
+ __in PCSTR SearchPath,
+ __in PCSTR FileName,
+ __in DWORD one,
+ __in DWORD two,
+ __in DWORD three,
+ __out_ecount(MAX_PATH + 1) PSTR FilePath
+ );
+
+DBHLP_DEPRECIATED
+BOOL
+IMAGEAPI
+SymEnumSym(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback,
+ __in_opt PVOID UserContext
+ );
+
+DBHLP_DEPRECIATED
+BOOL
+IMAGEAPI
+SymEnumerateSymbols64(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in PSYM_ENUMSYMBOLS_CALLBACK64 EnumSymbolsCallback,
+ __in_opt PVOID UserContext
+ );
+
+DBHLP_DEPRECIATED
+BOOL
+IMAGEAPI
+SymEnumerateSymbolsW64(
+ __in HANDLE hProcess,
+ __in ULONG64 BaseOfDll,
+ __in PSYM_ENUMSYMBOLS_CALLBACK64W EnumSymbolsCallback,
+ __in_opt PVOID UserContext
+ );
+
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymEnumerateSymbols SymEnumerateSymbols64
+#define SymEnumerateSymbolsW SymEnumerateSymbolsW64
+#else
+DBHLP_DEPRECIATED
+BOOL
+IMAGEAPI
+SymEnumerateSymbols(
+ __in HANDLE hProcess,
+ __in ULONG BaseOfDll,
+ __in PSYM_ENUMSYMBOLS_CALLBACK EnumSymbolsCallback,
+ __in_opt PVOID UserContext
+ );
+
+DBHLP_DEPRECIATED
+BOOL
+IMAGEAPI
+SymEnumerateSymbolsW(
+ __in HANDLE hProcess,
+ __in ULONG BaseOfDll,
+ __in PSYM_ENUMSYMBOLS_CALLBACKW EnumSymbolsCallback,
+ __in_opt PVOID UserContext
+ );
+#endif
+
+// use SymLoadModuleEx
+
+DWORD64
+IMAGEAPI
+SymLoadModule64(
+ __in HANDLE hProcess,
+ __in_opt HANDLE hFile,
+ __in_opt PCSTR ImageName,
+ __in_opt PCSTR ModuleName,
+ __in DWORD64 BaseOfDll,
+ __in DWORD SizeOfDll
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymLoadModule SymLoadModule64
+#else
+DWORD
+IMAGEAPI
+SymLoadModule(
+ __in HANDLE hProcess,
+ __in_opt HANDLE hFile,
+ __in_opt PCSTR ImageName,
+ __in_opt PCSTR ModuleName,
+ __in DWORD BaseOfDll,
+ __in DWORD SizeOfDll
+ );
+#endif
+
+BOOL
+IMAGEAPI
+SymGetSymNext64(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_SYMBOL64 Symbol
+ );
+
+BOOL
+IMAGEAPI
+SymGetSymNextW64(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_SYMBOLW64 Symbol
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetSymNext SymGetSymNext64
+#define SymGetSymNextW SymGetSymNextW64
+#else
+BOOL
+IMAGEAPI
+SymGetSymNext(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_SYMBOL Symbol
+ );
+
+BOOL
+IMAGEAPI
+SymGetSymNextW(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_SYMBOLW Symbol
+ );
+#endif
+
+BOOL
+IMAGEAPI
+SymGetSymPrev64(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_SYMBOL64 Symbol
+ );
+
+BOOL
+IMAGEAPI
+SymGetSymPrevW64(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_SYMBOLW64 Symbol
+ );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetSymPrev SymGetSymPrev64
+#define SymGetSymPrevW SymGetSymPrevW64
+#else
+BOOL
+IMAGEAPI
+SymGetSymPrev(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_SYMBOL Symbol
+ );
+
+BOOL
+IMAGEAPI
+SymGetSymPrevW(
+ __in HANDLE hProcess,
+ __inout PIMAGEHLP_SYMBOLW Symbol
+ );
+#endif
+
+
+// These values should not be used.
+// They have been replaced by SYMFLAG_ values.
+
+#define SYMF_OMAP_GENERATED 0x00000001
+#define SYMF_OMAP_MODIFIED 0x00000002
+#define SYMF_REGISTER 0x00000008
+#define SYMF_REGREL 0x00000010
+#define SYMF_FRAMEREL 0x00000020
+#define SYMF_PARAMETER 0x00000040
+#define SYMF_LOCAL 0x00000080
+#define SYMF_CONSTANT 0x00000100
+#define SYMF_EXPORT 0x00000200
+#define SYMF_FORWARDER 0x00000400
+#define SYMF_FUNCTION 0x00000800
+#define SYMF_VIRTUAL 0x00001000
+#define SYMF_THUNK 0x00002000
+#define SYMF_TLSREL 0x00004000
+
+// These values should also not be used.
+// They have been replaced by SYMFLAG_ values.
+
+#define IMAGEHLP_SYMBOL_INFO_VALUEPRESENT 1
+#define IMAGEHLP_SYMBOL_INFO_REGISTER SYMF_REGISTER // 0x0008
+#define IMAGEHLP_SYMBOL_INFO_REGRELATIVE SYMF_REGREL // 0x0010
+#define IMAGEHLP_SYMBOL_INFO_FRAMERELATIVE SYMF_FRAMEREL // 0x0020
+#define IMAGEHLP_SYMBOL_INFO_PARAMETER SYMF_PARAMETER // 0x0040
+#define IMAGEHLP_SYMBOL_INFO_LOCAL SYMF_LOCAL // 0x0080
+#define IMAGEHLP_SYMBOL_INFO_CONSTANT SYMF_CONSTANT // 0x0100
+#define IMAGEHLP_SYMBOL_FUNCTION SYMF_FUNCTION // 0x0800
+#define IMAGEHLP_SYMBOL_VIRTUAL SYMF_VIRTUAL // 0x1000
+#define IMAGEHLP_SYMBOL_THUNK SYMF_THUNK // 0x2000
+#define IMAGEHLP_SYMBOL_INFO_TLSRELATIVE SYMF_TLSREL // 0x4000
+
+
+#include <pshpack4.h>
+
+#if defined(_MSC_VER)
+#if _MSC_VER >= 800
+#if _MSC_VER >= 1200
+#pragma warning(push)
+#endif
+#pragma warning(disable:4200) /* Zero length array */
+#pragma warning(disable:4201) /* Nameless struct/union */
+#endif
+#endif
+
+#define MINIDUMP_SIGNATURE ('PMDM')
+#define MINIDUMP_VERSION (42899)
+typedef DWORD RVA;
+typedef ULONG64 RVA64;
+
+typedef struct _MINIDUMP_LOCATION_DESCRIPTOR {
+ ULONG32 DataSize;
+ RVA Rva;
+} MINIDUMP_LOCATION_DESCRIPTOR;
+
+typedef struct _MINIDUMP_LOCATION_DESCRIPTOR64 {
+ ULONG64 DataSize;
+ RVA64 Rva;
+} MINIDUMP_LOCATION_DESCRIPTOR64;
+
+
+typedef struct _MINIDUMP_MEMORY_DESCRIPTOR {
+ ULONG64 StartOfMemoryRange;
+ MINIDUMP_LOCATION_DESCRIPTOR Memory;
+} MINIDUMP_MEMORY_DESCRIPTOR, *PMINIDUMP_MEMORY_DESCRIPTOR;
+
+// DESCRIPTOR64 is used for full-memory minidumps where
+// all of the raw memory is laid out sequentially at the
+// end of the dump. There is no need for individual RVAs
+// as the RVA is the base RVA plus the sum of the preceeding
+// data blocks.
+typedef struct _MINIDUMP_MEMORY_DESCRIPTOR64 {
+ ULONG64 StartOfMemoryRange;
+ ULONG64 DataSize;
+} MINIDUMP_MEMORY_DESCRIPTOR64, *PMINIDUMP_MEMORY_DESCRIPTOR64;
+
+
+typedef struct _MINIDUMP_HEADER {
+ ULONG32 Signature;
+ ULONG32 Version;
+ ULONG32 NumberOfStreams;
+ RVA StreamDirectoryRva;
+ ULONG32 CheckSum;
+ union {
+ ULONG32 Reserved;
+ ULONG32 TimeDateStamp;
+ };
+ ULONG64 Flags;
+} MINIDUMP_HEADER, *PMINIDUMP_HEADER;
+
+//
+// The MINIDUMP_HEADER field StreamDirectoryRva points to
+// an array of MINIDUMP_DIRECTORY structures.
+//
+
+typedef struct _MINIDUMP_DIRECTORY {
+ ULONG32 StreamType;
+ MINIDUMP_LOCATION_DESCRIPTOR Location;
+} MINIDUMP_DIRECTORY, *PMINIDUMP_DIRECTORY;
+
+
+typedef struct _MINIDUMP_STRING {
+ ULONG32 Length; // Length in bytes of the string
+ WCHAR Buffer [0]; // Variable size buffer
+} MINIDUMP_STRING, *PMINIDUMP_STRING;
+
+
+
+//
+// The MINIDUMP_DIRECTORY field StreamType may be one of the following types.
+// Types will be added in the future, so if a program reading the minidump
+// header encounters a stream type it does not understand it should ignore
+// the data altogether. Any tag above LastReservedStream will not be used by
+// the system and is reserved for program-specific information.
+//
+
+typedef enum _MINIDUMP_STREAM_TYPE {
+
+ UnusedStream = 0,
+ ReservedStream0 = 1,
+ ReservedStream1 = 2,
+ ThreadListStream = 3,
+ ModuleListStream = 4,
+ MemoryListStream = 5,
+ ExceptionStream = 6,
+ SystemInfoStream = 7,
+ ThreadExListStream = 8,
+ Memory64ListStream = 9,
+ CommentStreamA = 10,
+ CommentStreamW = 11,
+ HandleDataStream = 12,
+ FunctionTableStream = 13,
+ UnloadedModuleListStream = 14,
+ MiscInfoStream = 15,
+ MemoryInfoListStream = 16,
+ ThreadInfoListStream = 17,
+ HandleOperationListStream = 18,
+
+ ceStreamNull = 0x8000,
+ ceStreamSystemInfo = 0x8001,
+ ceStreamException = 0x8002,
+ ceStreamModuleList = 0x8003,
+ ceStreamProcessList = 0x8004,
+ ceStreamThreadList = 0x8005,
+ ceStreamThreadContextList = 0x8006,
+ ceStreamThreadCallStackList = 0x8007,
+ ceStreamMemoryVirtualList = 0x8008,
+ ceStreamMemoryPhysicalList = 0x8009,
+ ceStreamBucketParameters = 0x800A,
+
+ LastReservedStream = 0xffff
+
+} MINIDUMP_STREAM_TYPE;
+
+
+//
+// The minidump system information contains processor and
+// Operating System specific information.
+//
+
+//
+// CPU information is obtained from one of two places.
+//
+// 1) On x86 computers, CPU_INFORMATION is obtained from the CPUID
+// instruction. You must use the X86 portion of the union for X86
+// computers.
+//
+// 2) On non-x86 architectures, CPU_INFORMATION is obtained by calling
+// IsProcessorFeatureSupported().
+//
+
+typedef union _CPU_INFORMATION {
+
+ //
+ // X86 platforms use CPUID function to obtain processor information.
+ //
+
+ struct {
+
+ //
+ // CPUID Subfunction 0, register EAX (VendorId [0]),
+ // EBX (VendorId [1]) and ECX (VendorId [2]).
+ //
+
+ ULONG32 VendorId [ 3 ];
+
+ //
+ // CPUID Subfunction 1, register EAX
+ //
+
+ ULONG32 VersionInformation;
+
+ //
+ // CPUID Subfunction 1, register EDX
+ //
+
+ ULONG32 FeatureInformation;
+
+
+ //
+ // CPUID, Subfunction 80000001, register EBX. This will only
+ // be obtained if the vendor id is "AuthenticAMD".
+ //
+
+ ULONG32 AMDExtendedCpuFeatures;
+
+ } X86CpuInfo;
+
+ //
+ // Non-x86 platforms use processor feature flags.
+ //
+
+ struct {
+
+ ULONG64 ProcessorFeatures [ 2 ];
+
+ } OtherCpuInfo;
+
+} CPU_INFORMATION, *PCPU_INFORMATION;
+
+typedef struct _MINIDUMP_SYSTEM_INFO {
+
+ //
+ // ProcessorArchitecture, ProcessorLevel and ProcessorRevision are all
+ // taken from the SYSTEM_INFO structure obtained by GetSystemInfo( ).
+ //
+
+ USHORT ProcessorArchitecture;
+ USHORT ProcessorLevel;
+ USHORT ProcessorRevision;
+
+ union {
+ USHORT Reserved0;
+ struct {
+ UCHAR NumberOfProcessors;
+ UCHAR ProductType;
+ };
+ };
+
+ //
+ // MajorVersion, MinorVersion, BuildNumber, PlatformId and
+ // CSDVersion are all taken from the OSVERSIONINFO structure
+ // returned by GetVersionEx( ).
+ //
+
+ ULONG32 MajorVersion;
+ ULONG32 MinorVersion;
+ ULONG32 BuildNumber;
+ ULONG32 PlatformId;
+
+ //
+ // RVA to a CSDVersion string in the string table.
+ //
+
+ RVA CSDVersionRva;
+
+ union {
+ ULONG32 Reserved1;
+ struct {
+ USHORT SuiteMask;
+ USHORT Reserved2;
+ };
+ };
+
+ CPU_INFORMATION Cpu;
+
+} MINIDUMP_SYSTEM_INFO, *PMINIDUMP_SYSTEM_INFO;
+
+
+//
+// The minidump thread contains standard thread
+// information plus an RVA to the memory for this
+// thread and an RVA to the CONTEXT structure for
+// this thread.
+//
+
+
+//
+// ThreadId must be 4 bytes on all architectures.
+//
+#ifndef FEATURE_PAL
+static_assert (sizeof ( ((PPROCESS_INFORMATION)0)->dwThreadId ) == 4, "ThreadId must be 4 bytes on all architectures.");
+#else
+typedef int VS_FIXEDFILEINFO;
+#endif
+
+
+typedef struct _MINIDUMP_THREAD {
+ ULONG32 ThreadId;
+ ULONG32 SuspendCount;
+ ULONG32 PriorityClass;
+ ULONG32 Priority;
+ ULONG64 Teb;
+ MINIDUMP_MEMORY_DESCRIPTOR Stack;
+ MINIDUMP_LOCATION_DESCRIPTOR ThreadContext;
+} MINIDUMP_THREAD, *PMINIDUMP_THREAD;
+
+//
+// The thread list is a container of threads.
+//
+
+typedef struct _MINIDUMP_THREAD_LIST {
+ ULONG32 NumberOfThreads;
+ MINIDUMP_THREAD Threads [0];
+} MINIDUMP_THREAD_LIST, *PMINIDUMP_THREAD_LIST;
+
+
+typedef struct _MINIDUMP_THREAD_EX {
+ ULONG32 ThreadId;
+ ULONG32 SuspendCount;
+ ULONG32 PriorityClass;
+ ULONG32 Priority;
+ ULONG64 Teb;
+ MINIDUMP_MEMORY_DESCRIPTOR Stack;
+ MINIDUMP_LOCATION_DESCRIPTOR ThreadContext;
+ MINIDUMP_MEMORY_DESCRIPTOR BackingStore;
+} MINIDUMP_THREAD_EX, *PMINIDUMP_THREAD_EX;
+
+//
+// The thread list is a container of threads.
+//
+
+typedef struct _MINIDUMP_THREAD_EX_LIST {
+ ULONG32 NumberOfThreads;
+ MINIDUMP_THREAD_EX Threads [0];
+} MINIDUMP_THREAD_EX_LIST, *PMINIDUMP_THREAD_EX_LIST;
+
+
+//
+// The MINIDUMP_EXCEPTION is the same as EXCEPTION on Win64.
+//
+
+typedef struct _MINIDUMP_EXCEPTION {
+ ULONG32 ExceptionCode;
+ ULONG32 ExceptionFlags;
+ ULONG64 ExceptionRecord;
+ ULONG64 ExceptionAddress;
+ ULONG32 NumberParameters;
+ ULONG32 __unusedAlignment;
+ ULONG64 ExceptionInformation [ EXCEPTION_MAXIMUM_PARAMETERS ];
+} MINIDUMP_EXCEPTION, *PMINIDUMP_EXCEPTION;
+
+
+//
+// The exception information stream contains the id of the thread that caused
+// the exception (ThreadId), the exception record for the exception
+// (ExceptionRecord) and an RVA to the thread context where the exception
+// occured.
+//
+
+typedef struct MINIDUMP_EXCEPTION_STREAM {
+ ULONG32 ThreadId;
+ ULONG32 __alignment;
+ MINIDUMP_EXCEPTION ExceptionRecord;
+ MINIDUMP_LOCATION_DESCRIPTOR ThreadContext;
+} MINIDUMP_EXCEPTION_STREAM, *PMINIDUMP_EXCEPTION_STREAM;
+
+
+//
+// The MINIDUMP_MODULE contains information about a
+// a specific module. It includes the CheckSum and
+// the TimeDateStamp for the module so the module
+// can be reloaded during the analysis phase.
+//
+
+typedef struct _MINIDUMP_MODULE {
+ ULONG64 BaseOfImage;
+ ULONG32 SizeOfImage;
+ ULONG32 CheckSum;
+ ULONG32 TimeDateStamp;
+ RVA ModuleNameRva;
+ VS_FIXEDFILEINFO VersionInfo;
+ MINIDUMP_LOCATION_DESCRIPTOR CvRecord;
+ MINIDUMP_LOCATION_DESCRIPTOR MiscRecord;
+ ULONG64 Reserved0; // Reserved for future use.
+ ULONG64 Reserved1; // Reserved for future use.
+} MINIDUMP_MODULE, *PMINIDUMP_MODULE;
+
+
+//
+// The minidump module list is a container for modules.
+//
+
+typedef struct _MINIDUMP_MODULE_LIST {
+ ULONG32 NumberOfModules;
+ MINIDUMP_MODULE Modules [ 0 ];
+} MINIDUMP_MODULE_LIST, *PMINIDUMP_MODULE_LIST;
+
+
+//
+// Memory Ranges
+//
+
+typedef struct _MINIDUMP_MEMORY_LIST {
+ ULONG32 NumberOfMemoryRanges;
+ MINIDUMP_MEMORY_DESCRIPTOR MemoryRanges [0];
+} MINIDUMP_MEMORY_LIST, *PMINIDUMP_MEMORY_LIST;
+
+typedef struct _MINIDUMP_MEMORY64_LIST {
+ ULONG64 NumberOfMemoryRanges;
+ RVA64 BaseRva;
+ MINIDUMP_MEMORY_DESCRIPTOR64 MemoryRanges [0];
+} MINIDUMP_MEMORY64_LIST, *PMINIDUMP_MEMORY64_LIST;
+
+
+//
+// Support for user supplied exception information.
+//
+
+typedef struct _MINIDUMP_EXCEPTION_INFORMATION {
+ DWORD ThreadId;
+ PEXCEPTION_POINTERS ExceptionPointers;
+ BOOL ClientPointers;
+} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;
+
+typedef struct _MINIDUMP_EXCEPTION_INFORMATION64 {
+ DWORD ThreadId;
+ ULONG64 ExceptionRecord;
+ ULONG64 ContextRecord;
+ BOOL ClientPointers;
+} MINIDUMP_EXCEPTION_INFORMATION64, *PMINIDUMP_EXCEPTION_INFORMATION64;
+
+
+//
+// Support for capturing system handle state at the time of the dump.
+//
+
+// Per-handle object information varies according to
+// the OS, the OS version, the processor type and
+// so on. The minidump gives a minidump identifier
+// to each possible data format for identification
+// purposes but does not control nor describe the actual data.
+typedef enum _MINIDUMP_HANDLE_OBJECT_INFORMATION_TYPE {
+ MiniHandleObjectInformationNone,
+ MiniThreadInformation1,
+ MiniMutantInformation1,
+ MiniMutantInformation2,
+ MiniProcessInformation1,
+ MiniProcessInformation2,
+ MiniHandleObjectInformationTypeMax
+} MINIDUMP_HANDLE_OBJECT_INFORMATION_TYPE;
+
+typedef struct _MINIDUMP_HANDLE_OBJECT_INFORMATION {
+ RVA NextInfoRva;
+ ULONG32 InfoType;
+ ULONG32 SizeOfInfo;
+ // Raw information follows.
+} MINIDUMP_HANDLE_OBJECT_INFORMATION;
+
+typedef struct _MINIDUMP_HANDLE_DESCRIPTOR {
+ ULONG64 Handle;
+ RVA TypeNameRva;
+ RVA ObjectNameRva;
+ ULONG32 Attributes;
+ ULONG32 GrantedAccess;
+ ULONG32 HandleCount;
+ ULONG32 PointerCount;
+} MINIDUMP_HANDLE_DESCRIPTOR, *PMINIDUMP_HANDLE_DESCRIPTOR;
+
+typedef struct _MINIDUMP_HANDLE_DESCRIPTOR_2 {
+ ULONG64 Handle;
+ RVA TypeNameRva;
+ RVA ObjectNameRva;
+ ULONG32 Attributes;
+ ULONG32 GrantedAccess;
+ ULONG32 HandleCount;
+ ULONG32 PointerCount;
+ RVA ObjectInfoRva;
+ ULONG32 Reserved0;
+} MINIDUMP_HANDLE_DESCRIPTOR_2, *PMINIDUMP_HANDLE_DESCRIPTOR_2;
+
+// The latest MINIDUMP_HANDLE_DESCRIPTOR definition.
+typedef MINIDUMP_HANDLE_DESCRIPTOR_2 MINIDUMP_HANDLE_DESCRIPTOR_N;
+typedef MINIDUMP_HANDLE_DESCRIPTOR_N *PMINIDUMP_HANDLE_DESCRIPTOR_N;
+
+typedef struct _MINIDUMP_HANDLE_DATA_STREAM {
+ ULONG32 SizeOfHeader;
+ ULONG32 SizeOfDescriptor;
+ ULONG32 NumberOfDescriptors;
+ ULONG32 Reserved;
+} MINIDUMP_HANDLE_DATA_STREAM, *PMINIDUMP_HANDLE_DATA_STREAM;
+
+// Some operating systems can track the last operations
+// performed on a handle. For example, Application Verifier
+// can enable this for some versions of Windows. The
+// handle operation list collects handle operations
+// known for the dump target.
+// Each entry is an AVRF_HANDLE_OPERATION.
+typedef struct _MINIDUMP_HANDLE_OPERATION_LIST {
+ ULONG32 SizeOfHeader;
+ ULONG32 SizeOfEntry;
+ ULONG32 NumberOfEntries;
+ ULONG32 Reserved;
+} MINIDUMP_HANDLE_OPERATION_LIST, *PMINIDUMP_HANDLE_OPERATION_LIST;
+
+
+//
+// Support for capturing dynamic function table state at the time of the dump.
+//
+
+typedef struct _MINIDUMP_FUNCTION_TABLE_DESCRIPTOR {
+ ULONG64 MinimumAddress;
+ ULONG64 MaximumAddress;
+ ULONG64 BaseAddress;
+ ULONG32 EntryCount;
+ ULONG32 SizeOfAlignPad;
+} MINIDUMP_FUNCTION_TABLE_DESCRIPTOR, *PMINIDUMP_FUNCTION_TABLE_DESCRIPTOR;
+
+typedef struct _MINIDUMP_FUNCTION_TABLE_STREAM {
+ ULONG32 SizeOfHeader;
+ ULONG32 SizeOfDescriptor;
+ ULONG32 SizeOfNativeDescriptor;
+ ULONG32 SizeOfFunctionEntry;
+ ULONG32 NumberOfDescriptors;
+ ULONG32 SizeOfAlignPad;
+} MINIDUMP_FUNCTION_TABLE_STREAM, *PMINIDUMP_FUNCTION_TABLE_STREAM;
+
+
+//
+// The MINIDUMP_UNLOADED_MODULE contains information about a
+// a specific module that was previously loaded but no
+// longer is. This can help with diagnosing problems where
+// callers attempt to call code that is no longer loaded.
+//
+
+typedef struct _MINIDUMP_UNLOADED_MODULE {
+ ULONG64 BaseOfImage;
+ ULONG32 SizeOfImage;
+ ULONG32 CheckSum;
+ ULONG32 TimeDateStamp;
+ RVA ModuleNameRva;
+} MINIDUMP_UNLOADED_MODULE, *PMINIDUMP_UNLOADED_MODULE;
+
+
+//
+// The minidump unloaded module list is a container for unloaded modules.
+//
+
+typedef struct _MINIDUMP_UNLOADED_MODULE_LIST {
+ ULONG32 SizeOfHeader;
+ ULONG32 SizeOfEntry;
+ ULONG32 NumberOfEntries;
+} MINIDUMP_UNLOADED_MODULE_LIST, *PMINIDUMP_UNLOADED_MODULE_LIST;
+
+
+//
+// The miscellaneous information stream contains a variety
+// of small pieces of information. A member is valid if
+// it's within the available size and its corresponding
+// bit is set.
+//
+
+#define MINIDUMP_MISC1_PROCESS_ID 0x00000001
+#define MINIDUMP_MISC1_PROCESS_TIMES 0x00000002
+#define MINIDUMP_MISC1_PROCESSOR_POWER_INFO 0x00000004
+
+typedef struct _MINIDUMP_MISC_INFO {
+ ULONG32 SizeOfInfo;
+ ULONG32 Flags1;
+ ULONG32 ProcessId;
+ ULONG32 ProcessCreateTime;
+ ULONG32 ProcessUserTime;
+ ULONG32 ProcessKernelTime;
+} MINIDUMP_MISC_INFO, *PMINIDUMP_MISC_INFO;
+
+typedef struct _MINIDUMP_MISC_INFO_2 {
+ ULONG32 SizeOfInfo;
+ ULONG32 Flags1;
+ ULONG32 ProcessId;
+ ULONG32 ProcessCreateTime;
+ ULONG32 ProcessUserTime;
+ ULONG32 ProcessKernelTime;
+ ULONG32 ProcessorMaxMhz;
+ ULONG32 ProcessorCurrentMhz;
+ ULONG32 ProcessorMhzLimit;
+ ULONG32 ProcessorMaxIdleState;
+ ULONG32 ProcessorCurrentIdleState;
+} MINIDUMP_MISC_INFO_2, *PMINIDUMP_MISC_INFO_2;
+
+// The latest MINIDUMP_MISC_INFO definition.
+typedef MINIDUMP_MISC_INFO_2 MINIDUMP_MISC_INFO_N;
+typedef MINIDUMP_MISC_INFO_N* PMINIDUMP_MISC_INFO_N;
+
+
+//
+// The memory information stream contains memory region
+// description information. This stream corresponds to
+// what VirtualQuery would return for the process the
+// dump was created for.
+//
+
+typedef struct _MINIDUMP_MEMORY_INFO {
+ ULONG64 BaseAddress;
+ ULONG64 AllocationBase;
+ ULONG32 AllocationProtect;
+ ULONG32 __alignment1;
+ ULONG64 RegionSize;
+ ULONG32 State;
+ ULONG32 Protect;
+ ULONG32 Type;
+ ULONG32 __alignment2;
+} MINIDUMP_MEMORY_INFO, *PMINIDUMP_MEMORY_INFO;
+
+typedef struct _MINIDUMP_MEMORY_INFO_LIST {
+ ULONG SizeOfHeader;
+ ULONG SizeOfEntry;
+ ULONG64 NumberOfEntries;
+} MINIDUMP_MEMORY_INFO_LIST, *PMINIDUMP_MEMORY_INFO_LIST;
+
+
+//
+// The memory information stream contains memory region
+// description information. This stream corresponds to
+// what VirtualQuery would return for the process the
+// dump was created for.
+//
+
+// Thread dump writer status flags.
+#define MINIDUMP_THREAD_INFO_ERROR_THREAD 0x00000001
+#define MINIDUMP_THREAD_INFO_WRITING_THREAD 0x00000002
+#define MINIDUMP_THREAD_INFO_EXITED_THREAD 0x00000004
+#define MINIDUMP_THREAD_INFO_INVALID_INFO 0x00000008
+#define MINIDUMP_THREAD_INFO_INVALID_CONTEXT 0x00000010
+#define MINIDUMP_THREAD_INFO_INVALID_TEB 0x00000020
+
+typedef struct _MINIDUMP_THREAD_INFO {
+ ULONG32 ThreadId;
+ ULONG32 DumpFlags;
+ ULONG32 DumpError;
+ ULONG32 ExitStatus;
+ ULONG64 CreateTime;
+ ULONG64 ExitTime;
+ ULONG64 KernelTime;
+ ULONG64 UserTime;
+ ULONG64 StartAddress;
+ ULONG64 Affinity;
+} MINIDUMP_THREAD_INFO, *PMINIDUMP_THREAD_INFO;
+
+typedef struct _MINIDUMP_THREAD_INFO_LIST {
+ ULONG SizeOfHeader;
+ ULONG SizeOfEntry;
+ ULONG NumberOfEntries;
+} MINIDUMP_THREAD_INFO_LIST, *PMINIDUMP_THREAD_INFO_LIST;
+
+
+//
+// Support for arbitrary user-defined information.
+//
+
+typedef struct _MINIDUMP_USER_RECORD {
+ ULONG32 Type;
+ MINIDUMP_LOCATION_DESCRIPTOR Memory;
+} MINIDUMP_USER_RECORD, *PMINIDUMP_USER_RECORD;
+
+
+typedef struct _MINIDUMP_USER_STREAM {
+ ULONG32 Type;
+ ULONG BufferSize;
+ PVOID Buffer;
+
+} MINIDUMP_USER_STREAM, *PMINIDUMP_USER_STREAM;
+
+
+typedef struct _MINIDUMP_USER_STREAM_INFORMATION {
+ ULONG UserStreamCount;
+ PMINIDUMP_USER_STREAM UserStreamArray;
+} MINIDUMP_USER_STREAM_INFORMATION, *PMINIDUMP_USER_STREAM_INFORMATION;
+
+//
+// Callback support.
+//
+
+typedef enum _MINIDUMP_CALLBACK_TYPE {
+ ModuleCallback,
+ ThreadCallback,
+ ThreadExCallback,
+ IncludeThreadCallback,
+ IncludeModuleCallback,
+ MemoryCallback,
+ CancelCallback,
+ WriteKernelMinidumpCallback,
+ KernelMinidumpStatusCallback,
+ RemoveMemoryCallback,
+ IncludeVmRegionCallback,
+ IoStartCallback,
+ IoWriteAllCallback,
+ IoFinishCallback,
+ ReadMemoryFailureCallback,
+ SecondaryFlagsCallback,
+} MINIDUMP_CALLBACK_TYPE;
+
+
+typedef struct _MINIDUMP_THREAD_CALLBACK {
+ ULONG ThreadId;
+ HANDLE ThreadHandle;
+ CONTEXT Context;
+ ULONG SizeOfContext;
+ ULONG64 StackBase;
+ ULONG64 StackEnd;
+} MINIDUMP_THREAD_CALLBACK, *PMINIDUMP_THREAD_CALLBACK;
+
+
+typedef struct _MINIDUMP_THREAD_EX_CALLBACK {
+ ULONG ThreadId;
+ HANDLE ThreadHandle;
+ CONTEXT Context;
+ ULONG SizeOfContext;
+ ULONG64 StackBase;
+ ULONG64 StackEnd;
+ ULONG64 BackingStoreBase;
+ ULONG64 BackingStoreEnd;
+} MINIDUMP_THREAD_EX_CALLBACK, *PMINIDUMP_THREAD_EX_CALLBACK;
+
+
+typedef struct _MINIDUMP_INCLUDE_THREAD_CALLBACK {
+ ULONG ThreadId;
+} MINIDUMP_INCLUDE_THREAD_CALLBACK, *PMINIDUMP_INCLUDE_THREAD_CALLBACK;
+
+
+typedef enum _THREAD_WRITE_FLAGS {
+ ThreadWriteThread = 0x0001,
+ ThreadWriteStack = 0x0002,
+ ThreadWriteContext = 0x0004,
+ ThreadWriteBackingStore = 0x0008,
+ ThreadWriteInstructionWindow = 0x0010,
+ ThreadWriteThreadData = 0x0020,
+ ThreadWriteThreadInfo = 0x0040,
+} THREAD_WRITE_FLAGS;
+
+typedef struct _MINIDUMP_MODULE_CALLBACK {
+ PWCHAR FullPath;
+ ULONG64 BaseOfImage;
+ ULONG SizeOfImage;
+ ULONG CheckSum;
+ ULONG TimeDateStamp;
+ VS_FIXEDFILEINFO VersionInfo;
+ PVOID CvRecord;
+ ULONG SizeOfCvRecord;
+ PVOID MiscRecord;
+ ULONG SizeOfMiscRecord;
+} MINIDUMP_MODULE_CALLBACK, *PMINIDUMP_MODULE_CALLBACK;
+
+
+typedef struct _MINIDUMP_INCLUDE_MODULE_CALLBACK {
+ ULONG64 BaseOfImage;
+} MINIDUMP_INCLUDE_MODULE_CALLBACK, *PMINIDUMP_INCLUDE_MODULE_CALLBACK;
+
+
+typedef enum _MODULE_WRITE_FLAGS {
+ ModuleWriteModule = 0x0001,
+ ModuleWriteDataSeg = 0x0002,
+ ModuleWriteMiscRecord = 0x0004,
+ ModuleWriteCvRecord = 0x0008,
+ ModuleReferencedByMemory = 0x0010,
+ ModuleWriteTlsData = 0x0020,
+ ModuleWriteCodeSegs = 0x0040,
+} MODULE_WRITE_FLAGS;
+
+
+typedef struct _MINIDUMP_IO_CALLBACK {
+ HANDLE Handle;
+ ULONG64 Offset;
+ PVOID Buffer;
+ ULONG BufferBytes;
+} MINIDUMP_IO_CALLBACK, *PMINIDUMP_IO_CALLBACK;
+
+
+typedef struct _MINIDUMP_READ_MEMORY_FAILURE_CALLBACK
+{
+ ULONG64 Offset;
+ ULONG Bytes;
+ HRESULT FailureStatus;
+} MINIDUMP_READ_MEMORY_FAILURE_CALLBACK,
+ *PMINIDUMP_READ_MEMORY_FAILURE_CALLBACK;
+
+
+typedef struct _MINIDUMP_CALLBACK_INPUT {
+ ULONG ProcessId;
+ HANDLE ProcessHandle;
+ ULONG CallbackType;
+ union {
+ HRESULT Status;
+ MINIDUMP_THREAD_CALLBACK Thread;
+ MINIDUMP_THREAD_EX_CALLBACK ThreadEx;
+ MINIDUMP_MODULE_CALLBACK Module;
+ MINIDUMP_INCLUDE_THREAD_CALLBACK IncludeThread;
+ MINIDUMP_INCLUDE_MODULE_CALLBACK IncludeModule;
+ MINIDUMP_IO_CALLBACK Io;
+ MINIDUMP_READ_MEMORY_FAILURE_CALLBACK ReadMemoryFailure;
+ ULONG SecondaryFlags;
+ };
+} MINIDUMP_CALLBACK_INPUT, *PMINIDUMP_CALLBACK_INPUT;
+
+typedef struct _MINIDUMP_CALLBACK_OUTPUT {
+ union {
+ ULONG ModuleWriteFlags;
+ ULONG ThreadWriteFlags;
+ ULONG SecondaryFlags;
+ struct {
+ ULONG64 MemoryBase;
+ ULONG MemorySize;
+ };
+ struct {
+ BOOL CheckCancel;
+ BOOL Cancel;
+ };
+ HANDLE Handle;
+ struct {
+ MINIDUMP_MEMORY_INFO VmRegion;
+ BOOL Continue;
+ };
+ HRESULT Status;
+ };
+} MINIDUMP_CALLBACK_OUTPUT, *PMINIDUMP_CALLBACK_OUTPUT;
+
+
+//
+// A normal minidump contains just the information
+// necessary to capture stack traces for all of the
+// existing threads in a process.
+//
+// A minidump with data segments includes all of the data
+// sections from loaded modules in order to capture
+// global variable contents. This can make the dump much
+// larger if many modules have global data.
+//
+// A minidump with full memory includes all of the accessible
+// memory in the process and can be very large. A minidump
+// with full memory always has the raw memory data at the end
+// of the dump so that the initial structures in the dump can
+// be mapped directly without having to include the raw
+// memory information.
+//
+// Stack and backing store memory can be filtered to remove
+// data unnecessary for stack walking. This can improve
+// compression of stacks and also deletes data that may
+// be private and should not be stored in a dump.
+// Memory can also be scanned to see what modules are
+// referenced by stack and backing store memory to allow
+// omission of other modules to reduce dump size.
+// In either of these modes the ModuleReferencedByMemory flag
+// is set for all modules referenced before the base
+// module callbacks occur.
+//
+// On some operating systems a list of modules that were
+// recently unloaded is kept in addition to the currently
+// loaded module list. This information can be saved in
+// the dump if desired.
+//
+// Stack and backing store memory can be scanned for referenced
+// pages in order to pick up data referenced by locals or other
+// stack memory. This can increase the size of a dump significantly.
+//
+// Module paths may contain undesired information such as user names
+// or other important directory names so they can be stripped. This
+// option reduces the ability to locate the proper image later
+// and should only be used in certain situations.
+//
+// Complete operating system per-process and per-thread information can
+// be gathered and stored in the dump.
+//
+// The virtual address space can be scanned for various types
+// of memory to be included in the dump.
+//
+// Code which is concerned with potentially private information
+// getting into the minidump can set a flag that automatically
+// modifies all existing and future flags to avoid placing
+// unnecessary data in the dump. Basic data, such as stack
+// information, will still be included but optional data, such
+// as indirect memory, will not.
+//
+// When doing a full memory dump it's possible to store all
+// of the enumerated memory region descriptive information
+// in a memory information stream.
+//
+// Additional thread information beyond the basic thread
+// structure can be collected if desired.
+//
+// A minidump with code segments includes all of the code
+// and code-related sections from loaded modules in order
+// to capture executable content.
+//
+// MiniDumpWithoutAuxiliaryState turns off any secondary,
+// auxiliary-supported memory gathering.
+//
+// MiniDumpWithFullAuxiliaryState asks any present auxiliary
+// data providers to include all of their state in the dump.
+// The exact set of what is provided depends on the auxiliary.
+// This can be quite large.
+//
+
+typedef enum _MINIDUMP_TYPE {
+ MiniDumpNormal = 0x00000000,
+ MiniDumpWithDataSegs = 0x00000001,
+ MiniDumpWithFullMemory = 0x00000002,
+ MiniDumpWithHandleData = 0x00000004,
+ MiniDumpFilterMemory = 0x00000008,
+ MiniDumpScanMemory = 0x00000010,
+ MiniDumpWithUnloadedModules = 0x00000020,
+ MiniDumpWithIndirectlyReferencedMemory = 0x00000040,
+ MiniDumpFilterModulePaths = 0x00000080,
+ MiniDumpWithProcessThreadData = 0x00000100,
+ MiniDumpWithPrivateReadWriteMemory = 0x00000200,
+ MiniDumpWithoutOptionalData = 0x00000400,
+ MiniDumpWithFullMemoryInfo = 0x00000800,
+ MiniDumpWithThreadInfo = 0x00001000,
+ MiniDumpWithCodeSegs = 0x00002000,
+ MiniDumpWithoutAuxiliaryState = 0x00004000,
+ MiniDumpWithFullAuxiliaryState = 0x00008000,
+
+ MiniDumpValidTypeFlags = 0x0000ffff,
+} MINIDUMP_TYPE;
+
+//
+// In addition to the primary flags provided to
+// MiniDumpWriteDump there are additional, less
+// frequently used options queried via the secondary
+// flags callback.
+//
+// MiniSecondaryWithoutPowerInfo suppresses the minidump
+// query that retrieves processor power information for
+// MINIDUMP_MISC_INFO.
+//
+
+typedef enum _MINIDUMP_SECONDARY_FLAGS {
+ MiniSecondaryWithoutPowerInfo = 0x00000001,
+
+ MiniSecondaryValidFlags = 0x00000001,
+} MINIDUMP_SECONDARY_FLAGS;
+
+
+//
+// The minidump callback should modify the FieldsToWrite parameter to reflect
+// what portions of the specified thread or module should be written to the
+// file.
+//
+
+typedef
+BOOL
+(WINAPI * MINIDUMP_CALLBACK_ROUTINE) (
+ IN PVOID CallbackParam,
+ IN CONST PMINIDUMP_CALLBACK_INPUT CallbackInput,
+ IN OUT PMINIDUMP_CALLBACK_OUTPUT CallbackOutput
+ );
+
+typedef struct _MINIDUMP_CALLBACK_INFORMATION {
+ MINIDUMP_CALLBACK_ROUTINE CallbackRoutine;
+ PVOID CallbackParam;
+} MINIDUMP_CALLBACK_INFORMATION, *PMINIDUMP_CALLBACK_INFORMATION;
+
+
+
+//++
+//
+// PVOID
+// RVA_TO_ADDR(
+// PVOID Mapping,
+// ULONG Rva
+// )
+//
+// Routine Description:
+//
+// Map an RVA that is contained within a mapped file to it's associated
+// flat address.
+//
+// Arguments:
+//
+// Mapping - Base address of mapped file containing the RVA.
+//
+// Rva - An Rva to fixup.
+//
+// Return Values:
+//
+// A pointer to the desired data.
+//
+//--
+
+#define RVA_TO_ADDR(Mapping,Rva) ((PVOID)(((ULONG_PTR) (Mapping)) + (Rva)))
+
+BOOL
+WINAPI
+MiniDumpWriteDump(
+ IN HANDLE hProcess,
+ IN DWORD ProcessId,
+ IN HANDLE hFile,
+ IN MINIDUMP_TYPE DumpType,
+ IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL
+ IN CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, OPTIONAL
+ IN CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL
+ );
+
+BOOL
+WINAPI
+MiniDumpReadDumpStream(
+ IN PVOID BaseOfDump,
+ IN ULONG StreamNumber,
+ OUT PMINIDUMP_DIRECTORY * Dir, OPTIONAL
+ OUT PVOID * StreamPointer, OPTIONAL
+ OUT ULONG * StreamSize OPTIONAL
+ );
+
+#if defined(_MSC_VER)
+#if _MSC_VER >= 800
+#if _MSC_VER >= 1200
+#pragma warning(pop)
+#else
+#pragma warning(default:4200) /* Zero length array */
+#pragma warning(default:4201) /* Nameless struct/union */
+#endif
+#endif
+#endif
+
+#include <poppack.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif // _DBGHELP_
diff --git a/src/ToolBox/SOS/Strike/inc/wdbgexts.h b/src/ToolBox/SOS/Strike/inc/wdbgexts.h
new file mode 100644
index 0000000000..5e0e8c3adf
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/inc/wdbgexts.h
@@ -0,0 +1,2807 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ wdbgexts.h
+
+Abstract:
+
+ This file contains the necessary prototypes and data types for a user
+ to write a debugger extension DLL. This header file is also included
+ by the NT debuggers (WINDBG & KD).
+
+ This header file must be included after "windows.h" and "dbghelp.h".
+
+ Please see the NT DDK documentation for specific information about
+ how to write your own debugger extension DLL.
+
+Environment:
+
+ Win32 only.
+
+Revision History:
+
+--*/
+
+#ifndef _WDBGEXTS_
+#define _WDBGEXTS_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if _MSC_VER >= 1200
+#pragma warning(push)
+#endif
+#ifndef FEATURE_PAL
+#pragma warning(disable:4115 4201 4204 4214 4221)
+#endif
+
+// Maximum value of MAXIMUM_PROCESSORS for all platforms.
+#define CROSS_PLATFORM_MAXIMUM_PROCESSORS 256
+
+#if !defined(WDBGAPI)
+#define WDBGAPI __stdcall
+#endif
+
+#if !defined(WDBGAPIV)
+#define WDBGAPIV __cdecl
+#endif
+
+#ifndef _WINDEF_
+typedef CONST void *LPCVOID;
+#endif
+
+#ifndef _ULONGLONG_
+typedef unsigned __int64 ULONGLONG;
+typedef ULONGLONG *PULONGLONG;
+#endif
+
+#ifndef __field_ecount_opt
+// Should include SpecStrings.h to get proper definitions.
+#define __field_ecount_opt(x)
+#endif
+
+#define WDBGEXTS_MAXSIZE_T ((SIZE_T)~((SIZE_T)0))
+
+typedef
+VOID
+(WDBGAPIV*PWINDBG_OUTPUT_ROUTINE)(
+ PCSTR lpFormat,
+ ...
+ );
+
+typedef
+ULONG_PTR
+(WDBGAPI*PWINDBG_GET_EXPRESSION)(
+ PCSTR lpExpression
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_GET_EXPRESSION32)(
+ PCSTR lpExpression
+ );
+
+typedef
+ULONG64
+(WDBGAPI*PWINDBG_GET_EXPRESSION64)(
+ PCSTR lpExpression
+ );
+
+typedef
+VOID
+(WDBGAPI*PWINDBG_GET_SYMBOL)(
+ PVOID offset,
+ PCHAR pchBuffer,
+ ULONG_PTR *pDisplacement
+ );
+
+typedef
+VOID
+(WDBGAPI*PWINDBG_GET_SYMBOL32)(
+ ULONG offset,
+ PCHAR pchBuffer,
+ PULONG pDisplacement
+ );
+
+typedef
+VOID
+(WDBGAPI*PWINDBG_GET_SYMBOL64)(
+ ULONG64 offset,
+ PCHAR pchBuffer,
+ PULONG64 pDisplacement
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_DISASM)(
+ ULONG_PTR *lpOffset,
+ PCSTR lpBuffer,
+ ULONG fShowEffectiveAddress
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_DISASM32)(
+ ULONG *lpOffset,
+ PCSTR lpBuffer,
+ ULONG fShowEffectiveAddress
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_DISASM64)(
+ ULONG64 *lpOffset,
+ PCSTR lpBuffer,
+ ULONG fShowEffectiveAddress
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_CHECK_CONTROL_C)(
+ VOID
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_READ_PROCESS_MEMORY_ROUTINE)(
+ ULONG_PTR offset,
+ PVOID lpBuffer,
+ ULONG cb,
+ PULONG lpcbBytesRead
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_READ_PROCESS_MEMORY_ROUTINE32)(
+ ULONG offset,
+ PVOID lpBuffer,
+ ULONG cb,
+ PULONG lpcbBytesRead
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_READ_PROCESS_MEMORY_ROUTINE64)(
+ ULONG64 offset,
+ PVOID lpBuffer,
+ ULONG cb,
+ PULONG lpcbBytesRead
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_WRITE_PROCESS_MEMORY_ROUTINE)(
+ ULONG_PTR offset,
+ LPCVOID lpBuffer,
+ ULONG cb,
+ PULONG lpcbBytesWritten
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_WRITE_PROCESS_MEMORY_ROUTINE32)(
+ ULONG offset,
+ LPCVOID lpBuffer,
+ ULONG cb,
+ PULONG lpcbBytesWritten
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_WRITE_PROCESS_MEMORY_ROUTINE64)(
+ ULONG64 offset,
+ LPCVOID lpBuffer,
+ ULONG cb,
+ PULONG lpcbBytesWritten
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_GET_THREAD_CONTEXT_ROUTINE)(
+ ULONG Processor,
+ PCONTEXT lpContext,
+ ULONG cbSizeOfContext
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_SET_THREAD_CONTEXT_ROUTINE)(
+ ULONG Processor,
+ PCONTEXT lpContext,
+ ULONG cbSizeOfContext
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_IOCTL_ROUTINE)(
+ USHORT IoctlType,
+ PVOID lpvData,
+ ULONG cbSize
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_OLDKD_READ_PHYSICAL_MEMORY)(
+ ULONGLONG address,
+ PVOID buffer,
+ ULONG count,
+ PULONG bytesread
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_OLDKD_WRITE_PHYSICAL_MEMORY)(
+ ULONGLONG address,
+ PVOID buffer,
+ ULONG length,
+ PULONG byteswritten
+ );
+
+
+typedef struct _EXTSTACKTRACE {
+ ULONG FramePointer;
+ ULONG ProgramCounter;
+ ULONG ReturnAddress;
+ ULONG Args[4];
+} EXTSTACKTRACE, *PEXTSTACKTRACE;
+
+typedef struct _EXTSTACKTRACE32 {
+ ULONG FramePointer;
+ ULONG ProgramCounter;
+ ULONG ReturnAddress;
+ ULONG Args[4];
+} EXTSTACKTRACE32, *PEXTSTACKTRACE32;
+
+typedef struct _EXTSTACKTRACE64 {
+ ULONG64 FramePointer;
+ ULONG64 ProgramCounter;
+ ULONG64 ReturnAddress;
+ ULONG64 Args[4];
+} EXTSTACKTRACE64, *PEXTSTACKTRACE64;
+
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_STACKTRACE_ROUTINE)(
+ ULONG FramePointer,
+ ULONG StackPointer,
+ ULONG ProgramCounter,
+ PEXTSTACKTRACE StackFrames,
+ ULONG Frames
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_STACKTRACE_ROUTINE32)(
+ ULONG FramePointer,
+ ULONG StackPointer,
+ ULONG ProgramCounter,
+ PEXTSTACKTRACE32 StackFrames,
+ ULONG Frames
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_STACKTRACE_ROUTINE64)(
+ ULONG64 FramePointer,
+ ULONG64 StackPointer,
+ ULONG64 ProgramCounter,
+ PEXTSTACKTRACE64 StackFrames,
+ ULONG Frames
+ );
+
+typedef struct _WINDBG_EXTENSION_APIS {
+ ULONG nSize;
+ PWINDBG_OUTPUT_ROUTINE lpOutputRoutine;
+ PWINDBG_GET_EXPRESSION lpGetExpressionRoutine;
+ PWINDBG_GET_SYMBOL lpGetSymbolRoutine;
+ PWINDBG_DISASM lpDisasmRoutine;
+ PWINDBG_CHECK_CONTROL_C lpCheckControlCRoutine;
+ PWINDBG_READ_PROCESS_MEMORY_ROUTINE lpReadProcessMemoryRoutine;
+ PWINDBG_WRITE_PROCESS_MEMORY_ROUTINE lpWriteProcessMemoryRoutine;
+ PWINDBG_GET_THREAD_CONTEXT_ROUTINE lpGetThreadContextRoutine;
+ PWINDBG_SET_THREAD_CONTEXT_ROUTINE lpSetThreadContextRoutine;
+ PWINDBG_IOCTL_ROUTINE lpIoctlRoutine;
+ PWINDBG_STACKTRACE_ROUTINE lpStackTraceRoutine;
+} WINDBG_EXTENSION_APIS, *PWINDBG_EXTENSION_APIS;
+
+typedef struct _WINDBG_EXTENSION_APIS32 {
+ ULONG nSize;
+ PWINDBG_OUTPUT_ROUTINE lpOutputRoutine;
+ PWINDBG_GET_EXPRESSION32 lpGetExpressionRoutine;
+ PWINDBG_GET_SYMBOL32 lpGetSymbolRoutine;
+ PWINDBG_DISASM32 lpDisasmRoutine;
+ PWINDBG_CHECK_CONTROL_C lpCheckControlCRoutine;
+ PWINDBG_READ_PROCESS_MEMORY_ROUTINE32 lpReadProcessMemoryRoutine;
+ PWINDBG_WRITE_PROCESS_MEMORY_ROUTINE32 lpWriteProcessMemoryRoutine;
+ PWINDBG_GET_THREAD_CONTEXT_ROUTINE lpGetThreadContextRoutine;
+ PWINDBG_SET_THREAD_CONTEXT_ROUTINE lpSetThreadContextRoutine;
+ PWINDBG_IOCTL_ROUTINE lpIoctlRoutine;
+ PWINDBG_STACKTRACE_ROUTINE32 lpStackTraceRoutine;
+} WINDBG_EXTENSION_APIS32, *PWINDBG_EXTENSION_APIS32;
+
+typedef struct _WINDBG_EXTENSION_APIS64 {
+ ULONG nSize;
+ PWINDBG_OUTPUT_ROUTINE lpOutputRoutine;
+ PWINDBG_GET_EXPRESSION64 lpGetExpressionRoutine;
+ PWINDBG_GET_SYMBOL64 lpGetSymbolRoutine;
+ PWINDBG_DISASM64 lpDisasmRoutine;
+ PWINDBG_CHECK_CONTROL_C lpCheckControlCRoutine;
+ PWINDBG_READ_PROCESS_MEMORY_ROUTINE64 lpReadProcessMemoryRoutine;
+ PWINDBG_WRITE_PROCESS_MEMORY_ROUTINE64 lpWriteProcessMemoryRoutine;
+ PWINDBG_GET_THREAD_CONTEXT_ROUTINE lpGetThreadContextRoutine;
+ PWINDBG_SET_THREAD_CONTEXT_ROUTINE lpSetThreadContextRoutine;
+ PWINDBG_IOCTL_ROUTINE lpIoctlRoutine;
+ PWINDBG_STACKTRACE_ROUTINE64 lpStackTraceRoutine;
+} WINDBG_EXTENSION_APIS64, *PWINDBG_EXTENSION_APIS64;
+
+
+typedef struct _WINDBG_OLD_EXTENSION_APIS {
+ ULONG nSize;
+ PWINDBG_OUTPUT_ROUTINE lpOutputRoutine;
+ PWINDBG_GET_EXPRESSION lpGetExpressionRoutine;
+ PWINDBG_GET_SYMBOL lpGetSymbolRoutine;
+ PWINDBG_DISASM lpDisasmRoutine;
+ PWINDBG_CHECK_CONTROL_C lpCheckControlCRoutine;
+} WINDBG_OLD_EXTENSION_APIS, *PWINDBG_OLD_EXTENSION_APIS;
+
+typedef struct _WINDBG_OLDKD_EXTENSION_APIS {
+ ULONG nSize;
+ PWINDBG_OUTPUT_ROUTINE lpOutputRoutine;
+ PWINDBG_GET_EXPRESSION32 lpGetExpressionRoutine;
+ PWINDBG_GET_SYMBOL32 lpGetSymbolRoutine;
+ PWINDBG_DISASM32 lpDisasmRoutine;
+ PWINDBG_CHECK_CONTROL_C lpCheckControlCRoutine;
+ PWINDBG_READ_PROCESS_MEMORY_ROUTINE32 lpReadVirtualMemRoutine;
+ PWINDBG_WRITE_PROCESS_MEMORY_ROUTINE32 lpWriteVirtualMemRoutine;
+ PWINDBG_OLDKD_READ_PHYSICAL_MEMORY lpReadPhysicalMemRoutine;
+ PWINDBG_OLDKD_WRITE_PHYSICAL_MEMORY lpWritePhysicalMemRoutine;
+} WINDBG_OLDKD_EXTENSION_APIS, *PWINDBG_OLDKD_EXTENSION_APIS;
+
+typedef
+VOID
+(WDBGAPI*PWINDBG_OLD_EXTENSION_ROUTINE)(
+ ULONG dwCurrentPc,
+ PWINDBG_EXTENSION_APIS lpExtensionApis,
+ PCSTR lpArgumentString
+ );
+
+typedef
+VOID
+(WDBGAPI*PWINDBG_EXTENSION_ROUTINE)(
+ HANDLE hCurrentProcess,
+ HANDLE hCurrentThread,
+ ULONG dwCurrentPc,
+ ULONG dwProcessor,
+ PCSTR lpArgumentString
+ );
+
+typedef
+VOID
+(WDBGAPI*PWINDBG_EXTENSION_ROUTINE32)(
+ HANDLE hCurrentProcess,
+ HANDLE hCurrentThread,
+ ULONG dwCurrentPc,
+ ULONG dwProcessor,
+ PCSTR lpArgumentString
+ );
+
+typedef
+VOID
+(WDBGAPI*PWINDBG_EXTENSION_ROUTINE64)(
+ HANDLE hCurrentProcess,
+ HANDLE hCurrentThread,
+ ULONG64 dwCurrentPc,
+ ULONG dwProcessor,
+ PCSTR lpArgumentString
+ );
+
+typedef
+VOID
+(WDBGAPI*PWINDBG_OLDKD_EXTENSION_ROUTINE)(
+ ULONG dwCurrentPc,
+ PWINDBG_OLDKD_EXTENSION_APIS lpExtensionApis,
+ PCSTR lpArgumentString
+ );
+
+typedef
+VOID
+(WDBGAPI*PWINDBG_EXTENSION_DLL_INIT)(
+ PWINDBG_EXTENSION_APIS lpExtensionApis,
+ USHORT MajorVersion,
+ USHORT MinorVersion
+ );
+
+typedef
+VOID
+(WDBGAPI*PWINDBG_EXTENSION_DLL_INIT32)(
+ PWINDBG_EXTENSION_APIS32 lpExtensionApis,
+ USHORT MajorVersion,
+ USHORT MinorVersion
+ );
+
+typedef
+VOID
+(WDBGAPI*PWINDBG_EXTENSION_DLL_INIT64)(
+ PWINDBG_EXTENSION_APIS64 lpExtensionApis,
+ USHORT MajorVersion,
+ USHORT MinorVersion
+ );
+
+typedef
+ULONG
+(WDBGAPI*PWINDBG_CHECK_VERSION)(
+ VOID
+ );
+
+#define EXT_API_VERSION_NUMBER 5
+#define EXT_API_VERSION_NUMBER32 5
+#define EXT_API_VERSION_NUMBER64 6
+
+typedef struct EXT_API_VERSION {
+ USHORT MajorVersion;
+ USHORT MinorVersion;
+ USHORT Revision;
+ USHORT Reserved;
+} EXT_API_VERSION, *LPEXT_API_VERSION;
+
+typedef
+LPEXT_API_VERSION
+(WDBGAPI*PWINDBG_EXTENSION_API_VERSION)(
+ VOID
+ );
+
+#define IG_KD_CONTEXT 1
+#define IG_READ_CONTROL_SPACE 2
+#define IG_WRITE_CONTROL_SPACE 3
+#define IG_READ_IO_SPACE 4
+#define IG_WRITE_IO_SPACE 5
+#define IG_READ_PHYSICAL 6
+#define IG_WRITE_PHYSICAL 7
+#define IG_READ_IO_SPACE_EX 8
+#define IG_WRITE_IO_SPACE_EX 9
+#define IG_KSTACK_HELP 10 // obsolete
+#define IG_SET_THREAD 11
+#define IG_READ_MSR 12
+#define IG_WRITE_MSR 13
+#define IG_GET_DEBUGGER_DATA 14
+#define IG_GET_KERNEL_VERSION 15
+#define IG_RELOAD_SYMBOLS 16
+#define IG_GET_SET_SYMPATH 17
+#define IG_GET_EXCEPTION_RECORD 18
+#define IG_IS_PTR64 19
+#define IG_GET_BUS_DATA 20
+#define IG_SET_BUS_DATA 21
+#define IG_DUMP_SYMBOL_INFO 22
+#define IG_LOWMEM_CHECK 23
+#define IG_SEARCH_MEMORY 24
+#define IG_GET_CURRENT_THREAD 25
+#define IG_GET_CURRENT_PROCESS 26
+#define IG_GET_TYPE_SIZE 27
+#define IG_GET_CURRENT_PROCESS_HANDLE 28
+#define IG_GET_INPUT_LINE 29
+#define IG_GET_EXPRESSION_EX 30
+#define IG_TRANSLATE_VIRTUAL_TO_PHYSICAL 31
+#define IG_GET_CACHE_SIZE 32
+#define IG_READ_PHYSICAL_WITH_FLAGS 33
+#define IG_WRITE_PHYSICAL_WITH_FLAGS 34
+#define IG_POINTER_SEARCH_PHYSICAL 35
+#define IG_OBSOLETE_PLACEHOLDER_36 36
+#define IG_GET_THREAD_OS_INFO 37
+#define IG_GET_CLR_DATA_INTERFACE 38
+#define IG_MATCH_PATTERN_A 39
+#define IG_FIND_FILE 40
+#define IG_TYPED_DATA_OBSOLETE 41
+#define IG_QUERY_TARGET_INTERFACE 42
+#define IG_TYPED_DATA 43
+#define IG_DISASSEMBLE_BUFFER 44
+#define IG_GET_ANY_MODULE_IN_RANGE 45
+
+#define IG_GET_TEB_ADDRESS 128
+#define IG_GET_PEB_ADDRESS 129
+
+typedef struct _PROCESSORINFO {
+ USHORT Processor; // current processor
+ USHORT NumberProcessors; // total number of processors
+} PROCESSORINFO, *PPROCESSORINFO;
+
+typedef struct _READCONTROLSPACE {
+ USHORT Processor;
+ ULONG Address;
+ ULONG BufLen;
+ UCHAR Buf[1];
+} READCONTROLSPACE, *PREADCONTROLSPACE;
+
+typedef struct _READCONTROLSPACE32 {
+ USHORT Processor;
+ ULONG Address;
+ ULONG BufLen;
+ UCHAR Buf[1];
+} READCONTROLSPACE32, *PREADCONTROLSPACE32;
+
+typedef struct _READCONTROLSPACE64 {
+ USHORT Processor;
+ ULONG64 Address;
+ ULONG BufLen;
+ UCHAR Buf[1];
+} READCONTROLSPACE64, *PREADCONTROLSPACE64;
+
+typedef struct _IOSPACE {
+ ULONG Address;
+ ULONG Length; // 1, 2, or 4 bytes
+ ULONG Data;
+} IOSPACE, *PIOSPACE;
+
+typedef struct _IOSPACE32 {
+ ULONG Address;
+ ULONG Length; // 1, 2, or 4 bytes
+ ULONG Data;
+} IOSPACE32, *PIOSPACE32;
+
+typedef struct _IOSPACE64 {
+ ULONG64 Address;
+ ULONG Length; // 1, 2, or 4 bytes
+ ULONG Data;
+} IOSPACE64, *PIOSPACE64;
+
+typedef struct _IOSPACE_EX {
+ ULONG Address;
+ ULONG Length; // 1, 2, or 4 bytes
+ ULONG Data;
+ ULONG InterfaceType;
+ ULONG BusNumber;
+ ULONG AddressSpace;
+} IOSPACE_EX, *PIOSPACE_EX;
+
+typedef struct _IOSPACE_EX32 {
+ ULONG Address;
+ ULONG Length; // 1, 2, or 4 bytes
+ ULONG Data;
+ ULONG InterfaceType;
+ ULONG BusNumber;
+ ULONG AddressSpace;
+} IOSPACE_EX32, *PIOSPACE_EX32;
+
+typedef struct _IOSPACE_EX64 {
+ ULONG64 Address;
+ ULONG Length; // 1, 2, or 4 bytes
+ ULONG Data;
+ ULONG InterfaceType;
+ ULONG BusNumber;
+ ULONG AddressSpace;
+} IOSPACE_EX64, *PIOSPACE_EX64;
+
+typedef struct _GETSETBUSDATA {
+ ULONG BusDataType;
+ ULONG BusNumber;
+ ULONG SlotNumber;
+ PVOID Buffer;
+ ULONG Offset;
+ ULONG Length;
+} BUSDATA, *PBUSDATA;
+
+typedef struct _SEARCHMEMORY {
+ ULONG64 SearchAddress;
+ ULONG64 SearchLength;
+ ULONG64 FoundAddress;
+ ULONG PatternLength;
+ PVOID Pattern;
+} SEARCHMEMORY, *PSEARCHMEMORY;
+
+typedef struct _PHYSICAL {
+ ULONGLONG Address;
+ ULONG BufLen;
+ UCHAR Buf[1];
+} PHYSICAL, *PPHYSICAL;
+
+#define PHYS_FLAG_DEFAULT 0
+#define PHYS_FLAG_CACHED 1
+#define PHYS_FLAG_UNCACHED 2
+#define PHYS_FLAG_WRITE_COMBINED 3
+
+typedef struct _PHYSICAL_WITH_FLAGS {
+ ULONGLONG Address;
+ ULONG BufLen;
+ ULONG Flags;
+ UCHAR Buf[1];
+} PHYSICAL_WITH_FLAGS, *PPHYSICAL_WITH_FLAGS;
+
+typedef struct _READ_WRITE_MSR {
+ ULONG Msr;
+ LONGLONG Value;
+} READ_WRITE_MSR, *PREAD_WRITE_MSR;
+
+typedef struct _GET_SET_SYMPATH {
+ PCSTR Args; // args to !reload command
+ PSTR Result; // returns new path
+ int Length; // Length of result buffer
+} GET_SET_SYMPATH, *PGET_SET_SYMPATH;
+
+typedef struct _GET_TEB_ADDRESS {
+ ULONGLONG Address;
+} GET_TEB_ADDRESS, *PGET_TEB_ADDRESS;
+
+typedef struct _GET_PEB_ADDRESS {
+ ULONG64 CurrentThread;
+ ULONGLONG Address;
+} GET_PEB_ADDRESS, *PGET_PEB_ADDRESS;
+
+typedef struct _GET_CURRENT_THREAD_ADDRESS {
+ ULONG Processor;
+ ULONG64 Address;
+} GET_CURRENT_THREAD_ADDRESS, *PGET_CURRENT_THREAD_ADDRESS;
+
+typedef struct _GET_CURRENT_PROCESS_ADDRESS {
+ ULONG Processor;
+ ULONG64 CurrentThread;
+ ULONG64 Address;
+} GET_CURRENT_PROCESS_ADDRESS, *PGET_CURRENT_PROCESS_ADDRESS;
+
+typedef struct _GET_INPUT_LINE {
+ PCSTR Prompt;
+ PSTR Buffer;
+ ULONG BufferSize;
+ ULONG InputSize;
+} GET_INPUT_LINE, *PGET_INPUT_LINE;
+
+typedef struct _GET_EXPRESSION_EX {
+ PCSTR Expression;
+ PCSTR Remainder;
+ ULONG64 Value;
+} GET_EXPRESSION_EX, *PGET_EXPRESSION_EX;
+
+typedef struct _TRANSLATE_VIRTUAL_TO_PHYSICAL {
+ ULONG64 Virtual;
+ ULONG64 Physical;
+} TRANSLATE_VIRTUAL_TO_PHYSICAL, *PTRANSLATE_VIRTUAL_TO_PHYSICAL;
+
+#define PTR_SEARCH_PHYS_ALL_HITS 0x00000001
+#define PTR_SEARCH_PHYS_PTE 0x00000002
+#define PTR_SEARCH_PHYS_RANGE_CHECK_ONLY 0x00000004
+
+#define PTR_SEARCH_PHYS_SIZE_SHIFT 3
+#define PTR_SEARCH_PHYS_SIZE_MASK (0xf << PTR_SEARCH_PHYS_SIZE_SHIFT)
+
+#define PTR_SEARCH_NO_SYMBOL_CHECK 0x80000000
+
+typedef struct _POINTER_SEARCH_PHYSICAL {
+ IN ULONG64 Offset;
+ IN ULONG64 Length;
+ IN ULONG64 PointerMin;
+ IN ULONG64 PointerMax;
+ IN ULONG Flags;
+ OUT PULONG64 MatchOffsets;
+ IN ULONG MatchOffsetsSize;
+ OUT ULONG MatchOffsetsCount;
+} POINTER_SEARCH_PHYSICAL, *PPOINTER_SEARCH_PHYSICAL;
+
+typedef struct _WDBGEXTS_THREAD_OS_INFO {
+ // System thread ID input.
+ ULONG ThreadId;
+
+ //
+ // Output information.
+ //
+
+ // Exit status is STILL_ACTIVE by default.
+ ULONG ExitStatus;
+ // Priority class is zero if not known.
+ ULONG PriorityClass;
+ // Priority defaults to normal.
+ ULONG Priority;
+ // Times can be zero if not known.
+ ULONG64 CreateTime;
+ ULONG64 ExitTime;
+ ULONG64 KernelTime;
+ ULONG64 UserTime;
+ // Start offset is zero if not known.
+ ULONG64 StartOffset;
+ // Affinity is zero if not known.
+ ULONG64 Affinity;
+} WDBGEXTS_THREAD_OS_INFO, *PWDBGEXTS_THREAD_OS_INFO;
+
+typedef struct _WDBGEXTS_CLR_DATA_INTERFACE {
+ // Interface requested.
+ const IID* Iid;
+ // Interface pointer return.
+ PVOID Iface;
+} WDBGEXTS_CLR_DATA_INTERFACE, *PWDBGEXTS_CLR_DATA_INTERFACE;
+
+typedef struct _EXT_MATCH_PATTERN_A {
+ IN PCSTR Str;
+ IN PCSTR Pattern;
+ IN ULONG CaseSensitive;
+} EXT_MATCH_PATTERN_A, *PEXT_MATCH_PATTERN_A;
+
+#define EXT_FIND_FILE_ALLOW_GIVEN_PATH 0x00000001
+
+typedef struct _EXT_FIND_FILE {
+ IN PCWSTR FileName;
+ IN ULONG64 IndexedSize;
+ IN ULONG ImageTimeDateStamp;
+ // Pass zero to ignore.
+ IN ULONG ImageCheckSum;
+ IN OPTIONAL PVOID ExtraInfo;
+ IN ULONG ExtraInfoSize;
+ IN ULONG Flags;
+ // Free with UnmapViewOfFile.
+ OUT PVOID FileMapping;
+ OUT ULONG64 FileMappingSize;
+ // Free with CloseHandle.
+ OUT HANDLE FileHandle;
+ // Must be at least MAX_PATH characters if set.
+ OUT OPTIONAL PWSTR FoundFileName;
+ OUT ULONG FoundFileNameChars;
+} EXT_FIND_FILE, *PEXT_FIND_FILE;
+
+#define DEBUG_TYPED_DATA_IS_IN_MEMORY 0x00000001
+#define DEBUG_TYPED_DATA_PHYSICAL_DEFAULT 0x00000002
+#define DEBUG_TYPED_DATA_PHYSICAL_CACHED 0x00000004
+#define DEBUG_TYPED_DATA_PHYSICAL_UNCACHED 0x00000006
+#define DEBUG_TYPED_DATA_PHYSICAL_WRITE_COMBINED 0x00000008
+
+// Mask for all physical flags.
+#define DEBUG_TYPED_DATA_PHYSICAL_MEMORY 0x0000000e
+
+typedef struct _DEBUG_TYPED_DATA
+{
+ ULONG64 ModBase;
+ ULONG64 Offset;
+ ULONG64 EngineHandle;
+ ULONG64 Data;
+ ULONG Size;
+ ULONG Flags;
+ ULONG TypeId;
+ ULONG BaseTypeId;
+ ULONG Tag;
+ ULONG Register;
+ ULONG64 Internal[9];
+} DEBUG_TYPED_DATA, *PDEBUG_TYPED_DATA;
+
+typedef enum _EXT_TDOP {
+ EXT_TDOP_COPY,
+ EXT_TDOP_RELEASE,
+ EXT_TDOP_SET_FROM_EXPR,
+ EXT_TDOP_SET_FROM_U64_EXPR,
+ EXT_TDOP_GET_FIELD,
+ EXT_TDOP_EVALUATE,
+ EXT_TDOP_GET_TYPE_NAME,
+ EXT_TDOP_OUTPUT_TYPE_NAME,
+ EXT_TDOP_OUTPUT_SIMPLE_VALUE,
+ EXT_TDOP_OUTPUT_FULL_VALUE,
+ EXT_TDOP_HAS_FIELD,
+ EXT_TDOP_GET_FIELD_OFFSET,
+ EXT_TDOP_GET_ARRAY_ELEMENT,
+ EXT_TDOP_GET_DEREFERENCE,
+ EXT_TDOP_GET_TYPE_SIZE,
+ EXT_TDOP_OUTPUT_TYPE_DEFINITION,
+ EXT_TDOP_GET_POINTER_TO,
+ EXT_TDOP_SET_FROM_TYPE_ID_AND_U64,
+ EXT_TDOP_SET_PTR_FROM_TYPE_ID_AND_U64,
+
+ EXT_TDOP_COUNT
+} EXT_TDOP;
+
+// EXT_TDF physical flags must match DEBUG_TYPED.
+#define EXT_TDF_PHYSICAL_DEFAULT 0x00000002
+#define EXT_TDF_PHYSICAL_CACHED 0x00000004
+#define EXT_TDF_PHYSICAL_UNCACHED 0x00000006
+#define EXT_TDF_PHYSICAL_WRITE_COMBINED 0x00000008
+#define EXT_TDF_PHYSICAL_MEMORY 0x0000000e
+
+// NOTE: Every DEBUG_TYPED_DATA should be released
+// via EXT_TDOP_RELEASE when it is no longer needed.
+typedef struct _EXT_TYPED_DATA {
+ IN EXT_TDOP Operation;
+ IN ULONG Flags;
+ IN DEBUG_TYPED_DATA InData;
+ OUT DEBUG_TYPED_DATA OutData;
+ IN ULONG InStrIndex;
+ IN ULONG In32;
+ OUT ULONG Out32;
+ IN ULONG64 In64;
+ OUT ULONG64 Out64;
+ OUT ULONG StrBufferIndex;
+ IN ULONG StrBufferChars;
+ OUT ULONG StrCharsNeeded;
+ IN OUT ULONG DataBufferIndex;
+ IN ULONG DataBufferBytes;
+ OUT ULONG DataBytesNeeded;
+ OUT HRESULT Status;
+ // Must be zeroed.
+ ULONG64 Reserved[8];
+} EXT_TYPED_DATA, *PEXT_TYPED_DATA;
+
+typedef struct _WDBGEXTS_QUERY_INTERFACE {
+ // Interface requested.
+ const IID* Iid;
+ // Interface pointer return.
+ PVOID Iface;
+} WDBGEXTS_QUERY_INTERFACE, *PWDBGEXTS_QUERY_INTERFACE;
+
+#define WDBGEXTS_ADDRESS_DEFAULT 0x00000000
+#define WDBGEXTS_ADDRESS_SEG16 0x00000001
+#define WDBGEXTS_ADDRESS_SEG32 0x00000002
+#define WDBGEXTS_ADDRESS_RESERVED0 0x80000000
+
+typedef struct _WDBGEXTS_DISASSEMBLE_BUFFER {
+ IN ULONG64 InOffset;
+ OUT ULONG64 OutOffset;
+ // AddrFlags are from above.
+ IN ULONG AddrFlags;
+ // FormatFlags are from dbgeng's DEBUG_DISASM_*.
+ IN ULONG FormatFlags;
+ IN ULONG DataBufferBytes;
+ IN ULONG DisasmBufferChars;
+ IN OPTIONAL PVOID DataBuffer;
+ OUT PWSTR DisasmBuffer;
+ IN ULONG64 Reserved0[3];
+} WDBGEXTS_DISASSEMBLE_BUFFER, *PWDBGEXTS_DISASSEMBLE_BUFFER;
+
+typedef struct _WDBGEXTS_MODULE_IN_RANGE {
+ IN ULONG64 Start;
+ // Inclusive ending offset.
+ IN ULONG64 End;
+ OUT ULONG64 FoundModBase;
+ OUT ULONG FoundModSize;
+} WDBGEXTS_MODULE_IN_RANGE, *PWDBGEXTS_MODULE_IN_RANGE;
+
+//
+// If DBGKD_VERS_FLAG_DATA is set in Flags, info should be retrieved from
+// the KDDEBUGGER_DATA block rather than from the DBGKD_GET_VERSION
+// packet. The data will remain in the version packet for a while to
+// reduce compatibility problems.
+//
+
+#define DBGKD_VERS_FLAG_MP 0x0001 // kernel is MP built
+#define DBGKD_VERS_FLAG_DATA 0x0002 // DebuggerDataList is valid
+#define DBGKD_VERS_FLAG_PTR64 0x0004 // native pointers are 64 bits
+#define DBGKD_VERS_FLAG_NOMM 0x0008 // No MM - don't decode PTEs
+#define DBGKD_VERS_FLAG_HSS 0x0010 // hardware stepping support
+#define DBGKD_VERS_FLAG_PARTITIONS 0x0020 // multiple OS partitions exist
+
+#define KDBG_TAG 'GBDK'
+
+//
+// KD version MajorVersion high-byte identifiers.
+//
+
+typedef enum _DBGKD_MAJOR_TYPES
+{
+ DBGKD_MAJOR_NT,
+ DBGKD_MAJOR_XBOX,
+ DBGKD_MAJOR_BIG,
+ DBGKD_MAJOR_EXDI,
+ DBGKD_MAJOR_NTBD,
+ DBGKD_MAJOR_EFI,
+ DBGKD_MAJOR_TNT,
+ DBGKD_MAJOR_SINGULARITY,
+ DBGKD_MAJOR_HYPERVISOR,
+ DBGKD_MAJOR_COUNT
+} DBGKD_MAJOR_TYPES;
+
+#define DBGKD_MAJOR_TYPE(MajorVersion) \
+ ((DBGKD_MAJOR_TYPES)((MajorVersion) >> 8))
+
+
+// **********************************************************************
+// DO NOT CHANGE THESE 32 BIT STRUCTURES!
+// ONLY MAKE CHAGES TO THE 64 BIT VERSION BELOW!!
+// **********************************************************************
+
+//
+// The following structure has changed in more than pointer size.
+//
+// This is the version packet for pre-NT5 Beta 2 systems.
+// For now, it is also still used on x86
+//
+typedef struct _DBGKD_GET_VERSION32 {
+ USHORT MajorVersion;
+ USHORT MinorVersion;
+ USHORT ProtocolVersion;
+ USHORT Flags;
+ ULONG KernBase;
+ ULONG PsLoadedModuleList;
+
+ USHORT MachineType;
+
+ //
+ // help for walking stacks with user callbacks:
+ //
+
+ //
+ // The address of the thread structure is provided in the
+ // WAIT_STATE_CHANGE packet. This is the offset from the base of
+ // the thread structure to the pointer to the kernel stack frame
+ // for the currently active usermode callback.
+ //
+
+ USHORT ThCallbackStack; // offset in thread data
+
+ //
+ // these values are offsets into that frame:
+ //
+
+ USHORT NextCallback; // saved pointer to next callback frame
+ USHORT FramePointer; // saved frame pointer
+
+ //
+ // Address of the kernel callout routine.
+ //
+
+ ULONG KiCallUserMode; // kernel routine
+
+ //
+ // Address of the usermode entry point for callbacks.
+ //
+
+ ULONG KeUserCallbackDispatcher; // address in ntdll
+
+ //
+ // DbgBreakPointWithStatus is a function which takes a ULONG argument
+ // and hits a breakpoint. This field contains the address of the
+ // breakpoint instruction. When the debugger sees a breakpoint
+ // at this address, it may retrieve the argument from the first
+ // argument register, or on x86 the eax register.
+ //
+
+ ULONG BreakpointWithStatus; // address of breakpoint
+
+ //
+ // Components may register a debug data block for use by
+ // debugger extensions. This is the address of the list head.
+ //
+
+ ULONG DebuggerDataList;
+
+} DBGKD_GET_VERSION32, *PDBGKD_GET_VERSION32;
+
+
+//
+// This is the debugger data packet for pre NT5 Beta 2 systems.
+// For now, it is still used on x86
+//
+
+typedef struct _DBGKD_DEBUG_DATA_HEADER32 {
+
+ LIST_ENTRY32 List;
+ ULONG OwnerTag;
+ ULONG Size;
+
+} DBGKD_DEBUG_DATA_HEADER32, *PDBGKD_DEBUG_DATA_HEADER32;
+
+typedef struct _KDDEBUGGER_DATA32 {
+
+ DBGKD_DEBUG_DATA_HEADER32 Header;
+ ULONG KernBase;
+ ULONG BreakpointWithStatus; // address of breakpoint
+ ULONG SavedContext;
+ USHORT ThCallbackStack; // offset in thread data
+ USHORT NextCallback; // saved pointer to next callback frame
+ USHORT FramePointer; // saved frame pointer
+ USHORT PaeEnabled:1;
+ ULONG KiCallUserMode; // kernel routine
+ ULONG KeUserCallbackDispatcher; // address in ntdll
+
+ ULONG PsLoadedModuleList;
+ ULONG PsActiveProcessHead;
+ ULONG PspCidTable;
+
+ ULONG ExpSystemResourcesList;
+ ULONG ExpPagedPoolDescriptor;
+ ULONG ExpNumberOfPagedPools;
+
+ ULONG KeTimeIncrement;
+ ULONG KeBugCheckCallbackListHead;
+ ULONG KiBugcheckData;
+
+ ULONG IopErrorLogListHead;
+
+ ULONG ObpRootDirectoryObject;
+ ULONG ObpTypeObjectType;
+
+ ULONG MmSystemCacheStart;
+ ULONG MmSystemCacheEnd;
+ ULONG MmSystemCacheWs;
+
+ ULONG MmPfnDatabase;
+ ULONG MmSystemPtesStart;
+ ULONG MmSystemPtesEnd;
+ ULONG MmSubsectionBase;
+ ULONG MmNumberOfPagingFiles;
+
+ ULONG MmLowestPhysicalPage;
+ ULONG MmHighestPhysicalPage;
+ ULONG MmNumberOfPhysicalPages;
+
+ ULONG MmMaximumNonPagedPoolInBytes;
+ ULONG MmNonPagedSystemStart;
+ ULONG MmNonPagedPoolStart;
+ ULONG MmNonPagedPoolEnd;
+
+ ULONG MmPagedPoolStart;
+ ULONG MmPagedPoolEnd;
+ ULONG MmPagedPoolInformation;
+ ULONG MmPageSize;
+
+ ULONG MmSizeOfPagedPoolInBytes;
+
+ ULONG MmTotalCommitLimit;
+ ULONG MmTotalCommittedPages;
+ ULONG MmSharedCommit;
+ ULONG MmDriverCommit;
+ ULONG MmProcessCommit;
+ ULONG MmPagedPoolCommit;
+ ULONG MmExtendedCommit;
+
+ ULONG MmZeroedPageListHead;
+ ULONG MmFreePageListHead;
+ ULONG MmStandbyPageListHead;
+ ULONG MmModifiedPageListHead;
+ ULONG MmModifiedNoWritePageListHead;
+ ULONG MmAvailablePages;
+ ULONG MmResidentAvailablePages;
+
+ ULONG PoolTrackTable;
+ ULONG NonPagedPoolDescriptor;
+
+ ULONG MmHighestUserAddress;
+ ULONG MmSystemRangeStart;
+ ULONG MmUserProbeAddress;
+
+ ULONG KdPrintCircularBuffer;
+ ULONG KdPrintCircularBufferEnd;
+ ULONG KdPrintWritePointer;
+ ULONG KdPrintRolloverCount;
+
+ ULONG MmLoadedUserImageList;
+
+} KDDEBUGGER_DATA32, *PKDDEBUGGER_DATA32;
+
+// **********************************************************************
+//
+// DO NOT CHANGE KDDEBUGGER_DATA32!!
+// ONLY MAKE CHANGES TO KDDEBUGGER_DATA64!!!
+//
+// **********************************************************************
+
+
+enum
+{
+ DBGKD_SIMULATION_NONE,
+ DBGKD_SIMULATION_EXDI
+};
+
+#define KD_SECONDARY_VERSION_DEFAULT 0
+
+#define KD_SECONDARY_VERSION_AMD64_OBSOLETE_CONTEXT_1 0
+#define KD_SECONDARY_VERSION_AMD64_OBSOLETE_CONTEXT_2 1
+#define KD_SECONDARY_VERSION_AMD64_CONTEXT 2
+
+#ifdef _AMD64_
+#define CURRENT_KD_SECONDARY_VERSION \
+ KD_SECONDARY_VERSION_AMD64_CONTEXT
+#else
+#define CURRENT_KD_SECONDARY_VERSION KD_SECONDARY_VERSION_DEFAULT
+#endif
+
+typedef struct _DBGKD_GET_VERSION64 {
+ USHORT MajorVersion;
+ USHORT MinorVersion;
+ UCHAR ProtocolVersion;
+ UCHAR KdSecondaryVersion; // Cannot be 'A' for compat with dump header
+ USHORT Flags;
+ USHORT MachineType;
+
+ //
+ // Protocol command support descriptions.
+ // These allow the debugger to automatically
+ // adapt to different levels of command support
+ // in different kernels.
+ //
+
+ // One beyond highest packet type understood, zero based.
+ UCHAR MaxPacketType;
+ // One beyond highest state change understood, zero based.
+ UCHAR MaxStateChange;
+ // One beyond highest state manipulate message understood, zero based.
+ UCHAR MaxManipulate;
+
+ // Kind of execution environment the kernel is running in,
+ // such as a real machine or a simulator. Written back
+ // by the simulation if one exists.
+ UCHAR Simulation;
+
+ USHORT Unused[1];
+
+ ULONG64 KernBase;
+ ULONG64 PsLoadedModuleList;
+
+ //
+ // Components may register a debug data block for use by
+ // debugger extensions. This is the address of the list head.
+ //
+ // There will always be an entry for the debugger.
+ //
+
+ ULONG64 DebuggerDataList;
+
+} DBGKD_GET_VERSION64, *PDBGKD_GET_VERSION64;
+
+
+//
+// This structure is used by the debugger for all targets
+// It is the same size as DBGKD_DATA_HEADER on all systems
+//
+typedef struct _DBGKD_DEBUG_DATA_HEADER64 {
+
+ //
+ // Link to other blocks
+ //
+
+ LIST_ENTRY64 List;
+
+ //
+ // This is a unique tag to identify the owner of the block.
+ // If your component only uses one pool tag, use it for this, too.
+ //
+
+ ULONG OwnerTag;
+
+ //
+ // This must be initialized to the size of the data block,
+ // including this structure.
+ //
+
+ ULONG Size;
+
+} DBGKD_DEBUG_DATA_HEADER64, *PDBGKD_DEBUG_DATA_HEADER64;
+
+
+//
+// This structure is the same size on all systems. The only field
+// which must be translated by the debugger is Header.List.
+//
+
+//
+// DO NOT ADD OR REMOVE FIELDS FROM THE MIDDLE OF THIS STRUCTURE!!!
+//
+// If you remove a field, replace it with an "unused" placeholder.
+// Do not reuse fields until there has been enough time for old debuggers
+// and extensions to age out.
+//
+typedef struct _KDDEBUGGER_DATA64 {
+
+ DBGKD_DEBUG_DATA_HEADER64 Header;
+
+ //
+ // Base address of kernel image
+ //
+
+ ULONG64 KernBase;
+
+ //
+ // DbgBreakPointWithStatus is a function which takes an argument
+ // and hits a breakpoint. This field contains the address of the
+ // breakpoint instruction. When the debugger sees a breakpoint
+ // at this address, it may retrieve the argument from the first
+ // argument register, or on x86 the eax register.
+ //
+
+ ULONG64 BreakpointWithStatus; // address of breakpoint
+
+ //
+ // Address of the saved context record during a bugcheck
+ //
+ // N.B. This is an automatic in KeBugcheckEx's frame, and
+ // is only valid after a bugcheck.
+ //
+
+ ULONG64 SavedContext;
+
+ //
+ // help for walking stacks with user callbacks:
+ //
+
+ //
+ // The address of the thread structure is provided in the
+ // WAIT_STATE_CHANGE packet. This is the offset from the base of
+ // the thread structure to the pointer to the kernel stack frame
+ // for the currently active usermode callback.
+ //
+
+ USHORT ThCallbackStack; // offset in thread data
+
+ //
+ // these values are offsets into that frame:
+ //
+
+ USHORT NextCallback; // saved pointer to next callback frame
+ USHORT FramePointer; // saved frame pointer
+
+ //
+ // pad to a quad boundary
+ //
+ USHORT PaeEnabled:1;
+
+ //
+ // Address of the kernel callout routine.
+ //
+
+ ULONG64 KiCallUserMode; // kernel routine
+
+ //
+ // Address of the usermode entry point for callbacks.
+ //
+
+ ULONG64 KeUserCallbackDispatcher; // address in ntdll
+
+
+ //
+ // Addresses of various kernel data structures and lists
+ // that are of interest to the kernel debugger.
+ //
+
+ ULONG64 PsLoadedModuleList;
+ ULONG64 PsActiveProcessHead;
+ ULONG64 PspCidTable;
+
+ ULONG64 ExpSystemResourcesList;
+ ULONG64 ExpPagedPoolDescriptor;
+ ULONG64 ExpNumberOfPagedPools;
+
+ ULONG64 KeTimeIncrement;
+ ULONG64 KeBugCheckCallbackListHead;
+ ULONG64 KiBugcheckData;
+
+ ULONG64 IopErrorLogListHead;
+
+ ULONG64 ObpRootDirectoryObject;
+ ULONG64 ObpTypeObjectType;
+
+ ULONG64 MmSystemCacheStart;
+ ULONG64 MmSystemCacheEnd;
+ ULONG64 MmSystemCacheWs;
+
+ ULONG64 MmPfnDatabase;
+ ULONG64 MmSystemPtesStart;
+ ULONG64 MmSystemPtesEnd;
+ ULONG64 MmSubsectionBase;
+ ULONG64 MmNumberOfPagingFiles;
+
+ ULONG64 MmLowestPhysicalPage;
+ ULONG64 MmHighestPhysicalPage;
+ ULONG64 MmNumberOfPhysicalPages;
+
+ ULONG64 MmMaximumNonPagedPoolInBytes;
+ ULONG64 MmNonPagedSystemStart;
+ ULONG64 MmNonPagedPoolStart;
+ ULONG64 MmNonPagedPoolEnd;
+
+ ULONG64 MmPagedPoolStart;
+ ULONG64 MmPagedPoolEnd;
+ ULONG64 MmPagedPoolInformation;
+ ULONG64 MmPageSize;
+
+ ULONG64 MmSizeOfPagedPoolInBytes;
+
+ ULONG64 MmTotalCommitLimit;
+ ULONG64 MmTotalCommittedPages;
+ ULONG64 MmSharedCommit;
+ ULONG64 MmDriverCommit;
+ ULONG64 MmProcessCommit;
+ ULONG64 MmPagedPoolCommit;
+ ULONG64 MmExtendedCommit;
+
+ ULONG64 MmZeroedPageListHead;
+ ULONG64 MmFreePageListHead;
+ ULONG64 MmStandbyPageListHead;
+ ULONG64 MmModifiedPageListHead;
+ ULONG64 MmModifiedNoWritePageListHead;
+ ULONG64 MmAvailablePages;
+ ULONG64 MmResidentAvailablePages;
+
+ ULONG64 PoolTrackTable;
+ ULONG64 NonPagedPoolDescriptor;
+
+ ULONG64 MmHighestUserAddress;
+ ULONG64 MmSystemRangeStart;
+ ULONG64 MmUserProbeAddress;
+
+ ULONG64 KdPrintCircularBuffer;
+ ULONG64 KdPrintCircularBufferEnd;
+ ULONG64 KdPrintWritePointer;
+ ULONG64 KdPrintRolloverCount;
+
+ ULONG64 MmLoadedUserImageList;
+
+ // NT 5.1 Addition
+
+ ULONG64 NtBuildLab;
+ ULONG64 KiNormalSystemCall;
+
+ // NT 5.0 hotfix addition
+
+ ULONG64 KiProcessorBlock;
+ ULONG64 MmUnloadedDrivers;
+ ULONG64 MmLastUnloadedDriver;
+ ULONG64 MmTriageActionTaken;
+ ULONG64 MmSpecialPoolTag;
+ ULONG64 KernelVerifier;
+ ULONG64 MmVerifierData;
+ ULONG64 MmAllocatedNonPagedPool;
+ ULONG64 MmPeakCommitment;
+ ULONG64 MmTotalCommitLimitMaximum;
+ ULONG64 CmNtCSDVersion;
+
+ // NT 5.1 Addition
+
+ ULONG64 MmPhysicalMemoryBlock;
+ ULONG64 MmSessionBase;
+ ULONG64 MmSessionSize;
+ ULONG64 MmSystemParentTablePage;
+
+ // Server 2003 addition
+
+ ULONG64 MmVirtualTranslationBase;
+
+ USHORT OffsetKThreadNextProcessor;
+ USHORT OffsetKThreadTeb;
+ USHORT OffsetKThreadKernelStack;
+ USHORT OffsetKThreadInitialStack;
+
+ USHORT OffsetKThreadApcProcess;
+ USHORT OffsetKThreadState;
+ USHORT OffsetKThreadBStore;
+ USHORT OffsetKThreadBStoreLimit;
+
+ USHORT SizeEProcess;
+ USHORT OffsetEprocessPeb;
+ USHORT OffsetEprocessParentCID;
+ USHORT OffsetEprocessDirectoryTableBase;
+
+ USHORT SizePrcb;
+ USHORT OffsetPrcbDpcRoutine;
+ USHORT OffsetPrcbCurrentThread;
+ USHORT OffsetPrcbMhz;
+
+ USHORT OffsetPrcbCpuType;
+ USHORT OffsetPrcbVendorString;
+ USHORT OffsetPrcbProcStateContext;
+ USHORT OffsetPrcbNumber;
+
+ USHORT SizeEThread;
+
+ ULONG64 KdPrintCircularBufferPtr;
+ ULONG64 KdPrintBufferSize;
+
+ ULONG64 KeLoaderBlock;
+
+ USHORT SizePcr;
+ USHORT OffsetPcrSelfPcr;
+ USHORT OffsetPcrCurrentPrcb;
+ USHORT OffsetPcrContainedPrcb;
+
+ USHORT OffsetPcrInitialBStore;
+ USHORT OffsetPcrBStoreLimit;
+ USHORT OffsetPcrInitialStack;
+ USHORT OffsetPcrStackLimit;
+
+ USHORT OffsetPrcbPcrPage;
+ USHORT OffsetPrcbProcStateSpecialReg;
+ USHORT GdtR0Code;
+ USHORT GdtR0Data;
+
+ USHORT GdtR0Pcr;
+ USHORT GdtR3Code;
+ USHORT GdtR3Data;
+ USHORT GdtR3Teb;
+
+ USHORT GdtLdt;
+ USHORT GdtTss;
+ USHORT Gdt64R3CmCode;
+ USHORT Gdt64R3CmTeb;
+
+ ULONG64 IopNumTriageDumpDataBlocks;
+ ULONG64 IopTriageDumpDataBlocks;
+
+ // Longhorn addition
+
+ ULONG64 VfCrashDataBlock;
+ ULONG64 MmBadPagesDetected;
+ ULONG64 MmZeroedPageSingleBitErrorsDetected;
+
+
+} KDDEBUGGER_DATA64, *PKDDEBUGGER_DATA64;
+
+
+
+/************************************
+
+ Type Dump Ioctl
+
+*************************************/
+
+
+//
+// Fields are not indented if this is set
+//
+#define DBG_DUMP_NO_INDENT 0x00000001
+//
+// Offsets are not printed if this is set
+//
+#define DBG_DUMP_NO_OFFSET 0x00000002
+//
+// Verbose output
+//
+#define DBG_DUMP_VERBOSE 0x00000004
+//
+// Callback is done for each of fields
+//
+#define DBG_DUMP_CALL_FOR_EACH 0x00000008
+//
+// A list of type is dumped, listLink should have info about next element pointer
+//
+#define DBG_DUMP_LIST 0x00000020
+//
+// Nothing is printed if this is set (only callbacks and data copies done)
+//
+#define DBG_DUMP_NO_PRINT 0x00000040
+//
+// Ioctl returns the size as usual, but will not do field prints/callbacks if this is set
+//
+#define DBG_DUMP_GET_SIZE_ONLY 0x00000080
+//
+// Specifies how much deep into structs we can go
+//
+#define DBG_DUMP_RECUR_LEVEL(l) ((l & 0xf) << 8)
+//
+// No newlines are printed after each field
+//
+#define DBG_DUMP_COMPACT_OUT 0x00002000
+//
+// An array of type is dumped, number of elements can be specified in listLink->size
+//
+#define DBG_DUMP_ARRAY 0x00008000
+//
+// The specified addr value is actually the address of field listLink->fName
+//
+#define DBG_DUMP_ADDRESS_OF_FIELD 0x00010000
+
+//
+// The specified addr value is actually the adress at the end of type
+//
+#define DBG_DUMP_ADDRESS_AT_END 0x00020000
+
+//
+// This could be used to copy only the primitive types like ULONG, PVOID etc.
+// - will not work with structures/unions
+//
+#define DBG_DUMP_COPY_TYPE_DATA 0x00040000
+//
+// Flag to allow read directly from physical memory
+//
+#define DBG_DUMP_READ_PHYSICAL 0x00080000
+//
+// This causes a function type to be dumped in format function(arg1, arg2, ...)
+//
+#define DBG_DUMP_FUNCTION_FORMAT 0x00100000
+//
+// This recurses on a struct but doesn't expand pointers
+//
+#define DBG_DUMP_BLOCK_RECURSE 0x00200000
+//
+// Match the type size to resolve ambiguity in case multiple matches with same name are available
+//
+#define DBG_DUMP_MATCH_SIZE 0x00400000
+
+//
+// Obsolete defs
+//
+#define DBG_RETURN_TYPE 0
+#define DBG_RETURN_SUBTYPES 0
+#define DBG_RETURN_TYPE_VALUES 0
+
+//
+// Dump and callback optons for fields - Options used in FIELD_INFO.fOptions
+//
+
+//
+// Callback is done before printing the field if this is set
+//
+#define DBG_DUMP_FIELD_CALL_BEFORE_PRINT 0x00000001
+//
+// No callback is done
+//
+#define DBG_DUMP_FIELD_NO_CALLBACK_REQ 0x00000002
+//
+// Subfields of the fields are processesed
+//
+#define DBG_DUMP_FIELD_RECUR_ON_THIS 0x00000004
+//
+// fName must match completely for the field to be dumped instead just a prefix
+// match by default
+//
+#define DBG_DUMP_FIELD_FULL_NAME 0x00000008
+//
+// This causes array elements of an array field to be printed
+//
+#define DBG_DUMP_FIELD_ARRAY 0x00000010
+//
+// The data of the field is copied into fieldCallBack
+//
+#define DBG_DUMP_FIELD_COPY_FIELD_DATA 0x00000020
+//
+// In callback or when Ioctl returns, the FIELD_INFO.address has the address of field.
+// If no address is supplied for the type, it contains total offset of the field.
+//
+#define DBG_DUMP_FIELD_RETURN_ADDRESS 0x00001000
+//
+// Return the offset and size in bits instead of bytes is case of Bitfield
+//
+#define DBG_DUMP_FIELD_SIZE_IN_BITS 0x00002000
+//
+// Nothing is printed for field if this is set (only callbacks and data copies done)
+//
+#define DBG_DUMP_FIELD_NO_PRINT 0x00004000
+//
+// If the field is a pointer, it is dumped as a string, ANSI, WCHAR, MULTI or GUID
+// depending on following options
+//
+#define DBG_DUMP_FIELD_DEFAULT_STRING 0x00010000
+#define DBG_DUMP_FIELD_WCHAR_STRING 0x00020000
+#define DBG_DUMP_FIELD_MULTI_STRING 0x00040000
+#define DBG_DUMP_FIELD_GUID_STRING 0x00080000
+
+
+//
+// Error status returned on TYPE DUMP Ioctl failure
+//
+#define MEMORY_READ_ERROR 0x01
+#define SYMBOL_TYPE_INDEX_NOT_FOUND 0x02
+#define SYMBOL_TYPE_INFO_NOT_FOUND 0x03
+#define FIELDS_DID_NOT_MATCH 0x04
+#define NULL_SYM_DUMP_PARAM 0x05
+#define NULL_FIELD_NAME 0x06
+#define INCORRECT_VERSION_INFO 0x07
+#define EXIT_ON_CONTROLC 0x08
+#define CANNOT_ALLOCATE_MEMORY 0x09
+#define INSUFFICIENT_SPACE_TO_COPY 0x0a
+#define ADDRESS_TYPE_INDEX_NOT_FOUND 0x0b
+
+
+//////////////////////////////////////////////////////////////////////////*/
+
+
+typedef
+ULONG
+(WDBGAPI*PSYM_DUMP_FIELD_CALLBACK)(
+ struct _FIELD_INFO *pField,
+ PVOID UserContext
+ );
+
+typedef struct _FIELD_INFO {
+ PUCHAR fName; // Name of the field
+ PUCHAR printName; // Name to be printed at dump
+ ULONG size; // Size of the field
+ ULONG fOptions; // Dump Options for the field
+ ULONG64 address; // address of the field
+ union {
+ PVOID fieldCallBack; // Return info or callBack routine for the field
+ PVOID pBuffer; // the type data is copied into this
+ };
+ ULONG TypeId; // OUT Type index of the field
+ ULONG FieldOffset; // OUT Offset of field inside struct
+ ULONG BufferSize; // size of buffer used with DBG_DUMP_FIELD_COPY_FIELD_DATA
+ struct _BitField {
+ USHORT Position; // OUT set to start position for bitfield
+ USHORT Size; // OUT set to size for bitfields
+ } BitField;
+ ULONG fPointer:2; // OUT set to 1 for pointers, 3 for 64bit pointers
+ ULONG fArray:1; // OUT set to 1 for array types
+ ULONG fStruct:1; // OUT set to 1 for struct/class tyoes
+ ULONG fConstant:1; // OUT set to 1 for constants (enumerate as fields)
+ ULONG fStatic:1; // OUT set to 1 for statics (class/struct static members)
+ ULONG Reserved:26; // unused
+} FIELD_INFO, *PFIELD_INFO;
+
+typedef struct _SYM_DUMP_PARAM {
+ ULONG size; // size of this struct
+ PUCHAR sName; // type name
+ ULONG Options; // Dump options
+ ULONG64 addr; // Address to take data for type
+ PFIELD_INFO listLink; // fName here would be used to do list dump
+ union {
+ PVOID Context; // Usercontext passed to CallbackRoutine
+ PVOID pBuffer; // the type data is copied into this
+ };
+ PSYM_DUMP_FIELD_CALLBACK CallbackRoutine;
+ // Routine called back
+ ULONG nFields; // # elements in Fields
+ __field_ecount_opt(nFields) PFIELD_INFO Fields; // Used to return information about field
+ ULONG64 ModBase; // OUT Module base address containing type
+ ULONG TypeId; // OUT Type index of the symbol
+ ULONG TypeSize; // OUT Size of type
+ ULONG BufferSize; // IN size of buffer (used with DBG_DUMP_COPY_TYPE_DATA)
+ ULONG fPointer:2; // OUT set to 1 for pointers, 3 for 64bit pointers
+ ULONG fArray:1; // OUT set to 1 for array types
+ ULONG fStruct:1; // OUT set to 1 for struct/class tyoes
+ ULONG fConstant:1; // OUT set to 1 for constant types (unused)
+ ULONG Reserved:27; // unused
+} SYM_DUMP_PARAM, *PSYM_DUMP_PARAM;
+
+#ifdef __cplusplus
+#define CPPMOD extern "C"
+#else
+#define CPPMOD
+#endif
+
+
+#ifndef NOEXTAPI
+
+#if defined(KDEXT_64BIT)
+#define WINDBG_EXTENSION_APIS WINDBG_EXTENSION_APIS64
+#define PWINDBG_EXTENSION_APIS PWINDBG_EXTENSION_APIS64
+#define PWINDBG_EXTENSION_ROUTINE PWINDBG_EXTENSION_ROUTINE64
+#define DECLARE_API(s) DECLARE_API64(s)
+#elif defined(KDEXT_32BIT)
+#define WINDBG_EXTENSION_APIS WINDBG_EXTENSION_APIS32
+#define PWINDBG_EXTENSION_APIS PWINDBG_EXTENSION_APIS32
+#define PWINDBG_EXTENSION_ROUTINE PWINDBG_EXTENSION_ROUTINE32
+#define DECLARE_API(s) DECLARE_API32(s)
+#else
+#define DECLARE_API(s) \
+ CPPMOD VOID \
+ s( \
+ HANDLE hCurrentProcess, \
+ HANDLE hCurrentThread, \
+ ULONG dwCurrentPc, \
+ ULONG dwProcessor, \
+ PCSTR args \
+ )
+#endif
+
+#define DECLARE_API32(s) \
+ CPPMOD VOID \
+ s( \
+ HANDLE hCurrentProcess, \
+ HANDLE hCurrentThread, \
+ ULONG dwCurrentPc, \
+ ULONG dwProcessor, \
+ PCSTR args \
+ )
+
+#define DECLARE_API64(s) \
+ CPPMOD VOID \
+ s( \
+ HANDLE hCurrentProcess, \
+ HANDLE hCurrentThread, \
+ ULONG64 dwCurrentPc, \
+ ULONG dwProcessor, \
+ PCSTR args \
+ )
+
+
+extern WINDBG_EXTENSION_APIS ExtensionApis;
+
+
+#define dprintf (ExtensionApis.lpOutputRoutine)
+#define GetExpression (ExtensionApis.lpGetExpressionRoutine)
+#define CheckControlC (ExtensionApis.lpCheckControlCRoutine)
+#define GetContext (ExtensionApis.lpGetThreadContextRoutine)
+#define SetContext (ExtensionApis.lpSetThreadContextRoutine)
+#define Ioctl (ExtensionApis.lpIoctlRoutine)
+#define Disasm (ExtensionApis.lpDisasmRoutine)
+#define GetSymbol (ExtensionApis.lpGetSymbolRoutine)
+#define ReadMemory (ExtensionApis.lpReadProcessMemoryRoutine)
+#define WriteMemory (ExtensionApis.lpWriteProcessMemoryRoutine)
+#define StackTrace (ExtensionApis.lpStackTraceRoutine)
+
+
+#define GetKdContext(ppi) \
+ Ioctl( IG_KD_CONTEXT, (PVOID)ppi, sizeof(*ppi) )
+
+
+//
+// BOOL
+// GetDebuggerData(
+// ULONG Tag,
+// PVOID Buf,
+// ULONG Size
+// )
+//
+
+#define GetDebuggerData(TAG, BUF, SIZE) \
+ ( (((PDBGKD_DEBUG_DATA_HEADER64)(BUF))->OwnerTag = (TAG)), \
+ (((PDBGKD_DEBUG_DATA_HEADER64)(BUF))->Size = (SIZE)), \
+ Ioctl( IG_GET_DEBUGGER_DATA, (PVOID)(BUF), (SIZE) ) )
+
+// Check if LocalAlloc is prototyped
+//#ifdef _WINBASE_
+
+#ifndef FEATURE_PAL
+__inline VOID
+ReadPhysical(
+ ULONG64 address,
+ PVOID buf,
+ ULONG size,
+ PULONG sizer
+ )
+{
+ PPHYSICAL phy = NULL;
+ *sizer = 0;
+ if (size <= WDBGEXTS_MAXSIZE_T - sizeof(*phy)) {
+ phy = (PPHYSICAL)LocalAlloc(LPTR, sizeof(*phy) + size );
+ }
+ if (phy) {
+ ZeroMemory( phy->Buf, size );
+ phy->Address = address;
+ phy->BufLen = size;
+ Ioctl( IG_READ_PHYSICAL, (PVOID)phy, sizeof(*phy) + size );
+ *sizer = phy->BufLen;
+ CopyMemory( buf, phy->Buf, *sizer );
+ LocalFree( phy );
+ }
+}
+
+__inline VOID
+WritePhysical(
+ ULONG64 address,
+ PVOID buf,
+ ULONG size,
+ PULONG sizew
+ )
+{
+ PPHYSICAL phy = NULL;
+ *sizew = 0;
+ if (size <= WDBGEXTS_MAXSIZE_T - sizeof(*phy)) {
+ phy = (PPHYSICAL)LocalAlloc(LPTR, sizeof(*phy) + size );
+ }
+ if (phy) {
+ ZeroMemory( phy->Buf, size );
+ phy->Address = address;
+ phy->BufLen = size;
+ CopyMemory( phy->Buf, buf, size );
+ Ioctl( IG_WRITE_PHYSICAL, (PVOID)phy, sizeof(*phy) + size );
+ *sizew = phy->BufLen;
+ LocalFree( phy );
+ }
+}
+
+__inline VOID
+ReadPhysicalWithFlags(
+ ULONG64 address,
+ PVOID buf,
+ ULONG size,
+ ULONG flags,
+ PULONG sizer
+ )
+{
+ PPHYSICAL_WITH_FLAGS phy = NULL;
+ *sizer = 0;
+ if (size <= WDBGEXTS_MAXSIZE_T - sizeof(*phy)) {
+ phy = (PPHYSICAL_WITH_FLAGS)LocalAlloc(LPTR, sizeof(*phy) + size );
+ }
+ if (phy) {
+ ZeroMemory( phy->Buf, size );
+ phy->Address = address;
+ phy->BufLen = size;
+ phy->Flags = flags;
+ Ioctl( IG_READ_PHYSICAL_WITH_FLAGS, (PVOID)phy, sizeof(*phy) + size );
+ *sizer = phy->BufLen;
+ CopyMemory( buf, phy->Buf, *sizer );
+ LocalFree( phy );
+ }
+}
+
+__inline VOID
+WritePhysicalWithFlags(
+ ULONG64 address,
+ PVOID buf,
+ ULONG size,
+ ULONG flags,
+ PULONG sizew
+ )
+{
+ PPHYSICAL_WITH_FLAGS phy = NULL;
+ *sizew = 0;
+ if (size <= WDBGEXTS_MAXSIZE_T - sizeof(*phy)) {
+ phy = (PPHYSICAL_WITH_FLAGS)LocalAlloc(LPTR, sizeof(*phy) + size );
+ }
+ if (phy) {
+ ZeroMemory( phy->Buf, size );
+ phy->Address = address;
+ phy->BufLen = size;
+ phy->Flags = flags;
+ CopyMemory( phy->Buf, buf, size );
+ Ioctl( IG_WRITE_PHYSICAL_WITH_FLAGS, (PVOID)phy, sizeof(*phy) + size );
+ *sizew = phy->BufLen;
+ LocalFree( phy );
+ }
+}
+
+__inline VOID
+ReadMsr(
+ ULONG MsrReg,
+ ULONGLONG *MsrValue
+ )
+{
+ READ_WRITE_MSR msr;
+
+ msr.Msr = MsrReg;
+ Ioctl( IG_READ_MSR, (PVOID)&msr, sizeof(msr) );
+
+ *MsrValue = msr.Value;
+}
+
+__inline VOID
+WriteMsr(
+ ULONG MsrReg,
+ ULONGLONG MsrValue
+ )
+{
+ READ_WRITE_MSR msr;
+
+ msr.Msr = MsrReg;
+ msr.Value = MsrValue;
+ Ioctl( IG_WRITE_MSR, (PVOID)&msr, sizeof(msr) );
+}
+
+__inline VOID
+SetThreadForOperation(
+ ULONG_PTR * Thread
+ )
+{
+ Ioctl(IG_SET_THREAD, (PVOID)Thread, sizeof(PULONG));
+}
+
+__inline VOID
+SetThreadForOperation32(
+ ULONG Thread
+ )
+{
+ Ioctl(IG_SET_THREAD, (PVOID)LongToPtr(Thread), sizeof(ULONG));
+}
+
+__inline VOID
+SetThreadForOperation64(
+ PULONG64 Thread
+ )
+{
+ Ioctl(IG_SET_THREAD, (PVOID)Thread, sizeof(ULONG64));
+}
+
+
+__inline VOID
+ReadControlSpace(
+ USHORT processor,
+ ULONG address,
+ PVOID buf,
+ ULONG size
+ )
+{
+ PREADCONTROLSPACE prc = NULL;
+ if (size <= WDBGEXTS_MAXSIZE_T - sizeof(*prc)) {
+ prc = (PREADCONTROLSPACE)LocalAlloc(LPTR, sizeof(*prc) + size );
+ }
+ if (prc) {
+ ZeroMemory( prc->Buf, size );
+ prc->Processor = processor;
+ prc->Address = address;
+ prc->BufLen = size;
+ Ioctl( IG_READ_CONTROL_SPACE, (PVOID)prc, sizeof(*prc) + size );
+ CopyMemory( buf, prc->Buf, size );
+ LocalFree( prc );
+ }
+}
+
+__inline VOID
+ReadControlSpace32(
+ USHORT processor,
+ ULONG address,
+ PVOID buf,
+ ULONG size
+ )
+{
+ PREADCONTROLSPACE32 prc = NULL;
+ if (size <= WDBGEXTS_MAXSIZE_T - sizeof(*prc)) {
+ prc = (PREADCONTROLSPACE32)LocalAlloc(LPTR, sizeof(*prc) + size );
+ }
+ if (prc) {
+ ZeroMemory( prc->Buf, size );
+ prc->Processor = processor;
+ prc->Address = address;
+ prc->BufLen = size;
+ Ioctl( IG_READ_CONTROL_SPACE, (PVOID)prc, sizeof(*prc) + size );
+ CopyMemory( buf, prc->Buf, size );
+ LocalFree( prc );
+ }
+}
+
+#define ReadTypedControlSpace32( _Proc, _Addr, _Buf ) \
+ ReadControlSpace64( (USHORT)(_Proc), (ULONG)(_Addr), (PVOID)&(_Buf), (ULONG)sizeof(_Buf) )
+
+__inline VOID
+ReadControlSpace64(
+ USHORT processor,
+ ULONG64 address,
+ PVOID buf,
+ ULONG size
+ )
+{
+ PREADCONTROLSPACE64 prc = NULL;
+ if (size <= WDBGEXTS_MAXSIZE_T - sizeof(*prc)) {
+ prc = (PREADCONTROLSPACE64)LocalAlloc(LPTR, sizeof(*prc) + size );
+ }
+ if (prc) {
+ ZeroMemory( prc->Buf, size );
+ prc->Processor = processor;
+ prc->Address = address;
+ prc->BufLen = size;
+ Ioctl( IG_READ_CONTROL_SPACE, (PVOID)prc, sizeof(*prc) + size );
+ CopyMemory( buf, prc->Buf, size );
+ LocalFree( prc );
+ }
+}
+
+#define ReadTypedControlSpace64( _Proc, _Addr, _Buf ) \
+ ReadControlSpace64( (USHORT)(_Proc), (ULONG64)(_Addr), (PVOID)&(_Buf), (ULONG)sizeof(_Buf) )
+
+__inline VOID
+WriteControlSpace(
+ USHORT processor,
+ ULONG address,
+ PVOID buf,
+ ULONG size
+ )
+{
+ PREADCONTROLSPACE64 prc = NULL;
+ if (size <= WDBGEXTS_MAXSIZE_T - sizeof(*prc)) {
+ prc = (PREADCONTROLSPACE64)LocalAlloc(LPTR, sizeof(*prc) + size );
+ }
+ if (prc) {
+ ZeroMemory( prc->Buf, size );
+ prc->Processor = processor;
+ prc->Address = address;
+ prc->BufLen = size;
+ CopyMemory( prc->Buf, buf, size );
+ Ioctl( IG_WRITE_CONTROL_SPACE, (PVOID)prc, sizeof(*prc) + size );
+ LocalFree( prc );
+ }
+}
+
+// #endif // _WINBASE_
+
+__inline VOID
+ReadIoSpace(
+ ULONG address,
+ PULONG data,
+ PULONG size
+ )
+{
+ IOSPACE is;
+ is.Address = address;
+ is.Length = *size;
+ Ioctl( IG_READ_IO_SPACE, (PVOID)&is, sizeof(is) );
+ memcpy(data, &is.Data, is.Length);
+ *size = is.Length;
+}
+
+__inline VOID
+ReadIoSpace32(
+ ULONG address,
+ PULONG data,
+ PULONG size
+ )
+{
+ IOSPACE32 is;
+ is.Address = address;
+ is.Length = *size;
+ Ioctl( IG_READ_IO_SPACE, (PVOID)&is, sizeof(is) );
+ memcpy(data, &is.Data, is.Length);
+ *size = is.Length;
+}
+
+__inline VOID
+ReadIoSpace64(
+ ULONG64 address,
+ PULONG data,
+ PULONG size
+ )
+{
+ IOSPACE64 is;
+ is.Address = address;
+ is.Length = *size;
+ Ioctl( IG_READ_IO_SPACE, (PVOID)&is, sizeof(is) );
+ memcpy(data, &is.Data, is.Length);
+ *size = is.Length;
+}
+
+__inline VOID
+WriteIoSpace(
+ ULONG address,
+ ULONG data,
+ PULONG size
+ )
+{
+ IOSPACE is;
+ is.Address = (ULONG)address;
+ is.Length = *size;
+ is.Data = data;
+ Ioctl( IG_WRITE_IO_SPACE, (PVOID)&is, sizeof(is) );
+ *size = is.Length;
+}
+
+__inline VOID
+WriteIoSpace32(
+ ULONG address,
+ ULONG data,
+ PULONG size
+ )
+{
+ IOSPACE32 is;
+ is.Address = address;
+ is.Length = *size;
+ is.Data = data;
+ Ioctl( IG_WRITE_IO_SPACE, (PVOID)&is, sizeof(is) );
+ *size = is.Length;
+}
+
+__inline VOID
+WriteIoSpace64(
+ ULONG64 address,
+ ULONG data,
+ PULONG size
+ )
+{
+ IOSPACE64 is;
+ is.Address = address;
+ is.Length = *size;
+ is.Data = data;
+ Ioctl( IG_WRITE_IO_SPACE, (PVOID)&is, sizeof(is) );
+ *size = is.Length;
+}
+
+__inline VOID
+ReadIoSpaceEx(
+ ULONG address,
+ PULONG data,
+ PULONG size,
+ ULONG interfacetype,
+ ULONG busnumber,
+ ULONG addressspace
+ )
+{
+ IOSPACE_EX is;
+ is.Address = (ULONG)address;
+ is.Length = *size;
+ is.Data = 0;
+ is.InterfaceType = interfacetype;
+ is.BusNumber = busnumber;
+ is.AddressSpace = addressspace;
+ Ioctl( IG_READ_IO_SPACE_EX, (PVOID)&is, sizeof(is) );
+ *data = is.Data;
+ *size = is.Length;
+}
+
+__inline VOID
+ReadIoSpaceEx32(
+ ULONG address,
+ PULONG data,
+ PULONG size,
+ ULONG interfacetype,
+ ULONG busnumber,
+ ULONG addressspace
+ )
+{
+ IOSPACE_EX32 is;
+ is.Address = address;
+ is.Length = *size;
+ is.Data = 0;
+ is.InterfaceType = interfacetype;
+ is.BusNumber = busnumber;
+ is.AddressSpace = addressspace;
+ Ioctl( IG_READ_IO_SPACE_EX, (PVOID)&is, sizeof(is) );
+ *data = is.Data;
+ *size = is.Length;
+}
+
+__inline VOID
+ReadIoSpaceEx64(
+ ULONG64 address,
+ PULONG data,
+ PULONG size,
+ ULONG interfacetype,
+ ULONG busnumber,
+ ULONG addressspace
+ )
+{
+ IOSPACE_EX64 is;
+ is.Address = address;
+ is.Length = *size;
+ is.Data = 0;
+ is.InterfaceType = interfacetype;
+ is.BusNumber = busnumber;
+ is.AddressSpace = addressspace;
+ Ioctl( IG_READ_IO_SPACE_EX, (PVOID)&is, sizeof(is) );
+ *data = is.Data;
+ *size = is.Length;
+}
+
+__inline VOID
+WriteIoSpaceEx(
+ ULONG address,
+ ULONG data,
+ PULONG size,
+ ULONG interfacetype,
+ ULONG busnumber,
+ ULONG addressspace
+ )
+{
+ IOSPACE_EX is;
+ is.Address = (ULONG)address;
+ is.Length = *size;
+ is.Data = data;
+ is.InterfaceType = interfacetype;
+ is.BusNumber = busnumber;
+ is.AddressSpace = addressspace;
+ Ioctl( IG_WRITE_IO_SPACE_EX, (PVOID)&is, sizeof(is) );
+ *size = is.Length;
+}
+
+__inline VOID
+WriteIoSpaceEx32(
+ ULONG address,
+ ULONG data,
+ PULONG size,
+ ULONG interfacetype,
+ ULONG busnumber,
+ ULONG addressspace
+ )
+{
+ IOSPACE_EX32 is;
+ is.Address = address;
+ is.Length = *size;
+ is.Data = data;
+ is.InterfaceType = interfacetype;
+ is.BusNumber = busnumber;
+ is.AddressSpace = addressspace;
+ Ioctl( IG_WRITE_IO_SPACE_EX, (PVOID)&is, sizeof(is) );
+ *size = is.Length;
+}
+
+__inline VOID
+WriteIoSpaceEx64(
+ ULONG64 address,
+ ULONG data,
+ PULONG size,
+ ULONG interfacetype,
+ ULONG busnumber,
+ ULONG addressspace
+ )
+{
+ IOSPACE_EX64 is;
+ is.Address = address;
+ is.Length = *size;
+ is.Data = data;
+ is.InterfaceType = interfacetype;
+ is.BusNumber = busnumber;
+ is.AddressSpace = addressspace;
+ Ioctl( IG_WRITE_IO_SPACE_EX, (PVOID)&is, sizeof(is) );
+ *size = is.Length;
+}
+
+__inline VOID
+ReloadSymbols(
+ IN PSTR Arg OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ Calls the debugger to reload symbols.
+
+Arguments:
+
+ Args - Supplies the tail of a !reload command string.
+
+ !reload [flags] [module[=address]]
+ flags: /n do not load from usermode list
+ /u unload symbols, no reload
+ /v verbose
+
+ A value of NULL is equivalent to an empty string
+
+Return Value:
+
+ None
+
+--*/
+{
+ Ioctl(IG_RELOAD_SYMBOLS, (PVOID)Arg, Arg?((ULONG)strlen(Arg)+1):0);
+}
+
+__inline VOID
+GetSetSympath(
+ IN PSTR Arg,
+ OUT PSTR Result OPTIONAL,
+ IN int Length
+ )
+/*++
+
+Routine Description:
+
+ Calls the debugger to set or retrieve symbol search path.
+
+Arguments:
+
+ Arg - Supplies new search path. If Arg is NULL or string is empty,
+ the search path is not changed and the current setting is
+ returned in Result. When the symbol search path is changed,
+ a call to ReloadSymbols is made implicitly.
+
+ Result - OPTIONAL Returns the symbol search path setting.
+
+ Length - Supplies the size of the buffer supplied by Result.
+
+Return Value:
+
+ None
+
+--*/
+{
+ GET_SET_SYMPATH gss;
+ gss.Args = Arg;
+ gss.Result = Result;
+ gss.Length = Length;
+ Ioctl(IG_GET_SET_SYMPATH, (PVOID)&gss, sizeof(gss));
+}
+
+#if defined(KDEXT_64BIT)
+
+__inline
+ULONG
+IsPtr64(
+ void
+ )
+{
+ ULONG flag;
+ ULONG dw;
+
+ if (Ioctl(IG_IS_PTR64, &dw, sizeof(dw))) {
+ flag = ((dw != 0) ? 1 : 0);
+ } else {
+ flag = 0;
+ }
+ return flag;
+}
+
+__inline
+ULONG
+ReadListEntry(
+ ULONG64 Address,
+ PLIST_ENTRY64 List
+ )
+{
+ ULONG cb;
+ if (IsPtr64()) {
+ return (ReadMemory(Address, (PVOID)List, sizeof(*List), &cb) &&
+ cb == sizeof(*List));
+ } else {
+ LIST_ENTRY32 List32;
+ ULONG Status;
+ Status = ReadMemory(Address,
+ (PVOID)&List32,
+ sizeof(List32),
+ &cb);
+ if (Status && cb == sizeof(List32)) {
+ List->Flink = (ULONG64)(LONG64)(LONG)List32.Flink;
+ List->Blink = (ULONG64)(LONG64)(LONG)List32.Blink;
+ return 1;
+ }
+ return 0;
+ }
+}
+
+__inline
+ULONG
+ReadPointer(
+ ULONG64 Address,
+ PULONG64 Pointer
+ )
+{
+ ULONG cb;
+ if (IsPtr64()) {
+ return (ReadMemory(Address, (PVOID)Pointer, sizeof(*Pointer), &cb) &&
+ cb == sizeof(*Pointer));
+ } else {
+ ULONG Pointer32;
+ ULONG Status;
+ Status = ReadMemory(Address,
+ (PVOID)&Pointer32,
+ sizeof(Pointer32),
+ &cb);
+ if (Status && cb == sizeof(Pointer32)) {
+ *Pointer = (ULONG64)(LONG64)(LONG)Pointer32;
+ return 1;
+ }
+ return 0;
+ }
+}
+
+__inline
+ULONG
+WritePointer(
+ ULONG64 Address,
+ ULONG64 Pointer
+ )
+{
+ ULONG cb;
+ if (IsPtr64()) {
+ return (WriteMemory(Address, &Pointer, sizeof(Pointer), &cb) &&
+ cb == sizeof(Pointer));
+ } else {
+ ULONG Pointer32 = (ULONG)Pointer;
+ ULONG Status;
+ Status = WriteMemory(Address,
+ &Pointer32,
+ sizeof(Pointer32),
+ &cb);
+ return (Status && cb == sizeof(Pointer32)) ? 1 : 0;
+ }
+}
+
+/**
+ This does Ioctl call for type info and returns size of the type on success.
+
+ **/
+__inline
+ULONG
+GetTypeSize (
+ IN LPCSTR Type
+ )
+{
+#ifndef FEATURE_PAL
+ SYM_DUMP_PARAM Sym = {
+ sizeof (SYM_DUMP_PARAM), (PUCHAR)Type, DBG_DUMP_NO_PRINT | DBG_DUMP_GET_SIZE_ONLY, 0,
+ NULL, NULL, NULL, 0, NULL
+ };
+
+ return Ioctl( IG_GET_TYPE_SIZE, &Sym, Sym.size );
+#else
+ return (ULONG)~0;
+#endif
+}
+
+/**
+ GetFieldData
+
+ Copies the value of the specified field into pOutValue assuming TypeAddress
+ points to start of the type in debugee.
+
+ If the Field is NULL and the size of Type is <= 8 Whole type value is read into
+ pOutValue. This is to allow to read in primitive types suchas ULONG, PVOID etc.
+
+ If address is zero this considers Type a global variable.
+
+ It raises an exception if OutSize is less than size to be copied.
+
+ Returns 0 on success, errorvalue (defined with SYM_DUMP_PARAM) otherwise.
+
+ **/
+__inline
+ULONG
+GetFieldData (
+ IN ULONG64 TypeAddress,
+ IN LPCSTR Type,
+ IN LPCSTR Field,
+ IN ULONG OutSize,
+ OUT PVOID pOutValue
+ )
+{
+#ifndef FEATURE_PAL
+ FIELD_INFO flds = {(PUCHAR)Field, NULL, 0, DBG_DUMP_FIELD_FULL_NAME | DBG_DUMP_FIELD_COPY_FIELD_DATA | DBG_DUMP_FIELD_RETURN_ADDRESS, 0, pOutValue};
+ SYM_DUMP_PARAM Sym = {
+ sizeof (SYM_DUMP_PARAM), (PUCHAR)Type, DBG_DUMP_NO_PRINT, TypeAddress,
+ NULL, NULL, NULL, 1, &flds
+ };
+ ULONG RetVal;
+
+ if (!Field) {
+ Sym.nFields =0; Sym.Options |= DBG_DUMP_COPY_TYPE_DATA;
+ Sym.Context = pOutValue;
+ }
+
+ ZeroMemory(pOutValue, OutSize);
+ RetVal = Ioctl( IG_DUMP_SYMBOL_INFO, &Sym, Sym.size );
+
+ if (OutSize < ((Field == NULL) ? 8 : flds.size)) {
+ // Fail
+ dprintf("Not enough space to read %s-%s\n", Type, Field);
+ RaiseException((DWORD)EXCEPTION_ACCESS_VIOLATION, 0, 0, NULL);
+ return 0;
+ }
+ return RetVal;
+#else
+ return (ULONG)~0;
+#endif
+}
+
+//
+// Typecast the buffer where value is to be read
+//
+#define GetFieldValue(Addr, Type, Field, OutValue) \
+ GetFieldData(Addr, Type, Field, sizeof(OutValue), (PVOID) &(OutValue))
+
+//
+// Used to read in value of a short (<= 8 bytes) fields
+//
+__inline
+ULONG64
+GetShortField (
+ IN ULONG64 TypeAddress,
+ IN LPCSTR Name,
+ IN USHORT StoreAddress
+ )
+{
+#ifndef FEATURE_PAL
+ static ULONG64 SavedAddress;
+ static PUCHAR SavedName;
+ static ULONG ReadPhysical;
+ FIELD_INFO flds = {(PUCHAR) Name, NULL, 0, DBG_DUMP_FIELD_FULL_NAME, 0, NULL};
+ SYM_DUMP_PARAM Sym = {
+ sizeof (SYM_DUMP_PARAM), SavedName, DBG_DUMP_NO_PRINT | ((StoreAddress & 2) ? DBG_DUMP_READ_PHYSICAL : 0),
+ SavedAddress, NULL, NULL, NULL, 1, &flds
+ };
+
+
+ if (StoreAddress) {
+ Sym.sName = (PUCHAR) Name;
+ Sym.nFields = 0;
+ SavedName = (PUCHAR) Name;
+ Sym.addr = SavedAddress = TypeAddress;
+ ReadPhysical = (StoreAddress & 2);
+ return SavedAddress ? Ioctl( IG_DUMP_SYMBOL_INFO, &Sym, Sym.size ) : MEMORY_READ_ERROR; // zero on success
+ } else {
+ Sym.Options |= ReadPhysical ? DBG_DUMP_READ_PHYSICAL : 0;
+ }
+
+ if (!Ioctl( IG_DUMP_SYMBOL_INFO, &Sym, Sym.size )) {
+ return flds.address;
+ }
+ return 0;
+#else
+ return (ULONG64)~0;
+#endif
+}
+
+//
+// Stores the address and type name for future reads
+//
+#define InitTypeRead(Addr, Type) GetShortField(Addr, #Type, 1)
+#define InitTypeStrRead(Addr, TypeStr) GetShortField(Addr, TypeStr, 1)
+
+//
+// Stores the address and type name for future reads
+//
+#define InitTypeReadPhysical(Addr, Type) GetShortField(Addr, #Type, 3)
+#define InitTypeStrReadPhysical(Addr, TypeStr) GetShortField(Addr, TypeStr, 3)
+
+//
+// Returns the field's value as ULONG64 if size of field is <= sizeof (ULONG64)
+//
+#define ReadField(Field) GetShortField(0, #Field, 0)
+#define ReadFieldStr(FieldStr) GetShortField(0, FieldStr, 0)
+
+//
+// Read in a pointer value
+//
+__inline
+ULONG
+ReadPtr(
+ ULONG64 Addr,
+ PULONG64 pPointer
+ )
+{
+ return !ReadPointer(Addr, pPointer);
+}
+
+/*
+ * ListType
+ *
+ * Routine ListType gives a callback on each element in the list of Type.
+ *
+ * Type : Name of the type to be listed
+ *
+ * NextPointer : Name of field which gives address of next element in list
+ *
+ * Context, CallbackRoutine :
+ * Context and the callback routine. The address field in PFIELD_INFO
+ * parameter of callback contains the address of next Type element in list.
+ *
+ * Address, ListByFieldAddress :
+ * if ListByFieldAddress is 0, Adress is the address of first element of Type List.
+ *
+ * Lists by LIST_ENTRY are also handled implicitly (by Ioctl). If the NextPointer
+ * is a pointer to LIST_ENTRY type, the type address is properly calculated by
+ * subtracting the offsets.
+ *
+ * If ListByFieldAddress is 1, the Address is considered to be the address of field
+ * "NextPointer" of the first Type element and first element address is derived
+ * from it.
+ *
+ */
+
+__inline
+ULONG
+ListType (
+ IN LPCSTR Type,
+ IN ULONG64 Address,
+ IN USHORT ListByFieldAddress,
+ IN LPCSTR NextPointer,
+ IN PVOID Context,
+ IN PSYM_DUMP_FIELD_CALLBACK CallbackRoutine
+ )
+{
+#ifndef FEATURE_PAL
+ FIELD_INFO flds = {(PUCHAR)NextPointer, NULL, 0, 0, 0, NULL};
+ SYM_DUMP_PARAM Sym = {
+ sizeof (SYM_DUMP_PARAM), (PUCHAR) Type, DBG_DUMP_NO_PRINT | DBG_DUMP_LIST, Address,
+ &flds, Context, CallbackRoutine, 0, NULL
+ };
+
+ if (ListByFieldAddress==1) {
+ //
+ // Address is the address of "NextPointer"
+ //
+ Sym.Options |= DBG_DUMP_ADDRESS_OF_FIELD;
+ }
+
+ return Ioctl( IG_DUMP_SYMBOL_INFO, &Sym, Sym.size );
+#else
+ return (ULONG)~0;
+#endif
+}
+
+
+/**
+
+ Routine to get offset of a "Field" of "Type" on a debugee machine. This uses
+ Ioctl call for type info.
+ Returns 0 on success, Ioctl error value otherwise.
+
+ **/
+
+__inline
+ULONG
+GetFieldOffset (
+ IN LPCSTR Type,
+ IN LPCSTR Field,
+ OUT PULONG pOffset
+ )
+{
+#ifndef FEATURE_PAL
+ FIELD_INFO flds = {
+ (PUCHAR)Field,
+ (PUCHAR)"",
+ 0,
+ DBG_DUMP_FIELD_FULL_NAME | DBG_DUMP_FIELD_RETURN_ADDRESS,
+ 0,
+ NULL};
+
+ SYM_DUMP_PARAM Sym = {
+ sizeof (SYM_DUMP_PARAM),
+ (PUCHAR)Type,
+ DBG_DUMP_NO_PRINT,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ 1,
+ &flds
+ };
+
+ ULONG Err;
+
+ Sym.nFields = 1;
+ Err = Ioctl( IG_DUMP_SYMBOL_INFO, &Sym, Sym.size );
+ *pOffset = (ULONG) (flds.address - Sym.addr);
+ return Err;
+#else
+ return (ULONG)~0;
+#endif
+}
+
+
+#endif // defined(KDEXT_64BIT)
+
+__inline VOID
+ GetCurrentProcessHandle(
+ PHANDLE hp
+ )
+{
+ Ioctl(IG_GET_CURRENT_PROCESS_HANDLE, hp, sizeof(HANDLE));
+}
+
+__inline VOID
+ GetTebAddress(
+ PULONGLONG Address
+ )
+{
+ GET_TEB_ADDRESS gpt;
+ gpt.Address = 0;
+ Ioctl(IG_GET_TEB_ADDRESS, (PVOID)&gpt, sizeof(gpt));
+ *Address = gpt.Address;
+}
+
+__inline VOID
+ GetPebAddress(
+ ULONG64 CurrentThread,
+ PULONGLONG Address
+ )
+{
+ GET_PEB_ADDRESS gpt;
+ gpt.CurrentThread = CurrentThread;
+ gpt.Address = 0;
+ Ioctl(IG_GET_PEB_ADDRESS, (PVOID)&gpt, sizeof(gpt));
+ *Address = gpt.Address;
+}
+
+__inline VOID
+ GetCurrentThreadAddr(
+ DWORD Processor,
+ PULONG64 Address
+ )
+{
+ GET_CURRENT_THREAD_ADDRESS ct;
+ ct.Processor = Processor;
+ Ioctl(IG_GET_CURRENT_THREAD, (PVOID)&ct, sizeof(ct));
+ *Address = ct.Address;
+}
+
+__inline VOID
+ GetCurrentProcessAddr(
+ DWORD Processor,
+ ULONG64 CurrentThread,
+ PULONG64 Address
+ )
+{
+ GET_CURRENT_PROCESS_ADDRESS cp;
+ cp.Processor = Processor;
+ cp.CurrentThread = CurrentThread;
+ Ioctl(IG_GET_CURRENT_PROCESS, (PVOID)&cp, sizeof(cp));
+ *Address = cp.Address;
+}
+
+__inline VOID
+SearchMemory(
+ ULONG64 SearchAddress,
+ ULONG64 SearchLength,
+ ULONG PatternLength,
+ PVOID Pattern,
+ PULONG64 FoundAddress
+ )
+{
+ SEARCHMEMORY sm;
+ sm.SearchAddress = SearchAddress;
+ sm.SearchLength = SearchLength;
+ sm.FoundAddress = 0;
+ sm.PatternLength = PatternLength;
+ sm.Pattern = Pattern;
+ Ioctl(IG_SEARCH_MEMORY, (PVOID)&sm, sizeof(sm));
+ *FoundAddress = sm.FoundAddress;
+}
+
+__inline ULONG
+GetInputLine(
+ PCSTR Prompt,
+ PSTR Buffer,
+ ULONG BufferSize
+ )
+{
+ GET_INPUT_LINE InLine;
+ InLine.Prompt = Prompt;
+ InLine.Buffer = Buffer;
+ InLine.BufferSize = BufferSize;
+ if (Ioctl(IG_GET_INPUT_LINE, (PVOID)&InLine, sizeof(InLine)))
+ {
+ return InLine.InputSize;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+__inline BOOL
+GetExpressionEx(
+ PCSTR Expression,
+ ULONG64* Value,
+ PCSTR* Remainder
+ )
+{
+ GET_EXPRESSION_EX Expr;
+ Expr.Expression = Expression;
+ if (Ioctl(IG_GET_EXPRESSION_EX, (PVOID)&Expr, sizeof(Expr)))
+ {
+ *Value = Expr.Value;
+
+ if (Remainder != NULL)
+ {
+ *Remainder = Expr.Remainder;
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+__inline BOOL
+TranslateVirtualToPhysical(
+ ULONG64 Virtual,
+ ULONG64* Physical
+ )
+{
+ TRANSLATE_VIRTUAL_TO_PHYSICAL VToP;
+ VToP.Virtual = Virtual;
+ if (Ioctl(IG_TRANSLATE_VIRTUAL_TO_PHYSICAL, (PVOID)&VToP, sizeof(VToP)))
+ {
+ *Physical = VToP.Physical;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+__inline BOOL
+GetDebuggerCacheSize(
+ OUT PULONG64 CacheSize
+ )
+{
+ return Ioctl(IG_GET_CACHE_SIZE, (PVOID) CacheSize, sizeof(ULONG64));
+}
+
+__inline BOOL
+ExtMatchPatternA(
+ IN PCSTR Str,
+ IN PCSTR Pattern,
+ IN BOOL CaseSensitive
+ )
+{
+ EXT_MATCH_PATTERN_A Args;
+
+ Args.Str = Str;
+ Args.Pattern = Pattern;
+ Args.CaseSensitive = CaseSensitive;
+ return Ioctl(IG_MATCH_PATTERN_A, (PVOID)&Args, sizeof(Args));
+}
+
+#endif // FEATURE_PAL
+
+#endif
+
+#ifndef FEATURE_PAL
+#pragma warning(default:4115 4201 4204 4214 4221)
+#endif
+#if _MSC_VER >= 1200
+#pragma warning(pop)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _WDBGEXTS_
diff --git a/src/ToolBox/SOS/Strike/metadata.cpp b/src/ToolBox/SOS/Strike/metadata.cpp
new file mode 100644
index 0000000000..073b979baa
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/metadata.cpp
@@ -0,0 +1,1041 @@
+// 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 "strike.h"
+#include "util.h"
+#include "genericstackprobe.h"
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to find the name of a TypeDef using *
+* metadata API. *
+* *
+\**********************************************************************/
+// Caller should guard against exception
+// !!! mdName should have at least mdNameLen WCHAR
+static HRESULT NameForTypeDef_s(mdTypeDef tkTypeDef, IMetaDataImport *pImport,
+ __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName)
+{
+ DWORD flags;
+ ULONG nameLen;
+
+ HRESULT hr = pImport->GetTypeDefProps(tkTypeDef, mdName,
+ mdNameLen, &nameLen,
+ &flags, NULL);
+ if (hr != S_OK) {
+ return hr;
+ }
+
+ if (!IsTdNested(flags)) {
+ return hr;
+ }
+ mdTypeDef tkEnclosingClass;
+ hr = pImport->GetNestedClassProps(tkTypeDef, &tkEnclosingClass);
+ if (hr != S_OK) {
+ return hr;
+ }
+ WCHAR *name = (WCHAR*)_alloca((nameLen+1)*sizeof(WCHAR));
+ wcscpy_s (name, nameLen+1, mdName);
+ hr = NameForTypeDef_s(tkEnclosingClass,pImport,mdName, capacity_mdName);
+ if (hr != S_OK) {
+ return hr;
+ }
+ size_t Len = _wcslen (mdName);
+ if (Len < mdNameLen-2) {
+ mdName[Len++] = L'+';
+ mdName[Len] = L'\0';
+ }
+ Len = mdNameLen-1 - Len;
+ if (Len > nameLen) {
+ Len = nameLen;
+ }
+ wcsncat_s (mdName,capacity_mdName,name,Len);
+ return hr;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to find the name of a TypeDef using *
+* metadata API. *
+* *
+\**********************************************************************/
+// Caller should guard against exception
+// !!! mdName should have at least mdNameLen WCHAR
+/*
+static HRESULT NameForTypeDefNew(mdTypeDef tkTypeDef, IMDInternalImport *pImport,
+ WCHAR *mdName)
+{
+ DWORD flags;
+ ULONG nameLen;
+ char *name = (char *)_alloca((mdNameLen+1)*sizeof(char));
+ char *namesp = (char *)_alloca((mdNameLen+1)*sizeof(char));
+
+ HRESULT hr = pImport->GetNameOfTypeDef(tkTypeDef, name, namesp);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ strcpy (namesp, ".");
+ strcpy (namesp, name);
+ MultiByteToWideChar (CP_ACP,0,namesp,-1,mdName,mdNameLen);
+ return hr;
+}
+*/
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Find the Module MD Importer given the name of the Module. *
+* *
+\**********************************************************************/
+IMetaDataImport* MDImportForModule(DacpModuleData* pModule)
+{
+ IMetaDataImport *pRet = NULL;
+ ToRelease<IXCLRDataModule> module;
+ HRESULT hr = g_sos->GetModule(pModule->Address, &module);
+
+ if (SUCCEEDED(hr))
+ hr = module->QueryInterface(IID_IMetaDataImport, (LPVOID *) &pRet);
+
+ if (SUCCEEDED(hr))
+ return pRet;
+
+ return NULL;
+}
+
+IMetaDataImport* MDImportForModule(DWORD_PTR pModule)
+{
+ DacpModuleData moduleData;
+ if(moduleData.Request(g_sos, TO_CDADDR(pModule))==S_OK)
+ return MDImportForModule(&moduleData);
+ else
+ return NULL;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Find the name for a metadata token given an importer. *
+* *
+\**********************************************************************/
+HRESULT NameForToken_s(mdTypeDef mb, IMetaDataImport *pImport, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName,
+ bool bClassName)
+{
+ mdName[0] = L'\0';
+ if ((mb & 0xff000000) != mdtTypeDef
+ && (mb & 0xff000000) != mdtFieldDef
+ && (mb & 0xff000000) != mdtMethodDef)
+ {
+ //ExtOut("unsupported\n");
+ return E_FAIL;
+ }
+
+ HRESULT hr = E_FAIL;
+
+ PAL_CPP_TRY
+ {
+ static WCHAR name[MAX_CLASSNAME_LENGTH];
+ if ((mb & 0xff000000) == mdtTypeDef)
+ {
+ hr = NameForTypeDef_s (mb, pImport, mdName, capacity_mdName);
+ }
+ else if ((mb & 0xff000000) == mdtFieldDef)
+ {
+ mdTypeDef mdClass;
+ ULONG size;
+ hr = pImport->GetMemberProps(mb, &mdClass,
+ name, sizeof(name)/sizeof(WCHAR)-1, &size,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL);
+ if (SUCCEEDED (hr))
+ {
+ if (mdClass != mdTypeDefNil && bClassName)
+ {
+ hr = NameForTypeDef_s (mdClass, pImport, mdName, capacity_mdName);
+ wcscat_s (mdName, capacity_mdName, W("."));
+ }
+ name[size] = L'\0';
+ wcscat_s (mdName, capacity_mdName, name);
+ }
+ }
+ else if ((mb & 0xff000000) == mdtMethodDef)
+ {
+ mdTypeDef mdClass;
+ ULONG size;
+ hr = pImport->GetMethodProps(mb, &mdClass,
+ name, sizeof(name)/sizeof(WCHAR)-1, &size,
+ NULL, NULL, NULL, NULL, NULL);
+ if (SUCCEEDED (hr))
+ {
+ if (mdClass != mdTypeDefNil && bClassName)
+ {
+ hr = NameForTypeDef_s (mdClass, pImport, mdName, capacity_mdName);
+ wcscat_s (mdName, capacity_mdName, W("."));
+ }
+ name[size] = L'\0';
+ wcscat_s (mdName, capacity_mdName, name);
+ }
+ }
+ else
+ {
+ ExtOut ("Unsupported token type\n");
+ hr = E_FAIL;
+ }
+ }
+ PAL_CPP_CATCH_ALL
+ {
+ hr = E_FAIL;
+ }
+ PAL_CPP_ENDTRY
+ return hr;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Find the name for a metadata token given an importer. *
+* *
+\**********************************************************************/
+/*
+HRESULT NameForTokenNew(mdTypeDef mb, IMDInternalImport *pImport, WCHAR *mdName, size_t capacity_mdName,
+ bool bClassName)
+{
+
+ // TODO: Change calls to use the secure versions (string as well as to functions defined here)
+ // Simply uncommenting this function will not result in a clean compile
+ // --chirayuk @ 11/23/2004
+
+ mdName[0] = L'\0';
+ if ((mb & 0xff000000) != mdtTypeDef
+ && (mb & 0xff000000) != mdtFieldDef
+ && (mb & 0xff000000) != mdtMethodDef)
+ {
+ //ExtOut("unsupported\n");
+ return E_FAIL;
+ }
+
+ HRESULT hr;
+
+ __try
+ {
+ static WCHAR name[MAX_CLASSNAME_LENGTH];
+ if (TypeFromToken(mb) == mdtTypeDef)
+ {
+ hr = NameForTypeDefNew (mb, pImport, mdName);
+ }
+ else if (TypeFromToken(mb) == mdtFieldDef)
+ {
+ mdTypeDef mdClass;
+ ULONG size;
+ MultiByteToWideChar (CP_ACP,0,pImport->GetNameOfFieldDef(mb),-1,name,MAX_CLASSNAME_LENGTH);
+
+ hr = pImport->GetParentToken (mb, &mdClass);
+ if (SUCCEEDED (hr))
+ {
+ if (mdClass != mdTypeDefNil && bClassName)
+ {
+ hr = NameForTypeDefNew (mdClass, pImport, mdName);
+ _wcscat (mdName, W("."));
+ }
+ name[size] = L'\0';
+ _wcscat (mdName, name);
+ }
+ }
+ else if (TypeFromToken(mb) == mdtMethodDef)
+ {
+ mdTypeDef mdClass;
+ ULONG size;
+
+ MultiByteToWideChar (CP_ACP,0,pImport->GetNameOfMethodDef(mb),-1,name,MAX_CLASSNAME_LENGTH);
+ hr = pImport->GetParentToken (mb, &mdClass);
+ if (SUCCEEDED (hr))
+ {
+ if (mdClass != mdTypeDefNil && bClassName)
+ {
+ hr = NameForTypeDefNew (mdClass, pImport, mdName);
+ _wcscat (mdName, W("."));
+ }
+ name[size] = L'\0';
+ _wcscat (mdName, name);
+ }
+ }
+ else
+ {
+ ExtOut ("Unsupported token type\n");
+ hr = E_FAIL;
+ }
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ //ExtOut("Metadata operation failure\n");
+ hr = E_FAIL;
+ }
+ return hr;
+}
+*/
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to find the name of a metadata token *
+* using metadata API. *
+* *
+\**********************************************************************/
+void NameForToken_s(DWORD_PTR ModuleAddr, mdTypeDef mb, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName,
+ bool bClassName)
+{
+ DacpModuleData ModuleData;
+ mdName[0] = L'\0';
+ if(ModuleData.Request(g_sos, TO_CDADDR(ModuleAddr))==S_OK)
+ NameForToken_s(&ModuleData,mb,mdName,capacity_mdName,bClassName);
+}
+
+BOOL IsValidToken(DWORD_PTR ModuleAddr, mdTypeDef mb)
+{
+ DacpModuleData ModuleData;
+ if(ModuleData.Request(g_sos, TO_CDADDR(ModuleAddr))==S_OK)
+ {
+ ToRelease<IMetaDataImport> pImport = MDImportForModule(&ModuleData);
+ if (pImport)
+ {
+ if (pImport->IsValidToken (mb))
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+void NameForToken_s(DacpModuleData *pModule, mdTypeDef mb, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName,
+ bool bClassName)
+{
+ mdName[0] = L'\0';
+ HRESULT hr = 0;
+ ToRelease<IMetaDataImport> pImport = MDImportForModule(pModule);
+ if (pImport)
+ {
+ hr = NameForToken_s (mb, pImport, mdName, capacity_mdName, bClassName);
+ }
+
+ if (!pImport || !SUCCEEDED (hr))
+ {
+ const SIZE_T capacity_moduleName = mdNameLen+19;
+ LPWSTR moduleName = (LPWSTR)alloca(capacity_moduleName * sizeof(WCHAR)); // for the "Dynamic Module In " below
+ FileNameForModule(pModule,moduleName);
+ if (moduleName[0] == L'\0') {
+ DacpAssemblyData assembly;
+ assembly.Request(g_sos,pModule->Assembly);
+ if (assembly.isDynamic) {
+ wcscpy_s(moduleName, capacity_moduleName, W("Dynamic "));
+ }
+ wcscat_s (moduleName, capacity_moduleName, W("Module in "));
+ if(g_sos->GetAssemblyName(pModule->Assembly, mdNameLen, g_mdName, NULL)==S_OK)
+ {
+ wcscat_s(moduleName, capacity_moduleName, g_mdName);
+ }
+ }
+ swprintf_s (mdName, capacity_mdName,
+ W(" mdToken: %08x (%ws)"),
+ mb,
+ moduleName[0] ? moduleName : W("Unknown Module") );
+ }
+}
+
+#define STRING_BUFFER_LEN 1024
+
+class MDInfo
+{
+public:
+ MDInfo (DWORD_PTR ModuleAddr)
+ {
+ m_pImport = MDImportForModule(ModuleAddr);
+ if (!m_pImport)
+ ExtOut("Unable to get IMetaDataImport for module %p\n", ModuleAddr);
+ m_pSigBuf = NULL;
+ }
+
+ MDInfo (IMetaDataImport * pImport)
+ {
+ m_pImport = pImport;
+ m_pImport->AddRef();
+ m_pSigBuf = NULL;
+ }
+
+ void GetMethodName(mdTypeDef token, CQuickBytes *fullName);
+ GetSignatureStringResults GetMethodSignature(PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, CQuickBytes *fullName);
+ GetSignatureStringResults GetSignature(PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, CQuickBytes *fullName);
+
+ LPCWSTR TypeDefName(mdTypeDef inTypeDef);
+ LPCWSTR TypeRefName(mdTypeRef tr);
+ LPCWSTR TypeDeforRefName(mdToken inToken);
+private:
+ // helper to init signature buffer
+ void InitSigBuffer()
+ {
+ ((LPWSTR)m_pSigBuf->Ptr())[0] = L'\0';
+ }
+
+ HRESULT AddToSigBuffer(LPCWSTR string);
+
+ HRESULT GetFullNameForMD(PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, LONG *plSigBlobRemaining OPTIONAL);
+ HRESULT GetOneElementType(PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, ULONG *pcb);
+
+ ToRelease<IMetaDataImport> m_pImport;
+ // Signature buffer.
+ CQuickBytes *m_pSigBuf;
+
+ // temporary buffer for TypeDef or TypeRef name. Consume immediately
+ // because other functions may overwrite it.
+ static WCHAR m_szTempBuf[MAX_CLASSNAME_LENGTH];
+
+ static WCHAR m_szName[MAX_CLASSNAME_LENGTH];
+};
+
+WCHAR MDInfo::m_szTempBuf[MAX_CLASSNAME_LENGTH];
+WCHAR MDInfo::m_szName[MAX_CLASSNAME_LENGTH];
+
+GetSignatureStringResults GetMethodSignatureString (PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, DWORD_PTR dwModuleAddr, CQuickBytes *sigString)
+{
+ MDInfo mdInfo(dwModuleAddr);
+
+ return mdInfo.GetMethodSignature(pbSigBlob, ulSigBlob, sigString);
+}
+
+
+GetSignatureStringResults GetSignatureString (PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, DWORD_PTR dwModuleAddr, CQuickBytes *sigString)
+{
+ MDInfo mdInfo(dwModuleAddr);
+
+ return mdInfo.GetSignature(pbSigBlob, ulSigBlob, sigString);
+}
+
+void GetMethodName(mdMethodDef methodDef, IMetaDataImport * pImport, CQuickBytes *fullName)
+{
+ MDInfo mdInfo(pImport);
+
+ mdInfo.GetMethodName(methodDef, fullName);
+}
+
+
+// Tables for mapping element type to text
+const WCHAR *g_wszMapElementType[] =
+{
+ W("End"), // 0x0
+ W("Void"), // 0x1
+ W("Boolean"),
+ W("Char"),
+ W("I1"),
+ W("UI1"),
+ W("I2"), // 0x6
+ W("UI2"),
+ W("I4"),
+ W("UI4"),
+ W("I8"),
+ W("UI8"),
+ W("R4"),
+ W("R8"),
+ W("String"),
+ W("Ptr"), // 0xf
+ W("ByRef"), // 0x10
+ W("ValueClass"),
+ W("Class"),
+ W("CopyCtor"),
+ W("MDArray"), // 0x14
+ W("GENArray"),
+ W("TypedByRef"),
+ W("VALUEARRAY"),
+ W("I"),
+ W("U"),
+ W("R"), // 0x1a
+ W("FNPTR"),
+ W("Object"),
+ W("SZArray"),
+ W("GENERICArray"),
+ W("CMOD_REQD"),
+ W("CMOD_OPT"),
+ W("INTERNAL"),
+};
+
+const WCHAR *g_wszCalling[] =
+{
+ W("[DEFAULT]"),
+ W("[C]"),
+ W("[STDCALL]"),
+ W("[THISCALL]"),
+ W("[FASTCALL]"),
+ W("[VARARG]"),
+ W("[FIELD]"),
+ W("[LOCALSIG]"),
+ W("[PROPERTY]"),
+ W("[UNMANAGED]"),
+};
+
+void MDInfo::GetMethodName(mdTypeDef token, CQuickBytes *fullName)
+{
+ if (m_pImport == NULL) {
+ return;
+ }
+
+ HRESULT hr;
+ mdTypeDef memTypeDef;
+ ULONG nameLen;
+ DWORD flags;
+ PCCOR_SIGNATURE pbSigBlob;
+ ULONG ulSigBlob;
+ ULONG ulCodeRVA;
+ ULONG ulImplFlags;
+
+ m_pSigBuf = fullName;
+ InitSigBuffer();
+
+ WCHAR szFunctionName[1024];
+
+ hr = m_pImport->GetMethodProps(token, &memTypeDef,
+ szFunctionName, _countof(szFunctionName), &nameLen,
+ &flags, &pbSigBlob, &ulSigBlob, &ulCodeRVA, &ulImplFlags);
+ if (FAILED (hr))
+ {
+ return;
+ }
+
+ szFunctionName[nameLen] = L'\0';
+ m_szName[0] = L'\0';
+ if (memTypeDef != mdTypeDefNil)
+ {
+ hr = NameForTypeDef_s (memTypeDef, m_pImport, m_szName, _countof(m_szName));
+ if (SUCCEEDED (hr)) {
+ wcscat_s (m_szName, _countof(m_szName), W("."));
+ }
+ }
+ wcscat_s (m_szName, _countof(m_szName), szFunctionName);
+
+ LONG lSigBlobRemaining;
+ hr = GetFullNameForMD(pbSigBlob, ulSigBlob, &lSigBlobRemaining);
+
+ // We should have consumed all signature blob. If not, dump the sig in hex.
+ // Also dump in hex if so requested.
+ if (lSigBlobRemaining != 0)
+ {
+ // Did we not consume enough, or try to consume too much?
+ if (lSigBlobRemaining < 0)
+ ExtOut("ERROR IN SIGNATURE: Signature should be larger.\n");
+ else
+ ExtOut("ERROR IN SIGNATURE: Not all of signature blob was consumed. %d byte(s) remain\n", lSigBlobRemaining);
+ }
+
+ if (FAILED(hr))
+ ExtOut("ERROR!! Bad signature blob value!");
+}
+
+
+GetSignatureStringResults MDInfo::GetMethodSignature(PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, CQuickBytes *fullName)
+{
+ if (!m_pImport)
+ return GSS_ERROR;
+
+ m_pSigBuf = fullName;
+ InitSigBuffer();
+
+ m_szName[0] = '\0';
+
+ LONG lSigBlobRemaining;
+ if (FAILED(GetFullNameForMD(pbSigBlob, ulSigBlob, &lSigBlobRemaining)))
+ return GSS_ERROR;
+
+ if (lSigBlobRemaining < 0)
+ return GSS_INSUFFICIENT_DATA;
+
+ return GSS_SUCCESS;
+}
+
+
+GetSignatureStringResults MDInfo::GetSignature(PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, CQuickBytes *fullName)
+{
+ if (!m_pImport)
+ return GSS_ERROR;
+
+ m_pSigBuf = fullName;
+ InitSigBuffer();
+
+ m_szName[0] = '\0';
+
+ ULONG cb;
+ if (FAILED(GetOneElementType(pbSigBlob, ulSigBlob, &cb)))
+ {
+ if (cb > ulSigBlob)
+ return GSS_INSUFFICIENT_DATA;
+ else
+ return GSS_ERROR;
+ }
+
+ return GSS_SUCCESS;
+}
+
+
+inline bool isCallConv(unsigned sigByte, CorCallingConvention conv)
+{
+ return ((sigByte & IMAGE_CEE_CS_CALLCONV_MASK) == (unsigned) conv);
+}
+
+#ifndef IfFailGoto
+#define IfFailGoto(EXPR, LABEL) \
+do { hr = (EXPR); if(FAILED(hr)) { goto LABEL; } } while (0)
+#endif
+
+#ifndef IfFailGo
+#define IfFailGo(EXPR) IfFailGoto(EXPR, ErrExit)
+#endif
+
+#ifndef IfFailRet
+#define IfFailRet(EXPR) do { hr = (EXPR); if(FAILED(hr)) { return (hr); } } while (0)
+#endif
+
+#ifndef _ASSERTE
+#define _ASSERTE(expr)
+#endif
+
+HRESULT MDInfo::GetFullNameForMD(PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, LONG *plSigBlobRemaining)
+{
+ ULONG cbCur = 0;
+ ULONG cb;
+ ULONG ulData = NULL;
+ ULONG ulArgs;
+ HRESULT hr = NOERROR;
+
+ cb = CorSigUncompressData(pbSigBlob, &ulData);
+
+ // 0 is a valid calling convention byte (IMAGE_CEE_CS_CALLCONV_DEFAULT w/ no flags)
+ //if (ulData == NULL)
+ // goto ErrExit;
+
+ AddToSigBuffer (g_wszCalling[ulData & IMAGE_CEE_CS_CALLCONV_MASK]);
+ if (cb>ulSigBlob)
+ goto ErrExit;
+ cbCur += cb;
+ ulSigBlob -= cb;
+
+ if (ulData & IMAGE_CEE_CS_CALLCONV_HASTHIS)
+ AddToSigBuffer ( W(" [hasThis]"));
+ if (ulData & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS)
+ AddToSigBuffer ( W(" [explicit]"));
+
+ AddToSigBuffer (W(" "));
+ if ( isCallConv(ulData,IMAGE_CEE_CS_CALLCONV_FIELD) )
+ {
+ // display field type
+ if (FAILED(hr = GetOneElementType(&pbSigBlob[cbCur], ulSigBlob, &cb)))
+ goto ErrExit;
+ AddToSigBuffer ( W(" "));
+ AddToSigBuffer ( m_szName);
+ if (cb>ulSigBlob)
+ goto ErrExit;
+ cbCur += cb;
+ ulSigBlob -= cb;
+ }
+ else
+ {
+ cb = CorSigUncompressData(&pbSigBlob[cbCur], &ulArgs);
+ if (cb>ulSigBlob)
+ goto ErrExit;
+ cbCur += cb;
+ ulSigBlob -= cb;
+
+ if (ulData != IMAGE_CEE_CS_CALLCONV_LOCAL_SIG)
+ {
+ // display return type when it is not a local varsig
+ if (FAILED(hr = GetOneElementType(&pbSigBlob[cbCur], ulSigBlob, &cb)))
+ goto ErrExit;
+ AddToSigBuffer (W(" "));
+ AddToSigBuffer (m_szName);
+ AddToSigBuffer ( W("("));
+ if (cb>ulSigBlob)
+ goto ErrExit;
+ cbCur += cb;
+ ulSigBlob -= cb;
+ }
+
+ ULONG i = 0;
+ while (i < ulArgs && ulSigBlob > 0)
+ {
+ ULONG ulDataUncompress;
+
+ // Handle the sentinal for varargs because it isn't counted in the args.
+ CorSigUncompressData(&pbSigBlob[cbCur], &ulDataUncompress);
+ ++i;
+
+ if (FAILED(hr = GetOneElementType(&pbSigBlob[cbCur], ulSigBlob, &cb)))
+ goto ErrExit;
+ if (i != ulArgs) {
+ AddToSigBuffer ( W(","));
+ }
+ if (cb>ulSigBlob)
+ goto ErrExit;
+
+ cbCur += cb;
+ ulSigBlob -= cb;
+ }
+ AddToSigBuffer ( W(")"));
+ }
+
+ // Nothing consumed but not yet counted.
+ cb = 0;
+
+ErrExit:
+
+ if (plSigBlobRemaining)
+ *plSigBlobRemaining = (ulSigBlob - cb);
+
+ return hr;
+}
+
+LPCWSTR MDInfo::TypeDefName(mdTypeDef inTypeDef)
+{
+ if (m_pImport == NULL) {
+ return W("");
+ }
+
+ HRESULT hr;
+
+ hr = m_pImport->GetTypeDefProps(
+ // [IN] The import scope.
+ inTypeDef, // [IN] TypeDef token for inquiry.
+ m_szTempBuf, // [OUT] Put name here.
+ MAX_CLASSNAME_LENGTH , // [IN] size of name buffer in wide chars.
+ NULL, // [OUT] put size of name (wide chars) here.
+ NULL, // [OUT] Put flags here.
+ NULL); // [OUT] Put base class TypeDef/TypeRef here.
+
+ if (FAILED(hr)) return (W("NoName"));
+ return (m_szTempBuf);
+} // LPCWSTR MDInfo::TypeDefName()
+LPCWSTR MDInfo::TypeRefName(mdTypeRef tr)
+{
+ if (m_pImport == NULL) {
+ return W("");
+ }
+
+ HRESULT hr;
+
+ hr = m_pImport->GetTypeRefProps(
+ tr, // The class ref token.
+ NULL, // Resolution scope.
+ m_szTempBuf, // Put the name here.
+ MAX_CLASSNAME_LENGTH, // Size of the name buffer, wide chars.
+ NULL); // Put actual size of name here.
+ if (FAILED(hr)) return (W("NoName"));
+
+ return (m_szTempBuf);
+} // LPCWSTR MDInfo::TypeRefName()
+
+LPCWSTR MDInfo::TypeDeforRefName(mdToken inToken)
+{
+ if (RidFromToken(inToken))
+ {
+ if (TypeFromToken(inToken) == mdtTypeDef)
+ return (TypeDefName((mdTypeDef) inToken));
+ else if (TypeFromToken(inToken) == mdtTypeRef)
+ return (TypeRefName((mdTypeRef) inToken));
+ else
+ return (W("[InvalidReference]"));
+ }
+ else
+ return (W(""));
+} // LPCWSTR MDInfo::TypeDeforRefName()
+
+
+HRESULT MDInfo::AddToSigBuffer(LPCWSTR string)
+{
+ HRESULT hr;
+ IfFailRet(m_pSigBuf->ReSize((_wcslen((LPWSTR)m_pSigBuf->Ptr()) + _wcslen(string) + 1) * sizeof(WCHAR)));
+ wcscat_s((LPWSTR)m_pSigBuf->Ptr(), m_pSigBuf->Size()/sizeof(WCHAR),string);
+ return NOERROR;
+}
+
+HRESULT MDInfo::GetOneElementType(PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, ULONG *pcb)
+{
+ HRESULT hr = S_OK; // A result.
+ ULONG cbCur = 0;
+ ULONG cb;
+ ULONG ulData;
+ ULONG ulTemp;
+ int iTemp = 0;
+ mdToken tk;
+ const size_t capacity_buffer = 9;
+
+ cb = CorSigUncompressData(pbSigBlob, &ulData);
+
+ if (cb == ULONG(-1)) {
+ hr = E_FAIL;
+ goto ErrExit;
+ }
+
+ cbCur += cb;
+
+ // Handle the modifiers.
+ if (ulData & ELEMENT_TYPE_MODIFIER)
+ {
+ if (ulData == ELEMENT_TYPE_SENTINEL)
+ IfFailGo(AddToSigBuffer(W("<ELEMENT_TYPE_SENTINEL> ")));
+ else if (ulData == ELEMENT_TYPE_PINNED)
+ IfFailGo(AddToSigBuffer(W("PINNED ")));
+ else
+ {
+ hr = E_FAIL;
+ goto ErrExit;
+ }
+ if (FAILED(GetOneElementType(&pbSigBlob[cbCur], ulSigBlob-cbCur, &cb)))
+ goto ErrExit;
+ cbCur += cb;
+ goto ErrExit;
+ }
+
+ // Handle the underlying element types.
+ if (ulData >= ELEMENT_TYPE_MAX)
+ {
+ hr = E_FAIL;
+ goto ErrExit;
+ }
+ while (ulData == ELEMENT_TYPE_PTR || ulData == ELEMENT_TYPE_BYREF)
+ {
+ IfFailGo(AddToSigBuffer(g_wszMapElementType[ulData]));
+ IfFailGo(AddToSigBuffer(W(" ")));
+ cb = CorSigUncompressData(&pbSigBlob[cbCur], &ulData);
+ cbCur += cb;
+ }
+
+ // Generics
+ if (ulData == ELEMENT_TYPE_VAR)
+ {
+ IfFailGo(AddToSigBuffer(W("__Canon")));
+
+ // The next byte represents which generic parameter is referred to. We
+ // do not currently use this information, so just bypass this byte.
+ cbCur++;
+
+ goto ErrExit;
+ }
+
+ // A generic instance, e.g. IEnumerable<String>
+ if (ulData == ELEMENT_TYPE_GENERICINST)
+ {
+ // Print out the base type.
+ IfFailGo(GetOneElementType(&pbSigBlob[cbCur], ulSigBlob-cbCur, &cb));
+ cbCur += cb;
+
+ // Get the number of generic arguments.
+ ULONG numParams = 0;
+ IfFailGo(CorSigUncompressData(&pbSigBlob[cbCur], 1, &numParams, &cb));
+ cbCur += cb;
+
+ // Print out the list of arguments
+ IfFailGo(AddToSigBuffer(W("<")));
+ for (ULONG i = 0; i < numParams; i++)
+ {
+ if (i > 0)
+ IfFailGo(AddToSigBuffer(W(",")));
+
+ IfFailGo(GetOneElementType(&pbSigBlob[cbCur], ulSigBlob-cbCur, &cb));
+ cbCur += cb;
+ }
+ IfFailGo(AddToSigBuffer(W(">")));
+ goto ErrExit;
+ }
+
+ // Past this point we must have something which directly maps to a value in g_wszMapElementType.
+ IfFailGo(AddToSigBuffer(g_wszMapElementType[ulData]));
+ if (CorIsPrimitiveType((CorElementType)ulData) ||
+ ulData == ELEMENT_TYPE_TYPEDBYREF ||
+ ulData == ELEMENT_TYPE_OBJECT ||
+ ulData == ELEMENT_TYPE_I ||
+ ulData == ELEMENT_TYPE_U)
+ {
+ // If this is a primitive type, we are done
+ goto ErrExit;
+ }
+
+ AddToSigBuffer(W(" "));
+ if (ulData == ELEMENT_TYPE_VALUETYPE ||
+ ulData == ELEMENT_TYPE_CLASS ||
+ ulData == ELEMENT_TYPE_CMOD_REQD ||
+ ulData == ELEMENT_TYPE_CMOD_OPT)
+ {
+ cb = CorSigUncompressToken(&pbSigBlob[cbCur], &tk);
+ cbCur += cb;
+
+ // get the name of type ref. Don't care if truncated
+ if (TypeFromToken(tk) == mdtTypeDef || TypeFromToken(tk) == mdtTypeRef)
+ {
+ IfFailGo(AddToSigBuffer(TypeDeforRefName(tk)));
+ }
+ else
+ {
+ _ASSERTE(TypeFromToken(tk) == mdtTypeSpec);
+ WCHAR buffer[capacity_buffer];
+ _itow_s (tk, buffer, capacity_buffer, 16);
+ IfFailGo(AddToSigBuffer(buffer));
+ }
+ if (ulData == ELEMENT_TYPE_CMOD_REQD ||
+ ulData == ELEMENT_TYPE_CMOD_OPT)
+ {
+ IfFailGo(AddToSigBuffer(W(" ")));
+ if (FAILED(GetOneElementType(&pbSigBlob[cbCur], ulSigBlob-cbCur, &cb)))
+ goto ErrExit;
+ cbCur += cb;
+ }
+
+ goto ErrExit;
+ }
+ if (ulData == ELEMENT_TYPE_SZARRAY)
+ {
+ // display the base type of SZARRAY or GENERICARRAY
+ if (FAILED(GetOneElementType(&pbSigBlob[cbCur], ulSigBlob-cbCur, &cb)))
+ goto ErrExit;
+ cbCur += cb;
+ goto ErrExit;
+ }
+ if (ulData == ELEMENT_TYPE_FNPTR)
+ {
+ cb = CorSigUncompressData(&pbSigBlob[cbCur], &ulData);
+ cbCur += cb;
+ if (ulData & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS)
+ IfFailGo(AddToSigBuffer(W("[explicit] ")));
+ if (ulData & IMAGE_CEE_CS_CALLCONV_HASTHIS)
+ IfFailGo(AddToSigBuffer(W("[hasThis] ")));
+
+ IfFailGo(AddToSigBuffer(g_wszCalling[ulData & IMAGE_CEE_CS_CALLCONV_MASK]));
+
+ // Get number of args
+ ULONG numArgs;
+ cb = CorSigUncompressData(&pbSigBlob[cbCur], &numArgs);
+ cbCur += cb;
+
+ // do return type
+ if (FAILED(GetOneElementType(&pbSigBlob[cbCur], ulSigBlob-cbCur, &cb)))
+ goto ErrExit;
+ cbCur += cb;
+
+ IfFailGo(AddToSigBuffer(W("(")));
+ while (numArgs > 0)
+ {
+ if (cbCur > ulSigBlob)
+ goto ErrExit;
+ if (FAILED(GetOneElementType(&pbSigBlob[cbCur], ulSigBlob-cbCur, &cb)))
+ goto ErrExit;
+ cbCur += cb;
+ --numArgs;
+ if (numArgs > 0)
+ IfFailGo(AddToSigBuffer(W(",")));
+ }
+ IfFailGo(AddToSigBuffer(W(")")));
+ goto ErrExit;
+ }
+
+ if (ulData == ELEMENT_TYPE_INTERNAL)
+ {
+ IfFailGo(AddToSigBuffer(W("MT ")));
+
+ void *pvMethodTable;
+ cb = CorSigUncompressPointer(&pbSigBlob[cbCur], (void**)&pvMethodTable);
+ cbCur += cb;
+
+ const size_t capacity_szMethodTableValue = 10;
+ WCHAR szMethodTableValue[10];
+ itow_s_ptr((INT_PTR)pvMethodTable, szMethodTableValue, capacity_szMethodTableValue, 16);
+
+ IfFailGo(AddToSigBuffer(szMethodTableValue));
+ IfFailGo(AddToSigBuffer(W(" ")));
+
+ IfFailGo(g_sos->GetMethodTableName(TO_CDADDR(pvMethodTable), mdNameLen, g_mdName, NULL));
+ IfFailGo(AddToSigBuffer(g_mdName));
+
+ goto ErrExit;
+ }
+
+
+ if(ulData != ELEMENT_TYPE_ARRAY) return E_FAIL;
+
+ // display the base type of SDARRAY
+ if (FAILED(GetOneElementType(&pbSigBlob[cbCur], ulSigBlob-cbCur, &cb)))
+ goto ErrExit;
+ cbCur += cb;
+
+ IfFailGo(AddToSigBuffer(W(" ")));
+ // display the rank of MDARRAY
+ cb = CorSigUncompressData(&pbSigBlob[cbCur], &ulData);
+ cbCur += cb;
+ WCHAR buffer[capacity_buffer];
+ _itow_s (ulData, buffer, capacity_buffer, 10);
+ IfFailGo(AddToSigBuffer(buffer));
+ if (ulData == 0)
+ // we are done if no rank specified
+ goto ErrExit;
+
+ IfFailGo(AddToSigBuffer(W(" ")));
+ // how many dimensions have size specified?
+ cb = CorSigUncompressData(&pbSigBlob[cbCur], &ulData);
+ cbCur += cb;
+ _itow_s (ulData, buffer, capacity_buffer, 10);
+ IfFailGo(AddToSigBuffer(buffer));
+ if (ulData == 0) {
+ IfFailGo(AddToSigBuffer(W(" ")));
+ }
+ while (ulData)
+ {
+
+ cb = CorSigUncompressData(&pbSigBlob[cbCur], &ulTemp);
+ _itow_s (ulTemp, buffer, capacity_buffer, 10);
+ IfFailGo(AddToSigBuffer(buffer));
+ IfFailGo(AddToSigBuffer(W(" ")));
+ cbCur += cb;
+ ulData--;
+ }
+ // how many dimensions have lower bounds specified?
+ cb = CorSigUncompressData(&pbSigBlob[cbCur], &ulData);
+ cbCur += cb;
+ _itow_s (ulData, buffer, capacity_buffer, 10);
+ IfFailGo(AddToSigBuffer(buffer));
+ while (ulData)
+ {
+
+ cb = CorSigUncompressSignedInt(&pbSigBlob[cbCur], &iTemp);
+ _itow_s (iTemp, buffer, capacity_buffer, 10);
+ IfFailGo(AddToSigBuffer(buffer));
+ IfFailGo(AddToSigBuffer(W(" ")));
+ cbCur += cb;
+ ulData--;
+ }
+
+ErrExit:
+ if (cbCur > ulSigBlob)
+ hr = E_FAIL;
+ *pcb = cbCur;
+ return hr;
+}
+
+//*****************************************************************************
+// Used when the method is tiny (< 64 bytes), and there are no local vars
+//*****************************************************************************
+typedef struct tagCOR_ILMETHOD_TINY : IMAGE_COR_ILMETHOD_TINY
+{
+ bool IsTiny() const { return((Flags_CodeSize & (CorILMethod_FormatMask >> 1)) == CorILMethod_TinyFormat); }
+ DWORD GetLocalVarSigTok() const { return(0); }
+} COR_ILMETHOD_TINY;
+
+
+//*****************************************************************************
+// This strucuture is the 'fat' layout, where no compression is attempted.
+// Note that this structure can be added on at the end, thus making it extensible
+//*****************************************************************************
+typedef struct tagCOR_ILMETHOD_FAT : IMAGE_COR_ILMETHOD_FAT
+{
+ bool IsFat() const { return((Flags & CorILMethod_FormatMask) == CorILMethod_FatFormat); }
+ mdToken GetLocalVarSigTok() const { return(LocalVarSigTok); }
+} COR_ILMETHOD_FAT;
diff --git a/src/ToolBox/SOS/Strike/ntinfo.h b/src/ToolBox/SOS/Strike/ntinfo.h
new file mode 100644
index 0000000000..f3e6e91ff0
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/ntinfo.h
@@ -0,0 +1,193 @@
+// 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.
+
+// ==++==
+//
+
+//
+// ==--==
+#ifndef _ntinfo_h__
+#define _ntinfo_h__
+
+//+---------------------------------------------------------------------------
+//
+// forward declarations (in order to avoid type casting when accessing
+// data members of the SOleTlsData structure).
+//
+//+---------------------------------------------------------------------------
+
+class CAptCallCtrl; // see callctrl.hxx
+class CSrvCallState; // see callctrl.hxx
+class CObjServer; // see sobjact.hxx
+class CSmAllocator; // see stg\h\smalloc.hxx
+class CMessageCall; // see call.hxx
+class CClientCall; // see call.hxx
+class CAsyncCall; // see call.hxx
+class CClipDataObject; // see ole232\clipbrd\clipdata.h
+class CSurrogatedObjectList; // see com\inc\comsrgt.hxx
+class CCtxCall; // see PSTable.hxx
+class CPolicySet; // see PSTable.hxx
+class CObjectContext; // see context.hxx
+class CComApartment; // see aprtmnt.hxx
+class ContextStackNode;
+//+-------------------------------------------------------------------
+//
+// Struct: CallEntry
+//
+// Synopsis: Call Table Entry.
+//
+//+-------------------------------------------------------------------
+typedef struct tagCallEntry
+{
+ void *pNext; // ptr to next entry
+ void *pvObject; // Entry object
+} CallEntry;
+
+//+---------------------------------------------------------------------------
+//
+// Enum: OLETLSFLAGS
+//
+// Synopsys: bit values for dwFlags field of SOleTlsData. If you just want
+// to store a BOOL in TLS, use this enum and the dwFlag field.
+//
+//+---------------------------------------------------------------------------
+typedef enum tagOLETLSFLAGS
+{
+ OLETLS_LOCALTID = 0x01, // This TID is in the current process.
+ OLETLS_UUIDINITIALIZED = 0x02, // This Logical thread is init'd.
+ OLETLS_INTHREADDETACH = 0x04, // This is in thread detach. Needed
+ // due to NT's special thread detach
+ // rules.
+ OLETLS_CHANNELTHREADINITIALZED = 0x08,// This channel has been init'd
+ OLETLS_WOWTHREAD = 0x10, // This thread is a 16-bit WOW thread.
+ OLETLS_THREADUNINITIALIZING = 0x20, // This thread is in CoUninitialize.
+ OLETLS_DISABLE_OLE1DDE = 0x40, // This thread can't use a DDE window.
+ OLETLS_APARTMENTTHREADED = 0x80, // This is an STA apartment thread
+ OLETLS_MULTITHREADED = 0x100, // This is an MTA apartment thread
+ OLETLS_IMPERSONATING = 0x200, // This thread is impersonating
+ OLETLS_DISABLE_EVENTLOGGER = 0x400, // Prevent recursion in event logger
+ OLETLS_INNEUTRALAPT = 0x800, // This thread is in the NTA
+ OLETLS_DISPATCHTHREAD = 0x1000, // This is a dispatch thread
+ OLETLS_HOSTTHREAD = 0x2000, // This is a host thread
+ OLETLS_ALLOWCOINIT = 0x4000, // This thread allows inits
+ OLETLS_PENDINGUNINIT = 0x8000, // This thread has pending uninit
+ OLETLS_FIRSTMTAINIT = 0x10000,// First thread to attempt an MTA init
+ OLETLS_FIRSTNTAINIT = 0x20000,// First thread to attempt an NTA init
+ OLETLS_APTINITIALIZING = 0x40000 // Apartment Object is initializing
+} OLETLSFLAGS;
+
+
+//+---------------------------------------------------------------------------
+//
+// Structure: SOleTlsData
+//
+// Synopsis: structure holding per thread state needed by OLE32
+//
+//+---------------------------------------------------------------------------
+typedef struct tagSOleTlsData
+{
+ // jsimmons 5/23/2001
+ // Alert Alert: nefarious folks (eg, URT) are looking in our TLS at
+ // various stuff. They expect that pCurrentCtx will be at a certain
+ // offset from the beginning of the tls struct. So don't add, delete, or
+ // move any members within this block.
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// ********* BEGIN "NO MUCKING AROUND" BLOCK *********
+/////////////////////////////////////////////////////////////////////////////////////////
+ // Docfile multiple allocator support
+ void *pvThreadBase; // per thread base pointer
+ CSmAllocator *pSmAllocator; // per thread docfile allocator
+
+ DWORD dwApartmentID; // Per thread "process ID"
+ DWORD dwFlags; // see OLETLSFLAGS above
+
+ LONG TlsMapIndex; // index in the global TLSMap
+ void **ppTlsSlot; // Back pointer to the thread tls slot
+ DWORD cComInits; // number of per-thread inits
+ DWORD cOleInits; // number of per-thread OLE inits
+
+ DWORD cCalls; // number of outstanding calls
+ CMessageCall *pCallInfo; // channel call info
+ CAsyncCall *pFreeAsyncCall; // ptr to available call object for this thread.
+ CClientCall *pFreeClientCall; // ptr to available call object for this thread.
+
+ CObjServer *pObjServer; // Activation Server Object for this apartment.
+ DWORD dwTIDCaller; // TID of current calling app
+ CObjectContext *pCurrentCtx; // Current context
+/////////////////////////////////////////////////////////////////////////////////////////
+// ********* END "NO MUCKING AROUND" BLOCK *********
+/////////////////////////////////////////////////////////////////////////////////////////
+
+ CObjectContext *pEmptyCtx; // Empty context
+
+ CObjectContext *pNativeCtx; // Native context
+ ULONGLONG ContextId; // Uniquely identifies the current context
+ CComApartment *pNativeApt; // Native apartment for the thread.
+ IUnknown *pCallContext; // call context object
+ CCtxCall *pCtxCall; // Context call object
+
+ CPolicySet *pPS; // Policy set
+ PVOID pvPendingCallsFront;// Per Apt pending async calls
+ PVOID pvPendingCallsBack;
+ CAptCallCtrl *pCallCtrl; // call control for RPC for this apartment
+
+ CSrvCallState *pTopSCS; // top server-side callctrl state
+ IMessageFilter *pMsgFilter; // temp storage for App MsgFilter
+ HWND hwndSTA; // STA server window same as poxid->hServerSTA
+ // ...needed on Win95 before oxid registration
+ LONG cORPCNestingLevel; // call nesting level (DBG only)
+
+ DWORD cDebugData; // count of bytes of debug data in call
+
+ UUID LogicalThreadId; // current logical thread id
+
+ HANDLE hThread; // Thread handle used for cancel
+ HANDLE hRevert; // Token before first impersonate.
+ IUnknown *pAsyncRelease; // Controlling unknown for async release
+ // DDE data
+ HWND hwndDdeServer; // Per thread Common DDE server
+
+ HWND hwndDdeClient; // Per thread Common DDE client
+ ULONG cServeDdeObjects; // non-zero if objects DDE should serve
+ // ClassCache data
+ LPVOID pSTALSvrsFront; // Chain of LServers registers in this thread if STA
+ // upper layer data
+ HWND hwndClip; // Clipboard window
+
+ IDataObject *pDataObjClip; // Current Clipboard DataObject
+ DWORD dwClipSeqNum; // Clipboard Sequence # for the above DataObject
+ DWORD fIsClipWrapper; // Did we hand out the wrapper Clipboard DataObject?
+ IUnknown *punkState; // Per thread "state" object
+ // cancel data
+ DWORD cCallCancellation; // count of CoEnableCallCancellation
+ // async sends data
+ DWORD cAsyncSends; // count of async sends outstanding
+
+ CAsyncCall* pAsyncCallList; // async calls outstanding
+ CSurrogatedObjectList *pSurrogateList; // Objects in the surrogate
+
+ LockEntry lockEntry; // Locks currently held by the thread
+ CallEntry CallEntry; // client-side call chain for this thread
+
+#ifdef WX86OLE
+ IUnknown *punkStateWx86; // Per thread "state" object for Wx86
+#endif
+ void *pDragCursors; // Per thread drag cursor table.
+
+ IUnknown *punkError; // Per thread error object.
+ ULONG cbErrorData; // Maximum size of error data.
+
+ IUnknown *punkActiveXSafetyProvider;
+
+#if DBG==1
+ LONG cTraceNestingLevel; // call nesting level for OLETRACE
+#endif
+
+ ContextStackNode* pContextStack;
+
+} SOleTlsData;
+
+#endif //_ntinfo_h__
+
diff --git a/src/ToolBox/SOS/Strike/platformspecific.h b/src/ToolBox/SOS/Strike/platformspecific.h
new file mode 100644
index 0000000000..fdbc5b52ca
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/platformspecific.h
@@ -0,0 +1,195 @@
+// 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 platform specific declarations based on the target platform rather than the host platform.
+
+#ifndef __PLATFORM_SPECIFIC_INCLUDED
+#define __PLATFORM_SPECIFIC_INCLUDED
+
+// The main debugger code already has target platform definitions for CONTEXT.
+#include "../../../debug/inc/dbgtargetcontext.h"
+
+#ifndef FEATURE_PAL
+
+// The various OS structure definitions below tend to differ based soley on the size of pointers. DT_POINTER
+// is a type whose size matches that of the target platform. It's integral rather than point since it is never
+// legal to dereference one of these on the host.
+#ifdef _TARGET_WIN64_
+typedef ULONG64 DT_POINTER;
+#else
+typedef ULONG32 DT_POINTER;
+#endif
+
+struct DT_LIST_ENTRY
+{
+ DT_POINTER Flink;
+ DT_POINTER Blink;
+};
+
+struct DT_UNICODE_STRING
+{
+ USHORT Length;
+ USHORT MaximumLength;
+ DT_POINTER Buffer;
+};
+
+#define DT_GDI_HANDLE_BUFFER_SIZE32 34
+#define DT_GDI_HANDLE_BUFFER_SIZE64 60
+
+#ifndef IMAGE_FILE_MACHINE_ARMNT
+#define IMAGE_FILE_MACHINE_ARMNT 0x01c4 // ARM Thumb-2 Little-Endian
+#endif
+
+#ifndef IMAGE_FILE_MACHINE_ARM64
+#define IMAGE_FILE_MACHINE_ARM64 0xAA64 // ARM64 Little-Endian
+#endif
+
+#ifdef _TARGET_WIN64_
+typedef ULONG DT_GDI_HANDLE_BUFFER[DT_GDI_HANDLE_BUFFER_SIZE64];
+#else
+typedef ULONG DT_GDI_HANDLE_BUFFER[DT_GDI_HANDLE_BUFFER_SIZE32];
+#endif
+
+struct DT_PEB
+{
+ BOOLEAN InheritedAddressSpace;
+ BOOLEAN ReadImageFileExecOptions;
+ BOOLEAN BeingDebugged;
+ BOOLEAN SpareBool;
+ DT_POINTER Mutant;
+ DT_POINTER ImageBaseAddress;
+ DT_POINTER Ldr;
+ DT_POINTER ProcessParameters;
+ DT_POINTER SubSystemData;
+ DT_POINTER ProcessHeap;
+ DT_POINTER FastPebLock;
+ DT_POINTER SparePtr1;
+ DT_POINTER SparePtr2;
+ ULONG EnvironmentUpdateCount;
+ DT_POINTER KernelCallbackTable;
+ ULONG SystemReserved[1];
+ struct _dummy {
+ ULONG ExecuteOptions : 2;
+ ULONG SpareBits : 30;
+ };
+ DT_POINTER FreeList;
+ ULONG TlsExpansionCounter;
+ DT_POINTER TlsBitmap;
+ ULONG TlsBitmapBits[2];
+ DT_POINTER ReadOnlySharedMemoryBase;
+ DT_POINTER ReadOnlySharedMemoryHeap;
+ DT_POINTER ReadOnlyStaticServerData;
+ DT_POINTER AnsiCodePageData;
+ DT_POINTER OemCodePageData;
+ DT_POINTER UnicodeCaseTableData;
+ ULONG NumberOfProcessors;
+ ULONG NtGlobalFlag;
+ LARGE_INTEGER CriticalSectionTimeout;
+ DT_POINTER HeapSegmentReserve;
+ DT_POINTER HeapSegmentCommit;
+ DT_POINTER HeapDeCommitTotalFreeThreshold;
+ DT_POINTER HeapDeCommitFreeBlockThreshold;
+ ULONG NumberOfHeaps;
+ ULONG MaximumNumberOfHeaps;
+ DT_POINTER ProcessHeaps;
+ DT_POINTER GdiSharedHandleTable;
+ DT_POINTER ProcessStarterHelper;
+ ULONG GdiDCAttributeList;
+ DT_POINTER LoaderLock;
+ ULONG OSMajorVersion;
+ ULONG OSMinorVersion;
+ USHORT OSBuildNumber;
+ USHORT OSCSDVersion;
+ ULONG OSPlatformId;
+ ULONG ImageSubsystem;
+ ULONG ImageSubsystemMajorVersion;
+ ULONG ImageSubsystemMinorVersion;
+ DT_POINTER ImageProcessAffinityMask;
+ DT_GDI_HANDLE_BUFFER GdiHandleBuffer;
+ DT_POINTER PostProcessInitRoutine;
+ DT_POINTER TlsExpansionBitmap;
+ ULONG TlsExpansionBitmapBits[32];
+ ULONG SessionId;
+ ULARGE_INTEGER AppCompatFlags;
+ ULARGE_INTEGER AppCompatFlagsUser;
+ DT_POINTER pShimData;
+ DT_POINTER AppCompatInfo;
+ DT_UNICODE_STRING CSDVersion;
+ DT_POINTER ActivationContextData;
+ DT_POINTER ProcessAssemblyStorageMap;
+ DT_POINTER SystemDefaultActivationContextData;
+ DT_POINTER SystemAssemblyStorageMap;
+ DT_POINTER MinimumStackCommit;
+ DT_POINTER FlsCallback;
+ DT_LIST_ENTRY FlsListHead;
+ DT_POINTER FlsBitmap;
+ ULONG FlsBitmapBits[FLS_MAXIMUM_AVAILABLE / (sizeof(ULONG) * 8)];
+ ULONG FlsHighIndex;
+};
+
+struct DT_PEB_LDR_DATA
+{
+ BYTE Reserved1[8];
+ DT_POINTER Reserved2[3];
+ DT_LIST_ENTRY InMemoryOrderModuleList;
+};
+
+struct DT_CURDIR
+{
+ DT_UNICODE_STRING DosPath;
+ DT_POINTER Handle;
+};
+
+struct DT_RTL_DRIVE_LETTER_CURDIR {
+ USHORT Flags;
+ USHORT Length;
+ ULONG TimeStamp;
+ STRING DosPath;
+};
+
+#define DT_RTL_MAX_DRIVE_LETTERS 32
+
+struct DT_RTL_USER_PROCESS_PARAMETERS
+{
+ ULONG MaximumLength;
+ ULONG Length;
+ ULONG Flags;
+ ULONG DebugFlags;
+ DT_POINTER ConsoleHandle;
+ ULONG ConsoleFlags;
+ DT_POINTER StandardInput;
+ DT_POINTER StandardOutput;
+ DT_POINTER StandardError;
+ DT_CURDIR CurrentDirectory;
+ DT_UNICODE_STRING DllPath;
+ DT_UNICODE_STRING ImagePathName;
+ DT_UNICODE_STRING CommandLine;
+ DT_POINTER Environment;
+ ULONG StartingX;
+ ULONG StartingY;
+ ULONG CountX;
+ ULONG CountY;
+ ULONG CountCharsX;
+ ULONG CountCharsY;
+ ULONG FillAttribute;
+ ULONG WindowFlags;
+ ULONG ShowWindowFlags;
+ DT_UNICODE_STRING WindowTitle;
+ DT_UNICODE_STRING DesktopInfo;
+ DT_UNICODE_STRING ShellInfo;
+ DT_UNICODE_STRING RuntimeData;
+ DT_RTL_DRIVE_LETTER_CURDIR CurrentDirectores[ DT_RTL_MAX_DRIVE_LETTERS ];
+};
+
+#endif // !FEATURE_PAL
+
+#define DT_OS_PAGE_SIZE 4096
+
+#endif // !__PLATFORM_SPECIFIC_INCLUDED
diff --git a/src/ToolBox/SOS/Strike/sildasm.cpp b/src/ToolBox/SOS/Strike/sildasm.cpp
new file mode 100644
index 0000000000..6bd3bb4801
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/sildasm.cpp
@@ -0,0 +1,1090 @@
+// 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.
+
+// ==++==
+//
+
+//
+// ==--==
+//
+// disasm.cpp : Defines the entry point for the console application.
+//
+#ifndef FEATURE_PAL
+#include <tchar.h>
+#endif
+#include "strike.h"
+#include "util.h"
+#include "strsafe.h"
+//#ifndef FEATURE_PAL
+//#include "gcinfo.h"
+//#endif
+#include "disasm.h"
+#include <dbghelp.h>
+
+#include "corhdr.h"
+
+#include "cor.h"
+#include "dacprivate.h"
+
+#include "openum.h"
+
+#include "sos_md.h"
+
+#define SOS_INCLUDE 1
+#include "corhlpr.h"
+#include "corhlpr.cpp"
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////
+#undef printf
+#define printf ExtOut
+
+// typedef unsigned char BYTE;
+struct OpCode
+{
+ int code;
+ const char *name;
+ int args;
+ BYTE b1;
+ BYTE b2;
+
+ unsigned int getCode() {
+ if (b1==0xFF) return b2;
+ else return (0xFE00 | b2);
+ }
+};
+
+#define OPCODES_LENGTH 0x122
+
+#undef OPDEF
+#define OPDEF(c,s,pop,push,args,type,l,s1,s2,ctrl) {c, s, args, s1, s2},
+static OpCode opcodes[] =
+{
+#include "opcode.def"
+};
+
+static ULONG position = 0;
+static BYTE *pBuffer = NULL;
+
+// The UNALIGNED is because on IA64 alignment rules would prevent
+// us from reading a pointer from an unaligned source.
+template <typename T>
+T readData ( ) {
+ T val = *((T UNALIGNED*)(pBuffer+position));
+ position += sizeof(T);
+ return val;
+}
+
+unsigned int readOpcode()
+{
+ unsigned int c = readData<BYTE>();
+ if (c == 0xFE)
+ {
+ c = readData<BYTE>();
+ c |= 0x100;
+ }
+ return c;
+}
+
+void DisassembleToken(IMetaDataImport *i,
+ DWORD token)
+{
+ HRESULT hr;
+
+ switch (TypeFromToken(token))
+ {
+ default:
+ printf("<unknown token type %08x>", TypeFromToken(token));
+ break;
+
+ case mdtTypeDef:
+ {
+ ULONG cLen;
+ WCHAR szName[50];
+
+ hr = i->GetTypeDefProps(token, szName, 49, &cLen, NULL, NULL);
+
+ if (FAILED(hr))
+ StringCchCopyW(szName, COUNTOF(szName), W("<unknown type def>"));
+
+ printf("%S", szName);
+ }
+ break;
+
+ case mdtTypeRef:
+ {
+ ULONG cLen;
+ WCHAR szName[50];
+
+ hr = i->GetTypeRefProps(token, NULL, szName, 49, &cLen);
+
+ if (FAILED(hr))
+ StringCchCopyW(szName, COUNTOF(szName), W("<unknown type ref>"));
+
+ printf("%S", szName);
+ }
+ break;
+
+ case mdtFieldDef:
+ {
+ ULONG cLen;
+ WCHAR szFieldName[50];
+ WCHAR szClassName[50];
+ mdTypeDef mdClass;
+
+ hr = i->GetFieldProps(token, &mdClass, szFieldName, 49, &cLen,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+
+ if (FAILED(hr))
+ StringCchCopyW(szFieldName, COUNTOF(szFieldName), W("<unknown field def>"));
+
+ hr = i->GetTypeDefProps(mdClass, szClassName, 49, &cLen,
+ NULL, NULL);
+
+ if (FAILED(hr))
+ StringCchCopyW(szClassName, COUNTOF(szClassName), W("<unknown type def>"));
+
+ printf("%S::%S", szClassName, szFieldName);
+ }
+ break;
+
+ case mdtMethodDef:
+ {
+ ULONG cLen;
+ WCHAR szFieldName[50];
+ WCHAR szClassName[50];
+ mdTypeDef mdClass;
+
+ hr = i->GetMethodProps(token, &mdClass, szFieldName, 49, &cLen,
+ NULL, NULL, NULL, NULL, NULL);
+
+ if (FAILED(hr))
+ StringCchCopyW(szFieldName, COUNTOF(szFieldName), W("<unknown method def>"));
+
+ hr = i->GetTypeDefProps(mdClass, szClassName, 49, &cLen,
+ NULL, NULL);
+
+ if (FAILED(hr))
+ StringCchCopyW(szClassName, COUNTOF(szClassName), W("<unknown type def>"));
+
+ printf("%S::%S", szClassName, szFieldName);
+ }
+ break;
+
+ case mdtMemberRef:
+ {
+ mdTypeRef cr = mdTypeRefNil;
+ LPCWSTR pMemberName;
+ WCHAR memberName[50];
+ ULONG memberNameLen;
+
+ hr = i->GetMemberRefProps(token, &cr, memberName, 49,
+ &memberNameLen, NULL, NULL);
+
+ if (FAILED(hr))
+ {
+ pMemberName = W("<unknown member ref>");
+ }
+ else
+ pMemberName = memberName;
+
+ ULONG cLen;
+ WCHAR szName[50];
+
+ if(TypeFromToken(cr) == mdtTypeRef)
+ {
+ if (FAILED(i->GetTypeRefProps(cr, NULL, szName, 50, &cLen)))
+ {
+ StringCchCopyW(szName, COUNTOF(szName), W("<unknown type ref>"));
+ }
+ }
+ else if(TypeFromToken(cr) == mdtTypeDef)
+ {
+ if (FAILED(i->GetTypeDefProps(cr, szName, 49, &cLen,
+ NULL, NULL)))
+ {
+ StringCchCopyW(szName, COUNTOF(szName), W("<unknown type def>"));
+ }
+ }
+ else if(TypeFromToken(cr) == mdtTypeSpec)
+ {
+ IMDInternalImport *pIMDI = NULL;
+ if (SUCCEEDED(GetMDInternalFromImport(i, &pIMDI)))
+ {
+ CQuickBytes out;
+ ULONG cSig;
+ PCCOR_SIGNATURE sig;
+ if (FAILED(pIMDI->GetSigFromToken(cr, &cSig, &sig)))
+ {
+ StringCchCopyW(szName, COUNTOF(szName), W("<Invalid record>"));
+ }
+ else
+ {
+ PrettyPrintType(sig, &out, pIMDI);
+ MultiByteToWideChar (CP_ACP, 0, asString(&out), -1, szName, 50);
+ }
+
+ pIMDI->Release();
+ }
+ else
+ {
+ StringCchCopyW(szName, COUNTOF(szName), W("<unknown type spec>"));
+ }
+ }
+ else
+ {
+ StringCchCopyW(szName, COUNTOF(szName), W("<unknown type token>"));
+ }
+
+ printf("%S::%S ", szName, pMemberName);
+ }
+ break;
+ }
+}
+
+ULONG GetILSize(DWORD_PTR ilAddr)
+{
+ ULONG uRet = 0;
+
+ // workaround: read enough bytes at ilAddr to presumably get the entire header.
+ // Could be error prone.
+
+ static BYTE headerArray[1024];
+ HRESULT Status = g_ExtData->ReadVirtual(TO_CDADDR(ilAddr), headerArray, sizeof(headerArray), NULL);
+ if (SUCCEEDED(Status))
+ {
+ COR_ILMETHOD_DECODER header((COR_ILMETHOD *)headerArray);
+ // uRet = header.GetHeaderSize();
+ uRet = header.GetOnDiskSize((COR_ILMETHOD *)headerArray);
+ }
+
+ return uRet;
+}
+
+HRESULT DecodeILFromAddress(IMetaDataImport *pImport, TADDR ilAddr)
+{
+ HRESULT Status = S_OK;
+
+ ULONG Size = GetILSize(ilAddr);
+ if (Size == 0)
+ {
+ ExtOut("error decoding IL\n");
+ return Status;
+ }
+
+ ExtOut("ilAddr = %p\n", SOS_PTR(ilAddr));
+
+ // Read the memory into a local buffer
+ ArrayHolder<BYTE> pArray = new BYTE[Size];
+ Status = g_ExtData->ReadVirtual(TO_CDADDR(ilAddr), pArray, Size, NULL);
+ if (Status != S_OK)
+ {
+ ExtOut("Failed to read memory\n");
+ return Status;
+ }
+
+ DecodeIL(pImport, pArray, Size);
+
+ return Status;
+}
+
+void DecodeIL(IMetaDataImport *pImport, BYTE *buffer, ULONG bufSize)
+{
+ // First decode the header
+ COR_ILMETHOD *pHeader = (COR_ILMETHOD *) buffer;
+ COR_ILMETHOD_DECODER header(pHeader);
+
+ // Set globals
+ position = 0;
+ pBuffer = (BYTE *) header.Code;
+
+ UINT indentCount = 0;
+ ULONG endCodePosition = header.GetCodeSize();
+ while(position < endCodePosition)
+ {
+ for (unsigned e=0;e<header.EHCount();e++)
+ {
+ IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT ehBuff;
+ const IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT* ehInfo;
+
+ ehInfo = header.EH->EHClause(e,&ehBuff);
+ if (ehInfo->TryOffset == position)
+ {
+ printf ("%*s.try\n%*s{\n", indentCount, "", indentCount, "");
+ indentCount+=2;
+ }
+ else if ((ehInfo->TryOffset + ehInfo->TryLength) == position)
+ {
+ indentCount-=2;
+ printf("%*s} // end .try\n", indentCount, "");
+ }
+
+ if (ehInfo->HandlerOffset == position)
+ {
+ if (ehInfo->Flags == COR_ILEXCEPTION_CLAUSE_FINALLY)
+ printf("%*s.finally\n%*s{\n", indentCount, "", indentCount, "");
+ else
+ printf("%*s.catch\n%*s{\n", indentCount, "", indentCount, "");
+
+ indentCount+=2;
+ }
+ else if ((ehInfo->HandlerOffset + ehInfo->HandlerLength) == position)
+ {
+ indentCount-=2;
+
+ if (ehInfo->Flags == COR_ILEXCEPTION_CLAUSE_FINALLY)
+ printf("%*s} // end .finally\n", indentCount, "");
+ else
+ printf("%*s} // end .catch\n", indentCount, "");
+ }
+ }
+
+ printf("%*sIL_%04x: ", indentCount, "", position);
+ unsigned int c = readOpcode();
+ OpCode opcode = opcodes[c];
+ printf("%s ", opcode.name);
+
+ switch(opcode.args)
+ {
+ case InlineNone: break;
+
+ case ShortInlineVar:
+ printf("VAR OR ARG %d",readData<BYTE>()); break;
+ case InlineVar:
+ printf("VAR OR ARG %d",readData<WORD>()); break;
+ case InlineI:
+ printf("%d",readData<LONG>());
+ break;
+ case InlineR:
+ printf("%f",readData<double>());
+ break;
+ case InlineBrTarget:
+ printf("IL_%04x",readData<LONG>() + position); break;
+ case ShortInlineBrTarget:
+ printf("IL_%04x",readData<BYTE>() + position); break;
+ case InlineI8:
+ printf("%ld", readData<__int64>()); break;
+
+ case InlineMethod:
+ case InlineField:
+ case InlineType:
+ case InlineTok:
+ case InlineSig:
+ {
+ LONG l = readData<LONG>();
+ if (pImport != NULL)
+ {
+ DisassembleToken(pImport, l);
+ }
+ else
+ {
+ printf("TOKEN %x", l);
+ }
+ break;
+ }
+
+ case InlineString:
+ {
+ LONG l = readData<LONG>();
+
+ ULONG numChars;
+ WCHAR str[84];
+
+ if ((pImport != NULL) && (pImport->GetUserString((mdString) l, str, 80, &numChars) == S_OK))
+ {
+ if (numChars < 80)
+ str[numChars] = 0;
+ wcscpy_s(&str[79], 4, W("..."));
+ WCHAR* ptr = str;
+ while(*ptr != 0) {
+ if (*ptr < 0x20 || * ptr >= 0x80) {
+ *ptr = '.';
+ }
+ ptr++;
+ }
+
+ printf("\"%S\"", str);
+ }
+ else
+ {
+ printf("STRING %x", l);
+ }
+ break;
+ }
+
+ case InlineSwitch:
+ {
+ LONG cases = readData<LONG>();
+ LONG *pArray = new LONG[cases];
+ LONG i=0;
+ for(i=0;i<cases;i++)
+ {
+ pArray[i] = readData<LONG>();
+ }
+ printf("(");
+ for(i=0;i<cases;i++)
+ {
+ if (i != 0)
+ printf(", ");
+ printf("IL_%04x",pArray[i] + position);
+ }
+ printf(")");
+ delete [] pArray;
+ break;
+ }
+ case ShortInlineI:
+ printf("%d", readData<BYTE>()); break;
+ case ShortInlineR:
+ printf("%f", readData<float>()); break;
+ default: printf("Error, unexpected opcode type\n"); break;
+ }
+
+ printf("\n");
+ }
+}
+
+DWORD_PTR GetObj(DacpObjectData& tokenArray, UINT item)
+{
+ if (item < tokenArray.dwNumComponents)
+ {
+ DWORD_PTR dwAddr = (DWORD_PTR) (tokenArray.ArrayDataPtr + tokenArray.dwComponentSize*item);
+ DWORD_PTR objPtr;
+ if (SUCCEEDED(MOVE(objPtr, dwAddr)))
+ {
+ return objPtr;
+ }
+ }
+ return NULL;
+}
+
+
+void DisassembleToken(DacpObjectData& tokenArray,
+ DWORD token)
+{
+ switch (TypeFromToken(token))
+ {
+ default:
+ printf("<unknown token type (token=%08x)>", token);
+ break;
+
+ case mdtTypeDef:
+ {
+ DWORD_PTR runtimeTypeHandle = GetObj(tokenArray, RidFromToken(token));
+
+ DWORD_PTR runtimeType = NULL;
+ MOVE(runtimeType, runtimeTypeHandle + sizeof(DWORD_PTR));
+
+ int offset = GetObjFieldOffset(runtimeType, W("m_handle"));
+
+ DWORD_PTR methodTable = NULL;
+ MOVE(methodTable, runtimeType + offset);
+
+ if (NameForMT_s(methodTable, g_mdName,mdNameLen))
+ {
+ printf("%x \"%S\"", token, g_mdName);
+ }
+ else
+ {
+ printf("<invalid MethodTable>");
+ }
+ }
+ break;
+
+ case mdtSignature:
+ case mdtTypeRef:
+ {
+ printf ("%x (%p)", token, SOS_PTR(GetObj(tokenArray, RidFromToken(token))));
+ }
+ break;
+
+ case mdtFieldDef:
+ {
+ printf ("%x (%p)", token, SOS_PTR(GetObj(tokenArray, RidFromToken(token))));
+ }
+ break;
+
+ case mdtMethodDef:
+ {
+ CLRDATA_ADDRESS runtimeMethodHandle = GetObj(tokenArray, RidFromToken(token));
+ int offset = GetObjFieldOffset(runtimeMethodHandle, W("m_value"));
+
+ TADDR runtimeMethodInfo = NULL;
+ MOVE(runtimeMethodInfo, runtimeMethodHandle+offset);
+
+ offset = GetObjFieldOffset(runtimeMethodInfo, W("m_handle"));
+
+ TADDR methodDesc = NULL;
+ MOVE(methodDesc, runtimeMethodInfo+offset);
+
+ NameForMD_s((DWORD_PTR)methodDesc, g_mdName, mdNameLen);
+ printf ("%x %S", token, g_mdName);
+ }
+ break;
+
+ case mdtMemberRef:
+ {
+ printf ("%x (%p)", token, SOS_PTR(GetObj(tokenArray, RidFromToken(token))));
+ }
+ break;
+ case mdtString:
+ {
+ DWORD_PTR strObj = GetObj(tokenArray, RidFromToken(token));
+ printf ("%x \"", token);
+ StringObjectContent (strObj, FALSE, 40);
+ printf ("\"");
+ }
+ break;
+ }
+}
+
+// Similar to the function above. TODO: factor them together before checkin.
+void DecodeDynamicIL(BYTE *data, ULONG Size, DacpObjectData& tokenArray)
+{
+ // There is no header for this dynamic guy.
+ // Set globals
+ position = 0;
+ pBuffer = data;
+
+ // At this time no exception information will be displayed (fix soon)
+ UINT indentCount = 0;
+ ULONG endCodePosition = Size;
+ while(position < endCodePosition)
+ {
+ printf("%*sIL_%04x: ", indentCount, "", position);
+ unsigned int c = readOpcode();
+ OpCode opcode = opcodes[c];
+ printf("%s ", opcode.name);
+
+ switch(opcode.args)
+ {
+ case InlineNone: break;
+
+ case ShortInlineVar:
+ printf("VAR OR ARG %d",readData<BYTE>()); break;
+ case InlineVar:
+ printf("VAR OR ARG %d",readData<WORD>()); break;
+ case InlineI:
+ printf("%d",readData<LONG>());
+ break;
+ case InlineR:
+ printf("%f",readData<double>());
+ break;
+ case InlineBrTarget:
+ printf("IL_%04x",readData<LONG>() + position); break;
+ case ShortInlineBrTarget:
+ printf("IL_%04x",readData<BYTE>() + position); break;
+ case InlineI8:
+ printf("%ld", readData<__int64>()); break;
+
+ case InlineMethod:
+ case InlineField:
+ case InlineType:
+ case InlineTok:
+ case InlineSig:
+ case InlineString:
+ {
+ LONG l = readData<LONG>();
+ DisassembleToken(tokenArray, l);
+ break;
+ }
+
+ case InlineSwitch:
+ {
+ LONG cases = readData<LONG>();
+ LONG *pArray = new LONG[cases];
+ LONG i=0;
+ for(i=0;i<cases;i++)
+ {
+ pArray[i] = readData<LONG>();
+ }
+ printf("(");
+ for(i=0;i<cases;i++)
+ {
+ if (i != 0)
+ printf(", ");
+ printf("IL_%04x",pArray[i] + position);
+ }
+ printf(")");
+ delete [] pArray;
+ break;
+ }
+ case ShortInlineI:
+ printf("%d", readData<BYTE>()); break;
+ case ShortInlineR:
+ printf("%f", readData<float>()); break;
+ default: printf("Error, unexpected opcode type\n"); break;
+ }
+
+ printf("\n");
+ }
+}
+
+
+
+/******************************************************************************/
+// CQuickBytes utilities
+static char* asString(CQuickBytes *out) {
+ SIZE_T oldSize = out->Size();
+ out->ReSize(oldSize + 1);
+ char* cur = &((char*) out->Ptr())[oldSize];
+ *cur = 0;
+ out->ReSize(oldSize); // Don't count the null character
+ return((char*) out->Ptr());
+}
+
+static void appendStr(CQuickBytes *out, const char* str, unsigned len=-1) {
+ if(len == (unsigned)(-1)) len = (unsigned)strlen(str);
+ SIZE_T oldSize = out->Size();
+ out->ReSize(oldSize + len);
+ char* cur = &((char*) out->Ptr())[oldSize];
+ memcpy(cur, str, len);
+ // Note no trailing null!
+}
+
+static void appendChar(CQuickBytes *out, char chr) {
+ SIZE_T oldSize = out->Size();
+ out->ReSize(oldSize + 1);
+ ((char*) out->Ptr())[oldSize] = chr;
+ // Note no trailing null!
+}
+
+static void insertStr(CQuickBytes *out, const char* str) {
+ unsigned len = (unsigned)strlen(str);
+ SIZE_T oldSize = out->Size();
+ out->ReSize(oldSize + len);
+ char* cur = &((char*) out->Ptr())[len];
+ memmove(cur,out->Ptr(),oldSize);
+ memcpy(out->Ptr(), str, len);
+ // Note no trailing null!
+}
+
+static void appendStrNum(CQuickBytes *out, int num) {
+ char buff[16];
+ sprintf_s(buff, COUNTOF(buff), "%d", num);
+ appendStr(out, buff);
+}
+
+
+//PrettyPrinting type names
+PCCOR_SIGNATURE PrettyPrintType(
+ PCCOR_SIGNATURE typePtr, // type to convert,
+ CQuickBytes *out, // where to put the pretty printed string
+ IMDInternalImport *pIMDI, // ptr to IMDInternal class with ComSig
+ DWORD formatFlags /*= formatILDasm*/)
+{
+ mdToken tk;
+ const char* str;
+ int typ;
+ CQuickBytes tmp;
+ CQuickBytes Appendix;
+ BOOL Reiterate;
+ int n;
+
+ do {
+ Reiterate = FALSE;
+ switch(typ = *typePtr++) {
+ case ELEMENT_TYPE_VOID :
+ str = "void"; goto APPEND;
+ case ELEMENT_TYPE_BOOLEAN :
+ str = "bool"; goto APPEND;
+ case ELEMENT_TYPE_CHAR :
+ str = "char"; goto APPEND;
+ case ELEMENT_TYPE_I1 :
+ str = "int8"; goto APPEND;
+ case ELEMENT_TYPE_U1 :
+ str = "uint8"; goto APPEND;
+ case ELEMENT_TYPE_I2 :
+ str = "int16"; goto APPEND;
+ case ELEMENT_TYPE_U2 :
+ str = "uint16"; goto APPEND;
+ case ELEMENT_TYPE_I4 :
+ str = "int32"; goto APPEND;
+ case ELEMENT_TYPE_U4 :
+ str = "uint32"; goto APPEND;
+ case ELEMENT_TYPE_I8 :
+ str = "int64"; goto APPEND;
+ case ELEMENT_TYPE_U8 :
+ str = "uint64"; goto APPEND;
+ case ELEMENT_TYPE_R4 :
+ str = "float32"; goto APPEND;
+ case ELEMENT_TYPE_R8 :
+ str = "float64"; goto APPEND;
+ case ELEMENT_TYPE_U :
+ str = "native uint"; goto APPEND;
+ case ELEMENT_TYPE_I :
+ str = "native int"; goto APPEND;
+ case ELEMENT_TYPE_OBJECT :
+ str = "object"; goto APPEND;
+ case ELEMENT_TYPE_STRING :
+ str = "string"; goto APPEND;
+ case ELEMENT_TYPE_TYPEDBYREF :
+ str = "typedref"; goto APPEND;
+ APPEND:
+ appendStr(out, (char*)str);
+ break;
+
+ case ELEMENT_TYPE_VALUETYPE :
+ if ((formatFlags & FormatKwInNames) != 0)
+ str = "valuetype ";
+ else str = "";
+ goto DO_CLASS;
+ case ELEMENT_TYPE_CLASS :
+ if ((formatFlags & FormatKwInNames) != 0)
+ str = "class ";
+ else str = "";
+ goto DO_CLASS;
+
+ DO_CLASS:
+ appendStr(out, (char*)str);
+ typePtr += CorSigUncompressToken(typePtr, &tk);
+ if(IsNilToken(tk))
+ {
+ appendStr(out, "[ERROR! NIL TOKEN]");
+ }
+ else PrettyPrintClass(out, tk, pIMDI, formatFlags);
+ break;
+
+ case ELEMENT_TYPE_SZARRAY :
+ insertStr(&Appendix,"[]");
+ Reiterate = TRUE;
+ break;
+
+ case ELEMENT_TYPE_ARRAY :
+ {
+ typePtr = PrettyPrintType(typePtr, out, pIMDI, formatFlags);
+ unsigned rank = CorSigUncompressData(typePtr);
+ // <TODO> what is the syntax for the rank 0 case? </TODO>
+ if (rank == 0) {
+ appendStr(out, "[BAD: RANK == 0!]");
+ }
+ else {
+ _ASSERTE(rank != 0);
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:22009) // "Suppress PREfast warnings about integer overflow"
+// PREFAST warns about using _alloca in a loop. However when we're in this switch case we do NOT
+// set Reiterate to true, so we only execute through the loop once!
+#pragma warning(disable:6263) // "Suppress PREfast warnings about stack overflow due to _alloca in a loop."
+#endif
+ int* lowerBounds = (int*) _alloca(sizeof(int)*2*rank);
+ int* sizes = &lowerBounds[rank];
+ memset(lowerBounds, 0, sizeof(int)*2*rank);
+
+ unsigned numSizes = CorSigUncompressData(typePtr);
+ _ASSERTE(numSizes <= rank);
+ unsigned i;
+ for(i =0; i < numSizes; i++)
+ sizes[i] = CorSigUncompressData(typePtr);
+
+ unsigned numLowBounds = CorSigUncompressData(typePtr);
+ _ASSERTE(numLowBounds <= rank);
+ for(i = 0; i < numLowBounds; i++)
+ typePtr+=CorSigUncompressSignedInt(typePtr,&lowerBounds[i]);
+
+ appendChar(out, '[');
+ if (rank == 1 && numSizes == 0 && numLowBounds == 0)
+ appendStr(out, "...");
+ else {
+ for(i = 0; i < rank; i++)
+ {
+ //if (sizes[i] != 0 || lowerBounds[i] != 0)
+ {
+ if (lowerBounds[i] == 0 && i < numSizes)
+ appendStrNum(out, sizes[i]);
+ else
+ {
+ if(i < numLowBounds)
+ {
+ appendStrNum(out, lowerBounds[i]);
+ appendStr(out, "...");
+ if (/*sizes[i] != 0 && */i < numSizes)
+ appendStrNum(out, lowerBounds[i] + sizes[i] - 1);
+ }
+ }
+ }
+ if (i < rank-1)
+ appendChar(out, ',');
+ }
+ }
+ appendChar(out, ']');
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+ }
+ } break;
+
+ case ELEMENT_TYPE_VAR :
+ appendChar(out, '!');
+ n = CorSigUncompressData(typePtr);
+ appendStrNum(out, n);
+ break;
+
+ case ELEMENT_TYPE_MVAR :
+ appendChar(out, '!');
+ appendChar(out, '!');
+ n = CorSigUncompressData(typePtr);
+ appendStrNum(out, n);
+ break;
+
+ case ELEMENT_TYPE_FNPTR :
+ appendStr(out, "method ");
+ appendStr(out, "METHOD"); // was: typePtr = PrettyPrintSignature(typePtr, 0x7FFF, "*", out, pIMDI, NULL);
+ break;
+
+ case ELEMENT_TYPE_GENERICINST :
+ {
+ typePtr = PrettyPrintType(typePtr, out, pIMDI, formatFlags);
+ if ((formatFlags & FormatSignature) == 0)
+ break;
+
+ if ((formatFlags & FormatAngleBrackets) != 0)
+ appendStr(out, "<");
+ else
+ appendStr(out,"[");
+ unsigned numArgs = CorSigUncompressData(typePtr);
+ bool needComma = false;
+ while(numArgs--)
+ {
+ if (needComma)
+ appendChar(out, ',');
+ typePtr = PrettyPrintType(typePtr, out, pIMDI, formatFlags);
+ needComma = true;
+ }
+ if ((formatFlags & FormatAngleBrackets) != 0)
+ appendStr(out, ">");
+ else
+ appendStr(out,"]");
+ break;
+ }
+
+ case ELEMENT_TYPE_PINNED :
+ str = " pinned"; goto MODIFIER;
+ case ELEMENT_TYPE_PTR :
+ str = "*"; goto MODIFIER;
+ case ELEMENT_TYPE_BYREF :
+ str = "&"; goto MODIFIER;
+ MODIFIER:
+ insertStr(&Appendix, str);
+ Reiterate = TRUE;
+ break;
+
+ default:
+ case ELEMENT_TYPE_SENTINEL :
+ case ELEMENT_TYPE_END :
+ //_ASSERTE(!"Unknown Type");
+ if(typ)
+ {
+ char sz[64];
+ sprintf_s(sz,COUNTOF(sz),"/* UNKNOWN TYPE (0x%X)*/",typ);
+ appendStr(out, sz);
+ }
+ break;
+ } // end switch
+ } while(Reiterate);
+ if (Appendix.Size() > 0)
+ appendStr(out,asString(&Appendix));
+
+ return(typePtr);
+}
+
+// Protection against null names, used by ILDASM/SOS
+const char *const szStdNamePrefix[] = {"MO","TR","TD","","FD","","MD","","PA","II","MR","","CA","","PE","","","SG","","","EV",
+"","","PR","","","MOR","TS","","","","","AS","","","AR","","","FL","ET","MAR"};
+
+#define MAKE_NAME_IF_NONE(psz, tk) { if(!(psz && *psz)) { char* sz = (char*)_alloca(16); \
+sprintf_s(sz,16,"$%s$%X",szStdNamePrefix[tk>>24],tk&0x00FFFFFF); psz = sz; } }
+
+const char* PrettyPrintClass(
+ CQuickBytes *out, // where to put the pretty printed string
+ mdToken tk, // The class token to look up
+ IMDInternalImport *pIMDI, // ptr to IMDInternalImport class with ComSig
+ DWORD formatFlags /*= formatILDasm*/)
+{
+ if(tk == mdTokenNil) // Zero resolution scope for "somewhere here" TypeRefs
+ {
+ appendStr(out,"[*]");
+ return(asString(out));
+ }
+ if (!pIMDI->IsValidToken(tk))
+ {
+ char str[1024];
+ sprintf_s(str,COUNTOF(str)," [ERROR: INVALID TOKEN 0x%8.8X] ",tk);
+ appendStr(out, str);
+ return(asString(out));
+ }
+ switch (TypeFromToken(tk))
+ {
+ case mdtTypeRef:
+ case mdtTypeDef:
+ {
+ const char *nameSpace = 0;
+ const char *name = 0;
+ mdToken tkEncloser = mdTokenNil;
+
+ if (TypeFromToken(tk) == mdtTypeRef)
+ {
+ if (((formatFlags & FormatAssembly) && FAILED(pIMDI->GetResolutionScopeOfTypeRef(tk, &tkEncloser))) ||
+ FAILED(pIMDI->GetNameOfTypeRef(tk, &nameSpace, &name)))
+ {
+ char str[1024];
+ sprintf_s(str, COUNTOF(str), " [ERROR: Invalid TypeRef record 0x%8.8X] ", tk);
+ appendStr(out, str);
+ return asString(out);
+ }
+ }
+ else
+ {
+ if (((formatFlags & FormatNamespace) == 0) || FAILED(pIMDI->GetNestedClassProps(tk,&tkEncloser)))
+ {
+ tkEncloser = mdTypeDefNil;
+ }
+ if (FAILED(pIMDI->GetNameOfTypeDef(tk, &name, &nameSpace)))
+ {
+ char str[1024];
+ sprintf_s(str, COUNTOF(str), " [ERROR: Invalid TypeDef record 0x%8.8X] ", tk);
+ appendStr(out, str);
+ return asString(out);
+ }
+ }
+ MAKE_NAME_IF_NONE(name,tk);
+ if((tkEncloser == mdTokenNil) || RidFromToken(tkEncloser))
+ {
+ if (TypeFromToken(tkEncloser) == mdtTypeRef || TypeFromToken(tkEncloser) == mdtTypeDef)
+ {
+ PrettyPrintClass(out,tkEncloser,pIMDI, formatFlags);
+ if (formatFlags & FormatSlashSep)
+ appendChar(out, '/');
+ else
+ appendChar(out, '+');
+ //nameSpace = ""; //don't print namespaces for nested classes!
+ }
+ else if (formatFlags & FormatAssembly)
+ {
+ PrettyPrintClass(out,tkEncloser,pIMDI, formatFlags);
+ }
+ }
+ if(TypeFromToken(tk)==mdtTypeDef)
+ {
+ unsigned L = (unsigned)strlen(name)+1;
+ char* szFN = NULL;
+ if(((formatFlags & FormatNamespace) != 0) && nameSpace && *nameSpace)
+ {
+ const char* sz = nameSpace;
+ L+= (unsigned)strlen(sz)+1;
+ szFN = new char[L];
+ sprintf_s(szFN,L,"%s.",sz);
+ }
+ else
+ {
+ szFN = new char[L];
+ *szFN = 0;
+ }
+ strcat_s(szFN,L, name);
+ appendStr(out, szFN);
+ if (szFN) delete[] (szFN);
+ }
+ else
+ {
+ if (((formatFlags & FormatNamespace) != 0) && nameSpace && *nameSpace) {
+ appendStr(out, nameSpace);
+ appendChar(out, '.');
+ }
+
+ appendStr(out, name);
+ }
+ }
+ break;
+
+ case mdtAssemblyRef:
+ {
+ LPCSTR szName = NULL;
+ pIMDI->GetAssemblyRefProps(tk,NULL,NULL,&szName,NULL,NULL,NULL,NULL);
+ if(szName && *szName)
+ {
+ appendChar(out, '[');
+ appendStr(out, szName);
+ appendChar(out, ']');
+ }
+ }
+ break;
+ case mdtAssembly:
+ {
+ LPCSTR szName = NULL;
+ pIMDI->GetAssemblyProps(tk,NULL,NULL,NULL,&szName,NULL,NULL);
+ if(szName && *szName)
+ {
+ appendChar(out, '[');
+ appendStr(out, szName);
+ appendChar(out, ']');
+ }
+ }
+ break;
+ case mdtModuleRef:
+ {
+ LPCSTR szName = NULL;
+ pIMDI->GetModuleRefProps(tk,&szName);
+ if(szName && *szName)
+ {
+ appendChar(out, '[');
+ appendStr(out, ".module ");
+ appendStr(out, szName);
+ appendChar(out, ']');
+ }
+ }
+ break;
+
+ case mdtTypeSpec:
+ {
+ ULONG cSig;
+ PCCOR_SIGNATURE sig;
+ if (FAILED(pIMDI->GetSigFromToken(tk, &cSig, &sig)))
+ {
+ char str[128];
+ sprintf_s(str, COUNTOF(str), " [ERROR: Invalid token 0x%8.8X] ", tk);
+ appendStr(out, str);
+ }
+ else
+ {
+ PrettyPrintType(sig, out, pIMDI, formatFlags);
+ }
+ }
+ break;
+
+ case mdtModule:
+ break;
+
+ default:
+ {
+ char str[128];
+ sprintf_s(str,COUNTOF(str)," [ERROR: INVALID TOKEN TYPE 0x%8.8X] ",tk);
+ appendStr(out, str);
+ }
+ }
+ return(asString(out));
+}
+
+// This function takes a module and a token and prints the representation in the mdName buffer.
+void PrettyPrintClassFromToken(
+ TADDR moduleAddr, // the module containing the token
+ mdToken tok, // the class token to look up
+ __out_ecount(cbName) WCHAR* mdName, // where to put the pretty printed string
+ size_t cbName, // the capacity of the buffer
+ DWORD formatFlags /*= FormatCSharp*/)
+{
+ // set the default value
+ swprintf_s(mdName, cbName, W("token_0x%8.8X"), tok);
+
+ DacpModuleData dmd;
+ if (dmd.Request(g_sos, TO_CDADDR(moduleAddr)) != S_OK)
+ return;
+
+ ToRelease<IMetaDataImport> pImport(MDImportForModule(&dmd));
+ ToRelease<IMDInternalImport> pIMDI = NULL;
+
+ if ((pImport == NULL) || FAILED(GetMDInternalFromImport(pImport, &pIMDI)))
+ return;
+
+ CQuickBytes qb;
+ PrettyPrintClass(&qb, tok, pIMDI, formatFlags);
+ MultiByteToWideChar (CP_ACP, 0, asString(&qb), -1, mdName, (int) cbName);
+}
diff --git a/src/ToolBox/SOS/Strike/sos.cpp b/src/ToolBox/SOS/Strike/sos.cpp
new file mode 100644
index 0000000000..351199c058
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/sos.cpp
@@ -0,0 +1,888 @@
+// 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 "strike.h"
+#include "util.h"
+
+#include "sos.h"
+
+
+#ifdef _ASSERTE
+#undef _ASSERTE
+#endif
+
+#define _ASSERTE(a) {;}
+
+#include "gcdesc.h"
+
+
+#ifdef _ASSERTE
+#undef _ASSERTE
+#endif
+
+namespace sos
+{
+ template <class T>
+ static bool MemOverlap(T beg1, T end1, // first range
+ T beg2, T end2) // second range
+ {
+ if (beg2 >= beg1 && beg2 <= end1) // second range starts within first range
+ return true;
+ else if (end2 >= beg1 && end2 <= end1) // second range ends within first range
+ return true;
+ else if (beg1 >= beg2 && beg1 <= end2) // first range starts within second range
+ return true;
+ else if (end1 >= beg2 && end1 <= end2) // first range ends within second range
+ return true;
+ else
+ return false;
+ }
+
+
+ Object::Object(TADDR addr)
+ : mAddress(addr), mMT(0), mSize(~0), mPointers(false), mMTData(0), mTypeName(0)
+ {
+ if ((mAddress & ~ALIGNCONST) != mAddress)
+ sos::Throw<Exception>("Object %p is misaligned.", mAddress);
+ }
+
+ Object::Object(TADDR addr, TADDR mt)
+ : mAddress(addr), mMT(mt & ~3), mSize(~0), mPointers(false), mMTData(0), mTypeName(0)
+ {
+ if ((mAddress & ~ALIGNCONST) != mAddress)
+ sos::Throw<Exception>("Object %p is misaligned.", mAddress);
+ }
+
+
+ Object::Object(const Object &rhs)
+ : mAddress(rhs.mAddress), mMT(rhs.mMT), mSize(rhs.mSize), mPointers(rhs.mPointers), mMTData(rhs.mMTData), mTypeName(rhs.mTypeName)
+ {
+ rhs.mMTData = 0;
+ rhs.mTypeName = 0;
+ }
+
+ const Object &Object::operator=(TADDR addr)
+ {
+ if (mMTData)
+ delete mMTData;
+
+ if (mTypeName)
+ delete mTypeName;
+
+ mAddress = addr;
+ mMT = 0;
+ mSize = ~0;
+ mMTData = 0;
+ mTypeName = 0;
+
+ return *this;
+ }
+
+ bool Object::TryGetHeader(ULONG &outHeader) const
+ {
+ struct ObjectHeader
+ {
+ #ifdef _WIN64
+ ULONG _alignpad;
+ #endif
+ ULONG SyncBlockValue; // the Index and the Bits
+ };
+
+ ObjectHeader header;
+
+ if (SUCCEEDED(rvCache->Read(TO_TADDR(GetAddress() - sizeof(ObjectHeader)), &header, sizeof(ObjectHeader), NULL)))
+ {
+ outHeader = header.SyncBlockValue;
+ return true;
+ }
+
+ return false;
+ }
+
+
+ ULONG Object::GetHeader() const
+ {
+ ULONG toReturn = 0;
+ if (!TryGetHeader(toReturn))
+ sos::Throw<DataRead>("Failed to get header for object %p.", GetAddress());
+
+ return toReturn;
+ }
+
+ TADDR Object::GetMT() const
+ {
+ if (mMT == NULL)
+ {
+ TADDR temp;
+ if (FAILED(MOVE(temp, mAddress)))
+ sos::Throw<DataRead>("Object %s has an invalid method table.", DMLListNearObj(mAddress));
+
+ if (temp == NULL)
+ sos::Throw<HeapCorruption>("Object %s has an invalid method table.", DMLListNearObj(mAddress));
+
+ mMT = temp & ~3;
+ }
+
+ return mMT;
+ }
+
+ TADDR Object::GetComponentMT() const
+ {
+ if (mMT != NULL && mMT != sos::MethodTable::GetArrayMT())
+ return NULL;
+
+ DacpObjectData objData;
+ if (FAILED(objData.Request(g_sos, TO_CDADDR(mAddress))))
+ sos::Throw<DataRead>("Failed to request object data for %s.", DMLListNearObj(mAddress));
+
+ if (mMT == NULL)
+ mMT = TO_TADDR(objData.MethodTable) & ~3;
+
+ return TO_TADDR(objData.ElementTypeHandle);
+ }
+
+ const WCHAR *Object::GetTypeName() const
+ {
+ if (mTypeName == NULL)
+ mTypeName = CreateMethodTableName(GetMT(), GetComponentMT());
+
+
+ if (mTypeName == NULL)
+ return W("<error>");
+
+ return mTypeName;
+ }
+
+ void Object::FillMTData() const
+ {
+ if (mMTData == NULL)
+ {
+ mMTData = new DacpMethodTableData;
+ if (FAILED(mMTData->Request(g_sos, GetMT())))
+ {
+ delete mMTData;
+ mMTData = NULL;
+ sos::Throw<DataRead>("Could not request method table data for object %p (MethodTable: %p).", mAddress, mMT);
+ }
+ }
+ }
+
+
+ void Object::CalculateSizeAndPointers() const
+ {
+ TADDR mt = GetMT();
+ MethodTableInfo* info = g_special_mtCache.Lookup((DWORD_PTR)mt);
+ if (!info->IsInitialized())
+ {
+ // this is the first time we see this method table, so we need to get the information
+ // from the target
+ FillMTData();
+
+ info->BaseSize = mMTData->BaseSize;
+ info->ComponentSize = mMTData->ComponentSize;
+ info->bContainsPointers = mMTData->bContainsPointers;
+ }
+
+ if (mSize == (size_t)~0)
+ {
+ mSize = info->BaseSize;
+ if (info->ComponentSize)
+ {
+ // this is an array, so the size has to include the size of the components. We read the number
+ // of components from the target and multiply by the component size to get the size.
+ mSize += info->ComponentSize * GetNumComponents(GetAddress());
+ }
+
+ // On x64 we do an optimization to save 4 bytes in almost every string we create.
+ #ifdef _WIN64
+ // Pad to min object size if necessary
+ if (mSize < min_obj_size)
+ mSize = min_obj_size;
+ #endif // _WIN64
+ }
+
+ mPointers = info->bContainsPointers != FALSE;
+ }
+
+ size_t Object::GetSize() const
+ {
+ if (mSize == (size_t)~0) // poison value
+ {
+ CalculateSizeAndPointers();
+ }
+
+ SOS_Assert(mSize != (size_t)~0);
+ return mSize;
+ }
+
+
+ bool Object::HasPointers() const
+ {
+ if (mSize == (size_t)~0)
+ CalculateSizeAndPointers();
+
+ SOS_Assert(mSize != (size_t)~0);
+ return mPointers;
+ }
+
+
+ bool Object::VerifyMemberFields(TADDR pMT, TADDR obj)
+ {
+ WORD numInstanceFields = 0;
+ return VerifyMemberFields(pMT, obj, numInstanceFields);
+ }
+
+
+ bool Object::VerifyMemberFields(TADDR pMT, TADDR obj, WORD &numInstanceFields)
+ {
+ DacpMethodTableData vMethTable;
+ if (FAILED(vMethTable.Request(g_sos, pMT)))
+ return false;
+
+ // Recursively verify the parent (this updates numInstanceFields)
+ if (vMethTable.ParentMethodTable)
+ {
+ if (!VerifyMemberFields(TO_TADDR(vMethTable.ParentMethodTable), obj, numInstanceFields))
+ return false;
+ }
+
+ DacpMethodTableFieldData vMethodTableFields;
+
+ // Verify all fields on the object.
+ CLRDATA_ADDRESS dwAddr = vMethodTableFields.FirstField;
+ DacpFieldDescData vFieldDesc;
+
+ while (numInstanceFields < vMethodTableFields.wNumInstanceFields)
+ {
+ CheckInterrupt();
+
+ if (FAILED(vFieldDesc.Request(g_sos, dwAddr)))
+ return false;
+
+ if (vFieldDesc.Type >= ELEMENT_TYPE_MAX)
+ return false;
+
+ dwAddr = vFieldDesc.NextField;
+
+ if (!vFieldDesc.bIsStatic)
+ {
+ numInstanceFields++;
+ TADDR dwTmp = TO_TADDR(obj + vFieldDesc.dwOffset + sizeof(BaseObject));
+ if (vFieldDesc.Type == ELEMENT_TYPE_CLASS)
+ {
+ // Is it a valid object?
+ if (FAILED(MOVE(dwTmp, dwTmp)))
+ return false;
+
+ if (dwTmp != NULL)
+ {
+ DacpObjectData objData;
+ if (FAILED(objData.Request(g_sos, TO_CDADDR(dwTmp))))
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ bool MethodTable::IsZombie(TADDR addr)
+ {
+ // Zombie objects are objects that reside in an unloaded AppDomain.
+ MethodTable mt = addr;
+ return _wcscmp(mt.GetName(), W("<Unloaded Type>")) == 0;
+ }
+
+ void MethodTable::Clear()
+ {
+ if (mName)
+ {
+ delete [] mName;
+ mName = NULL;
+ }
+ }
+
+ const WCHAR *MethodTable::GetName() const
+ {
+ if (mName == NULL)
+ mName = CreateMethodTableName(mMT);
+
+ if (mName == NULL)
+ return W("<error>");
+
+ return mName;
+ }
+
+ bool Object::IsValid(TADDR address, bool verifyFields)
+ {
+ DacpObjectData objectData;
+ if (FAILED(objectData.Request(g_sos, TO_CDADDR(address))))
+ return false;
+
+ if (verifyFields &&
+ objectData.MethodTable != g_special_usefulGlobals.FreeMethodTable &&
+ !MethodTable::IsZombie(TO_TADDR(objectData.MethodTable)))
+ {
+ return VerifyMemberFields(TO_TADDR(objectData.MethodTable), address);
+ }
+
+ return true;
+ }
+
+ bool Object::GetThinLock(ThinLockInfo &out) const
+ {
+ ULONG header = GetHeader();
+ if (header & (BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX | BIT_SBLK_SPIN_LOCK))
+ {
+ return false;
+ }
+
+ out.ThreadId = header & SBLK_MASK_LOCK_THREADID;
+ out.Recursion = (header & SBLK_MASK_LOCK_RECLEVEL) >> SBLK_RECLEVEL_SHIFT;
+
+ CLRDATA_ADDRESS threadPtr = NULL;
+ if (g_sos->GetThreadFromThinlockID(out.ThreadId, &threadPtr) != S_OK)
+ {
+ out.ThreadPtr = NULL;
+ }
+ else
+ {
+ out.ThreadPtr = TO_TADDR(threadPtr);
+ }
+
+ return out.ThreadId != 0 && out.ThreadPtr != NULL;
+ }
+
+ bool Object::GetStringData(__out_ecount(size) WCHAR *buffer, size_t size) const
+ {
+ SOS_Assert(IsString());
+ SOS_Assert(buffer);
+ SOS_Assert(size > 0);
+
+ return SUCCEEDED(g_sos->GetObjectStringData(mAddress, (ULONG32)size, buffer, NULL));
+ }
+
+ size_t Object::GetStringLength() const
+ {
+ SOS_Assert(IsString());
+
+ strobjInfo stInfo;
+ if (FAILED(MOVE(stInfo, mAddress)))
+ sos::Throw<DataRead>("Failed to read object data at %p.", mAddress);
+
+ // We get the method table for free here, if we don't have it already.
+ SOS_Assert((mMT == NULL) || (mMT == TO_TADDR(stInfo.methodTable)));
+ if (mMT == NULL)
+ mMT = TO_TADDR(stInfo.methodTable);
+
+ return (size_t)stInfo.m_StringLength;
+ }
+
+
+ RefIterator::RefIterator(TADDR obj, LinearReadCache *cache)
+ : mCache(cache), mGCDesc(0), mArrayOfVC(false), mDone(false), mBuffer(0), mCurrSeries(0),
+ i(0), mCount(0), mCurr(0), mStop(0), mObject(obj), mObjSize(0)
+ {
+ Init();
+ }
+
+ RefIterator::RefIterator(TADDR obj, CGCDesc *desc, bool arrayOfVC, LinearReadCache *cache)
+ : mCache(cache), mGCDesc(desc), mArrayOfVC(arrayOfVC), mDone(false), mBuffer(0), mCurrSeries(0),
+ i(0), mCount(0), mCurr(0), mStop(0), mObject(obj), mObjSize(0)
+ {
+ Init();
+ }
+
+ RefIterator::~RefIterator()
+ {
+ if (mBuffer)
+ delete [] mBuffer;
+ }
+
+ const RefIterator &RefIterator::operator++()
+ {
+ if (mDone)
+ Throw<Exception>("Attempt to move past the end of the iterator.");
+
+ if (!mArrayOfVC)
+ {
+ mCurr += sizeof(TADDR);
+ if (mCurr >= mStop)
+ {
+ mCurrSeries--;
+ if (mCurrSeries < mGCDesc->GetLowestSeries())
+ {
+ mDone = true;
+ }
+ else
+ {
+ mCurr = mObject + mCurrSeries->GetSeriesOffset();
+ mStop = mCurr + mCurrSeries->GetSeriesSize() + mObjSize;
+ }
+ }
+ }
+ else
+ {
+ mCurr += sizeof(TADDR);
+ if (mCurr >= mStop)
+ {
+ int i_last = i;
+ i--;
+
+ if (i == mCount)
+ i = 0;
+
+ mCurr += mCurrSeries->val_serie[i_last].skip;
+ mStop = mCurr + mCurrSeries->val_serie[i].nptrs * sizeof(TADDR);
+ }
+
+ if (mCurr >= mObject + mObjSize - plug_skew)
+ mDone = true;
+ }
+
+ return *this;
+ }
+
+ TADDR RefIterator::operator*() const
+ {
+ return ReadPointer(mCurr);
+ }
+
+ TADDR RefIterator::GetOffset() const
+ {
+ return mCurr - mObject;
+ }
+
+ void RefIterator::Init()
+ {
+ TADDR mt = ReadPointer(mObject);
+ BOOL bContainsPointers = FALSE;
+
+ if (!GetSizeEfficient(mObject, mt, FALSE, mObjSize, bContainsPointers))
+ Throw<DataRead>("Failed to get size of object.");
+
+ if (!bContainsPointers)
+ {
+ mDone = true;
+ return;
+ }
+
+ if (!mGCDesc)
+ {
+ int entries = 0;
+
+ if (FAILED(MOVE(entries, mt-sizeof(TADDR))))
+ Throw<DataRead>("Failed to request number of entries.");
+
+ // array of vc?
+ if (entries < 0)
+ {
+ entries = -entries;
+ mArrayOfVC = true;
+ }
+ else
+ {
+ mArrayOfVC = false;
+ }
+
+ size_t slots = 1 + entries * sizeof(CGCDescSeries)/sizeof(TADDR);
+
+ ArrayHolder<TADDR> buffer = new TADDR[slots];
+
+ ULONG fetched = 0;
+ CLRDATA_ADDRESS address = TO_CDADDR(mt - slots*sizeof(TADDR));
+ if (FAILED(g_ExtData->ReadVirtual(address, buffer, (ULONG)(slots*sizeof(TADDR)), &fetched)))
+ Throw<DataRead>("Failed to request GCDesc.");
+
+ mBuffer = buffer.Detach();
+ mGCDesc = (CGCDesc*)(mBuffer + slots);
+ }
+
+ mCurrSeries = mGCDesc->GetHighestSeries();
+
+ if (!mArrayOfVC)
+ {
+ mCurr = mObject + mCurrSeries->GetSeriesOffset();
+ mStop = mCurr + mCurrSeries->GetSeriesSize() + mObjSize;
+ }
+ else
+ {
+ i = 0;
+ mCurr = mObject + mCurrSeries->startoffset;
+ mStop = mCurr + mCurrSeries->val_serie[i].nptrs * sizeof(TADDR);
+ mCount = (int)mGCDesc->GetNumSeries();
+ }
+
+ if (mCurr == mStop)
+ operator++();
+ else if (mCurr >= mObject + mObjSize - plug_skew)
+ mDone = true;
+ }
+
+
+ const TADDR GCHeap::HeapStart = 0;
+ const TADDR GCHeap::HeapEnd = ~0;
+
+ ObjectIterator::ObjectIterator(const DacpGcHeapDetails *heap, int numHeaps, TADDR start, TADDR stop)
+ : bLarge(false), mCurrObj(0), mLastObj(0), mStart(start), mEnd(stop), mSegmentEnd(0), mHeaps(heap),
+ mNumHeaps(numHeaps), mCurrHeap(0)
+ {
+ mAllocInfo.Init();
+ SOS_Assert(numHeaps > 0);
+
+ TADDR segStart = TO_TADDR(mHeaps[0].generation_table[GetMaxGeneration()].start_segment);
+ if (FAILED(mSegment.Request(g_sos, segStart, mHeaps[0])))
+ sos::Throw<DataRead>("Could not request segment data at %p.", segStart);
+
+ mCurrObj = mStart < TO_TADDR(mSegment.mem) ? TO_TADDR(mSegment.mem) : mStart;
+ mSegmentEnd = (segStart == TO_TADDR(mHeaps[0].ephemeral_heap_segment)) ?
+ TO_TADDR(mHeaps[0].alloc_allocated) :
+ TO_TADDR(mSegment.allocated);
+
+ CheckSegmentRange();
+ }
+
+ bool ObjectIterator::NextSegment()
+ {
+ if (mCurrHeap >= mNumHeaps)
+ return false;
+
+ TADDR next = TO_TADDR(mSegment.next);
+ if (next == NULL)
+ {
+ if (bLarge)
+ {
+ mCurrHeap++;
+ if (mCurrHeap == mNumHeaps)
+ return false;
+
+ bLarge = false;
+ next = TO_TADDR(mHeaps[mCurrHeap].generation_table[GetMaxGeneration()].start_segment);
+ }
+ else
+ {
+ bLarge = true;
+ next = TO_TADDR(mHeaps[mCurrHeap].generation_table[GetMaxGeneration()+1].start_segment);
+ }
+ }
+
+ SOS_Assert(next != NULL);
+ if (FAILED(mSegment.Request(g_sos, next, mHeaps[mCurrHeap])))
+ sos::Throw<DataRead>("Failed to request segment data at %p.", next);
+
+ mLastObj = 0;
+ mCurrObj = mStart < TO_TADDR(mSegment.mem) ? TO_TADDR(mSegment.mem) : mStart;
+ mSegmentEnd = (next == TO_TADDR(mHeaps[mCurrHeap].ephemeral_heap_segment)) ?
+ TO_TADDR(mHeaps[mCurrHeap].alloc_allocated) :
+ TO_TADDR(mSegment.allocated);
+ return CheckSegmentRange();
+ }
+
+ bool ObjectIterator::CheckSegmentRange()
+ {
+ CheckInterrupt();
+
+ while (!MemOverlap(mStart, mEnd, TO_TADDR(mSegment.mem), mSegmentEnd))
+ if (!NextSegment())
+ return false;
+
+ // At this point we know that the current segment contains objects in
+ // the correct range. However, there's no telling if the user gave us
+ // a starting address that corresponds to an object. If mStart is a
+ // valid object, then we'll just start there. If it's not we'll need
+ // to walk the segment from the beginning to find the first aligned
+ // object on or after mStart.
+ if (mCurrObj == mStart && !Object::IsValid(mStart))
+ {
+ // It's possible mCurrObj will equal mStart after this. That's fine.
+ // It means that the starting object is corrupt (and we'll figure
+ // that when the user calls GetNext), or IsValid was wrong.
+ mLastObj = 0;
+ mCurrObj = TO_TADDR(mSegment.mem);
+ while (mCurrObj < mStart)
+ MoveToNextObject();
+ }
+
+ return true;
+ }
+
+
+
+ const Object &ObjectIterator::operator*() const
+ {
+ AssertSanity();
+ return mCurrObj;
+ }
+
+
+ const Object *ObjectIterator::operator->() const
+ {
+ AssertSanity();
+ return &mCurrObj;
+ }
+
+ //Object ObjectIterator::GetNext()
+ const ObjectIterator &ObjectIterator::operator++()
+ {
+ CheckInterrupt();
+
+ // Assert we aren't done walking the heap.
+ SOS_Assert(*this);
+ AssertSanity();
+
+ MoveToNextObject();
+ return *this;
+ }
+
+ void ObjectIterator::MoveToNextObjectCarefully()
+ {
+ CheckInterrupt();
+
+ SOS_Assert(*this);
+ AssertSanity();
+
+ // Move to NextObject won't generally throw unless it fails to request the
+ // MethodTable of the object. At which point we won't know how large the
+ // current object is, nor how to move past it. In this case we'll simply
+ // move to the next segment if possible to continue iterating from there.
+ try
+ {
+ MoveToNextObject();
+ }
+ catch(const sos::Exception &)
+ {
+ NextSegment();
+ }
+ }
+
+ void ObjectIterator::AssertSanity() const
+ {
+ // Assert that we are in a sane state. Function which call this assume two things:
+ // 1. That the current object is within the segment bounds.
+ // 2. That the current object is within the requested memory range.
+ SOS_Assert(mCurrObj >= TO_TADDR(mSegment.mem));
+ SOS_Assert(mCurrObj <= TO_TADDR(mSegmentEnd - Align(min_obj_size)));
+
+ SOS_Assert(mCurrObj >= mStart);
+ SOS_Assert(mCurrObj <= mEnd);
+ }
+
+ void ObjectIterator::MoveToNextObject()
+ {
+ // Object::GetSize can be unaligned, so we must align it ourselves.
+ size_t size = (bLarge ? AlignLarge(mCurrObj.GetSize()) : Align(mCurrObj.GetSize()));
+
+ mLastObj = mCurrObj;
+ mCurrObj = mCurrObj.GetAddress() + size;
+
+ if (!bLarge)
+ {
+ // Is this the end of an allocation context? We need to know this because there can be
+ // allocated memory at the end of an allocation context that doesn't yet contain any objects.
+ // This happens because we actually allocate a minimum amount of memory (the allocation quantum)
+ // whenever we need to get more memory. Typically, a single allocation request won't fill this
+ // block, so we'll fulfill subsequent requests out of the remainder of the block until it's
+ // depleted.
+ int i;
+ for (i = 0; i < mAllocInfo.num; i ++)
+ {
+ if (mCurrObj == TO_TADDR(mAllocInfo.array[i].alloc_ptr)) // end of objects in this context
+ {
+ // Set mCurrObj to point after the context (alloc_limit is the end of the allocation context).
+ mCurrObj = TO_TADDR(mAllocInfo.array[i].alloc_limit) + Align(min_obj_size);
+ break;
+ }
+ }
+
+ // We also need to look at the gen0 alloc context.
+ if (mCurrObj == TO_TADDR(mHeaps[mCurrHeap].generation_table[0].allocContextPtr))
+ mCurrObj = TO_TADDR(mHeaps[mCurrHeap].generation_table[0].allocContextLimit) + Align(min_obj_size);
+ }
+
+ if (mCurrObj > mEnd || mCurrObj >= mSegmentEnd)
+ NextSegment();
+ }
+
+ SyncBlkIterator::SyncBlkIterator()
+ : mCurr(1), mTotal(0)
+ {
+ // If DacpSyncBlockData::Request fails with the call "1", then it means
+ // there are no SyncBlocks in the process.
+ DacpSyncBlockData syncBlockData;
+ if (SUCCEEDED(syncBlockData.Request(g_sos, 1)))
+ mTotal = syncBlockData.SyncBlockCount;
+
+ mSyncBlk = mCurr;
+ }
+
+ GCHeap::GCHeap()
+ {
+ if (FAILED(mHeapData.Request(g_sos)))
+ sos::Throw<DataRead>("Failed to request GC heap data.");
+
+ if (mHeapData.bServerMode)
+ {
+ mNumHeaps = mHeapData.HeapCount;
+ DWORD dwAllocSize = 0;
+ if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), mNumHeaps, dwAllocSize))
+ sos::Throw<Exception>("Failed to get GCHeaps: Integer overflow.");
+
+ CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
+ if (FAILED(g_sos->GetGCHeapList(mNumHeaps, heapAddrs, NULL)))
+ sos::Throw<DataRead>("Failed to get GCHeaps.");
+
+ mHeaps = new DacpGcHeapDetails[mNumHeaps];
+
+ for (int i = 0; i < mNumHeaps; i++)
+ if (FAILED(mHeaps[i].Request(g_sos, heapAddrs[i])))
+ sos::Throw<DataRead>("Failed to get GC heap details at %p.", heapAddrs[i]);
+ }
+ else
+ {
+ mHeaps = new DacpGcHeapDetails[1];
+ mNumHeaps = 1;
+
+ if (FAILED(mHeaps[0].Request(g_sos)))
+ sos::Throw<DataRead>("Failed to request GC details data.");
+ }
+ }
+
+ ObjectIterator GCHeap::WalkHeap(TADDR start, TADDR stop) const
+ {
+ return ObjectIterator(mHeaps, mNumHeaps, start, stop);
+ }
+
+ bool GCHeap::AreGCStructuresValid() const
+ {
+ return mHeapData.bGcStructuresValid != FALSE;
+ }
+
+ // SyncBlk class
+ SyncBlk::SyncBlk()
+ : mIndex(0)
+ {
+ }
+
+ SyncBlk::SyncBlk(int index)
+ : mIndex(index)
+ {
+ Init();
+ }
+
+ const SyncBlk &SyncBlk::operator=(int index)
+ {
+ mIndex = index;
+ Init();
+
+ return *this;
+ }
+
+ void SyncBlk::Init()
+ {
+ if (FAILED(mData.Request(g_sos, mIndex)))
+ sos::Throw<DataRead>("Failed to request SyncBlk at index %d.", mIndex);
+ }
+
+ TADDR SyncBlk::GetAddress() const
+ {
+ SOS_Assert(mIndex);
+ return TO_TADDR(mData.SyncBlockPointer);
+ }
+
+ TADDR SyncBlk::GetObject() const
+ {
+ SOS_Assert(mIndex);
+ return TO_TADDR(mData.Object);
+ }
+
+ int SyncBlk::GetIndex() const
+ {
+ return mIndex;
+ }
+
+ bool SyncBlk::IsFree() const
+ {
+ SOS_Assert(mIndex);
+ return mData.bFree != FALSE;
+ }
+
+ unsigned int SyncBlk::GetMonitorHeldCount() const
+ {
+ SOS_Assert(mIndex);
+ return mData.MonitorHeld;
+ }
+
+ unsigned int SyncBlk::GetRecursion() const
+ {
+ SOS_Assert(mIndex);
+ return mData.Recursion;
+ }
+
+ DWORD SyncBlk::GetCOMFlags() const
+ {
+ SOS_Assert(mIndex);
+ #ifdef FEATURE_COMINTEROP
+ return mData.COMFlags;
+ #else
+ return 0;
+ #endif
+ }
+
+ unsigned int SyncBlk::GetAdditionalThreadCount() const
+ {
+ SOS_Assert(mIndex);
+ return mData.AdditionalThreadCount;
+ }
+
+ TADDR SyncBlk::GetHoldingThread() const
+ {
+ SOS_Assert(mIndex);
+ return TO_TADDR(mData.HoldingThread);
+ }
+
+ TADDR SyncBlk::GetAppDomain() const
+ {
+ SOS_Assert(mIndex);
+ return TO_TADDR(mData.appDomainPtr);
+ }
+
+ void BuildTypeWithExtraInfo(TADDR addr, unsigned int size, __inout_ecount(size) WCHAR *buffer)
+ {
+ try
+ {
+ sos::Object obj(addr);
+ TADDR mtAddr = obj.GetMT();
+ bool isArray = sos::MethodTable::IsArrayMT(mtAddr);
+ bool isString = obj.IsString();
+
+ sos::MethodTable mt(isArray ? obj.GetComponentMT() : mtAddr);
+
+ if (isArray)
+ {
+ swprintf_s(buffer, size, W("%s[]"), mt.GetName());
+ }
+ else if (isString)
+ {
+ WCHAR str[32];
+ obj.GetStringData(str, _countof(str));
+
+ _snwprintf_s(buffer, size, _TRUNCATE, W("%s: \"%s\""), mt.GetName(), str);
+ }
+ else
+ {
+ _snwprintf_s(buffer, size, _TRUNCATE, W("%s"), mt.GetName());
+ }
+ }
+ catch (const sos::Exception &e)
+ {
+ int len = MultiByteToWideChar(CP_ACP, 0, e.what(), -1, NULL, 0);
+
+ ArrayHolder<WCHAR> tmp = new WCHAR[len];
+ MultiByteToWideChar(CP_ACP, 0, e.what(), -1, (WCHAR*)tmp, len);
+
+ swprintf_s(buffer, size, W("<invalid object: '%s'>"), (WCHAR*)tmp);
+ }
+ }
+}
diff --git a/src/ToolBox/SOS/Strike/sos.def b/src/ToolBox/SOS/Strike/sos.def
new file mode 100644
index 0000000000..c8d08e7319
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/sos.def
@@ -0,0 +1,231 @@
+; 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.
+;
+LIBRARY STRIKE
+EXPORTS
+ AnalyzeOOM
+ analyzeoom=AnalyzeOOM
+ ao=AnalyzeOOM
+ ClrStack
+ clrstack=ClrStack
+ CLRStack=ClrStack
+ DumpArray
+ da=DumpArray
+ dumparray=DumpArray
+ DumpAssembly
+ dumpassembly=DumpAssembly
+ DumpClass
+ dumpclass=DumpClass
+ DumpDomain
+ dumpdomain=DumpDomain
+#ifdef TRACE_GC
+ DumpGCLog
+ dumpgclog=DumpGCLog
+ dlog=DumpGCLog
+#endif
+ DumpGCData
+ dumpgcdata=DumpGCData
+ dgc=DumpGCData
+ DumpGCConfigLog
+ dumpgcconfiglog=DumpGCConfigLog
+ dclog=DumpGCConfigLog
+ DumpHeap
+ dumpheap=DumpHeap
+ DumpIL
+ dumpil=DumpIL
+ DumpLog
+ dumplog=DumpLog
+ Dumplog=DumpLog
+ DumpMD
+ dumpmd=DumpMD
+ DumpModule
+ dumpmodule=DumpModule
+ DumpMT
+ dumpmt=DumpMT
+ DumpObj
+ do=DumpObj
+ dumpobj=DumpObj
+ DumpRuntimeTypes
+ dumpruntimetypes=DumpRuntimeTypes
+ Dumpruntimetypes=DumpRuntimeTypes
+ DumpSig
+ dumpsig=DumpSig
+ DumpSigElem
+ dumpsigelem=DumpSigElem
+ DumpStack
+ dumpstack=DumpStack
+ DumpStackObjects
+ dso=DumpStackObjects
+ dumpstackobjects=DumpStackObjects
+ DumpVC
+ dumpvc=DumpVC
+ EEHeap
+ eeheap=EEHeap
+ EEStack
+ eestack=EEStack
+ EHInfo
+ ehinfo=EHInfo
+ Ehinfo=EHInfo
+ FinalizeQueue
+ finalizequeue=FinalizeQueue
+ fq=FinalizeQueue
+ FindAppDomain
+ findappdomain=FindAppDomain
+ Findappdomain=FindAppDomain
+ FindRoots
+ findroots=FindRoots
+ GCHandles
+ gchandles=GCHandles
+ GCHeapStat
+ gcheapstat=GCHeapStat
+ GcHeapStat=GCHeapStat
+ heapstat=GCHeapStat
+ HeapStat=GCHeapStat
+ GCInfo
+ gcinfo=GCInfo
+ GCRoot
+ gcroot=GCRoot
+ GCWhere
+ gcwhere=GCWhere
+ GcWhere=GCWhere
+ Help
+ help=Help
+ HistClear
+ histclear=HistClear
+ HistInit
+ histinit=HistInit
+ HistObj
+ histobj=HistObj
+ HistObjFind
+ histobjfind=HistObjFind
+ hof=HistObjFind
+ HistRoot
+ histroot=HistRoot
+ HistStats
+ histstats=HistStats
+ IP2MD
+ ip2md=IP2MD
+ ListNearObj
+ listnearobj=ListNearObj
+ lno=ListNearObj
+ Name2EE
+ name2ee=Name2EE
+ ObjSize
+ objsize=ObjSize
+ PrintException
+ pe=PrintException
+ printexception=PrintException
+ Printexception=PrintException
+ SaveModule
+ savemodule=SaveModule
+ SOSFlush
+ sosflush=SOSFlush
+ StopOnException
+ soe=StopOnException
+ stoponexception=StopOnException
+ Stoponexception=StopOnException
+ SyncBlk
+ syncblk=SyncBlk
+ ThreadPool
+ threadpool=ThreadPool
+ tp=ThreadPool
+ Threads
+ t=Threads
+ threads=Threads
+ ThreadState
+ threadstate=ThreadState
+ Token2EE
+ token2ee=Token2EE
+ TraverseHeap
+ traverseheap=TraverseHeap
+ Traverseheap=TraverseHeap
+ u
+ U=u
+ VerifyHeap
+ verifyheap=VerifyHeap
+ Verifyheap=VerifyHeap
+ vh=VerifyHeap
+ VerifyObj
+ verifyobj=VerifyObj
+ vo=VerifyObj
+ VMMap
+ vmmap=VMMap
+ VMStat
+ vmstat=VMStat
+
+#ifdef FEATURE_COMINTEROP
+ COMState
+ comstate=COMState
+ RCWCleanupList
+ rcwcleanuplist=RCWCleanupList
+ Rcwcleanuplist=RCWCleanupList
+ DumpRCW
+ dumprcw=DumpRCW
+ Dumprcw=DumpRCW
+ DumpCCW
+ dumpccw=DumpCCW
+ Dumpccw=DumpCCW
+#endif
+
+#ifdef _DEBUG
+ DumpPermissionSet
+ dps=DumpPermissionSet
+ dumppermissionset=DumpPermissionSet
+ dbgout
+ filthint
+#endif
+
+#ifdef FEATURE_PAL
+ SetClrDebugDll
+ UnloadClrDebugDll
+#else
+ _EFN_GetManagedExcepStack
+ _EFN_GetManagedExcepStackW
+ _EFN_GetManagedObjectFieldInfo
+ _EFN_GetManagedObjectName
+ _EFN_StackTrace
+ bpmd
+ BPMD=bpmd
+ DebugExtensionInitialize
+ DebugExtensionNotify
+ DebugExtensionUninitialize
+ EEVersion
+ eeversion=EEVersion
+ GCHandleLeaks
+ gchandleleaks=GCHandleLeaks
+ Gchandleleaks=GCHandleLeaks
+ GCHandleleaks=GCHandleLeaks
+ HandleCLRN
+ MinidumpMode
+ minidumpmode=MinidumpMode
+ Minidumpmode=MinidumpMode
+ PathTo
+ pathto=PathTo
+ ProcInfo
+ procinfo=ProcInfo
+ VerifyStackTrace
+ WatsonBuckets
+#endif // !FEATURE_PAL
+
+
+#ifdef FEATURE_CORESYSTEM
+// Only documented for Apollo internal usage
+ Watch
+ watch=Watch
+ SuppressJitOptimization
+ suppressjitoptimization=SuppressJitOptimization
+ sjo=SuppressJitOptimization
+ SaveState
+ savestate=SaveState
+ StopOnCatch
+ stoponcatch=StopOnCatch
+
+// Undocumented commands - testing or very experimental
+ ExposeDML
+ exposeDML=ExposeDML
+ GetCodeTypeFlags
+ getCodeTypeFlags=GetCodeTypeFlags
+ TraceToCode
+ tracetocode=TraceToCode
+#endif
diff --git a/src/ToolBox/SOS/Strike/sos.h b/src/ToolBox/SOS/Strike/sos.h
new file mode 100644
index 0000000000..3778235964
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/sos.h
@@ -0,0 +1,792 @@
+// 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.
+
+#pragma once
+
+#include "strike.h"
+#include "util.h"
+
+#ifndef SOS_Assert
+#ifdef _DEBUG
+#define SOS_Assert(x) do { if (!(x)) sos::Throw<sos::Exception>("SOS Assert Failure: %s\n", #x); } while(0)
+#else
+#define SOS_Assert(x) (void)0
+#endif
+#endif
+
+#ifdef throw
+#undef throw
+#endif
+
+#ifdef try
+#undef try
+#endif
+
+#ifdef catch
+#undef catch
+#endif
+
+class LinearReadCache;
+class CGCDesc;
+class CGCDescSeries;
+
+namespace sos
+{
+ class GCHeap;
+
+ /* The base SOS Exception. Note that most commands should not attempt to be
+ * resilient to exceptions thrown by most functions here. Instead a top level
+ * try/catch at the beginning of the command which prints out the exception's
+ * message should be sufficient.
+ * Note you should not throw these directly, instead use the sos::Throw function.
+ */
+ class Exception
+ {
+ public:
+ Exception(const char *format, va_list args)
+ {
+ vsprintf_s(mMsg, _countof(mMsg), format, args);
+
+ va_end(args);
+ }
+
+ inline virtual ~Exception() {}
+
+ // from std::exception
+ virtual const char *what() const
+ {
+ return mMsg;
+ }
+
+ const char *GetMesssage() const
+ {
+ return mMsg;
+ }
+
+ protected:
+ char mMsg[1024];
+ };
+
+ /* Thrown when we could not read data we expected out of the target process.
+ * This can be due to heap corruption, or it could just be an invalid pointer.
+ */
+ class DataRead : public Exception
+ {
+ public:
+ DataRead(const char *format, va_list args)
+ : Exception(format, args)
+ {
+ }
+ };
+
+ /* This is thrown when we detect heap corruption in the process.
+ */
+ class HeapCorruption : public Exception
+ {
+ public:
+ HeapCorruption(const char *format, va_list args)
+ : Exception(format, args)
+ {
+ }
+ };
+
+ // Internal helper method. Use SOS_Throw macros instead.
+ template <class T>
+ void Throw(const char *format, ...)
+ {
+ va_list args;
+ va_start(args, format);
+
+ throw T(format, args);
+ }
+
+ /* Checks to see if the user hit control-c. Throws an exception to escape SOS
+ * if so.
+ */
+ inline void CheckInterrupt()
+ {
+ if (g_ExtControl->GetInterrupt() == S_OK)
+ Throw<Exception>("User interrupt.");
+ }
+
+ /* ThinLock struct. Use Object::GetThinLock to fill the struct.
+ */
+ struct ThinLockInfo
+ {
+ int ThreadId;
+ TADDR ThreadPtr;
+ int Recursion;
+
+ ThinLockInfo()
+ : ThreadId(0), ThreadPtr(0), Recursion(0)
+ {
+ }
+ };
+
+ /* The MethodTable for an Object. The general pattern should be:
+ * MethodTable mt = someObject.GetMT();
+ */
+ class MethodTable
+ {
+ public:
+ /* Returns whether an object is from an AppDomain that has been unloaded.
+ * If so, we cannot validate the object's members.
+ * Params:
+ * mt - The address of the MethodTable to test for.
+ */
+ static bool IsZombie(TADDR mt);
+
+ /* Returns the method table for arrays.
+ */
+ inline static TADDR GetArrayMT()
+ {
+ return TO_TADDR(g_special_usefulGlobals.ArrayMethodTable);
+ }
+
+ /* Returns the method table for String objects.
+ */
+ inline static TADDR GetStringMT()
+ {
+ return TO_TADDR(g_special_usefulGlobals.StringMethodTable);
+ }
+
+ /* Returns the method table for Free objects.
+ */
+ inline static TADDR GetFreeMT()
+ {
+ return TO_TADDR(g_special_usefulGlobals.FreeMethodTable);
+ }
+
+ /* Returns true if the given method table is that of a Free object.
+ */
+ inline static bool IsFreeMT(TADDR mt)
+ {
+ return GetFreeMT() == mt;
+ }
+
+ /* Returns true if the given method table is that of an Array.
+ */
+ inline static bool IsArrayMT(TADDR mt)
+ {
+ return GetArrayMT() == mt;
+ }
+
+ /* Returns true if the given method table is that of a System.String object.
+ */
+ inline static bool IsStringMT(TADDR mt)
+ {
+ return GetStringMT() == mt;
+ }
+
+ inline static bool IsValid(TADDR mt)
+ {
+ DacpMethodTableData data;
+ return data.Request(g_sos, TO_CDADDR(mt)) == S_OK;
+ }
+
+ public:
+ MethodTable(TADDR mt)
+ : mMT(mt), mName(0)
+ {
+ }
+
+ MethodTable(const MethodTable &mt)
+ : mMT(mt.mMT), mName(mt.mName)
+ {
+ // Acquire the calculated mName field. Since we are making a copy, we will likely use
+ // the copy instead of the original.
+ mt.mName = NULL;
+ }
+
+ const MethodTable &operator=(const MethodTable &mt)
+ {
+ Clear();
+
+ // Acquire the calculated mName field. Since we are making a copy, we will likely use
+ // the copy instead of the original.
+ mMT = mt.mMT;
+ mName = mt.mName;
+ mt.mName = NULL;
+
+ return *this;
+ }
+
+ ~MethodTable()
+ {
+ Clear();
+ }
+
+ /* Returns the class name of this MethodTable. The pointer returned is
+ * valid through the lifetime of the MethodTable object and should not be
+ * freed.
+ */
+ const WCHAR *GetName() const;
+
+ private:
+ void Clear();
+
+ private:
+ TADDR mMT;
+ mutable WCHAR *mName;
+ };
+
+ /* This represents an object on the GC heap in the target process. This class
+ * represents a single object, and is immutable after construction. All
+ * information about this class is lazily evaluated, so it is entirely possible
+ * to get exceptions when calling any member function. If this is a concern,
+ * call validate before attempting to call any other method on this object.
+ */
+ class Object
+ {
+ public:
+ /* Attempts to determine if the target address points to a valid object.
+ * Note that this is a heuristic based check, so false positives could
+ * be possible.
+ * Params:
+ * address - The address of the object to inspect.
+ * verifyFields - Whether or not to validate that the fields the object
+ * points to are also valid. (If the object contains a
+ * corrupted pointer, passing true to this parameter will
+ * cause IsValid to return false.) In general passing
+ * true will make IsValid return less false positives.
+ */
+ static bool IsValid(TADDR address, bool verifyFields=false);
+
+ static int GetStringDataOffset()
+ {
+#ifndef _TARGET_WIN64_
+ return 8;
+#else
+ return 0xc;
+#endif
+ }
+
+ public:
+ /* Constructor. Use Object(TADDR, TADDR) instead if you know the method table.
+ * Parameters:
+ * addr - an address to an object on the managed heap
+ * Throws:
+ * Exception - if addr is misaligned.
+ */
+ Object(TADDR addr);
+
+ /* Constructor. Use this constructor if you already know the method table for
+ * the object in question. This will save a read if the method table is needed.
+ * Parameters:
+ * addr - an address to an object on the managed heap
+ * Throws:
+ * Exception - if addr is misaligned.
+ */
+ Object(TADDR addr, TADDR mt);
+
+ Object(const Object &rhs);
+
+ inline ~Object()
+ {
+ if (mMTData)
+ delete mMTData;
+
+ if (mTypeName)
+ delete mTypeName;
+ }
+
+ const Object &operator=(TADDR addr);
+
+ // Comparison operators. These compare the underlying address of
+ // the object to the parameter.
+ inline bool operator<=(TADDR addr) { return mAddress <= addr; }
+ inline bool operator>=(TADDR addr) { return mAddress >= addr; }
+ inline bool operator<(TADDR addr) { return mAddress < addr; }
+ inline bool operator>(TADDR addr) { return mAddress > addr; }
+ inline bool operator==(TADDR addr) { return mAddress == addr; }
+
+ /* Returns the target address of the object this represents.
+ */
+ inline TADDR GetAddress() const
+ {
+ return mAddress;
+ }
+
+ /* Returns the target address of the object this represents.
+ */
+ inline operator TADDR() const
+ {
+ return GetAddress();
+ }
+
+ /* Returns the object header for this object.
+ * Throws:
+ * DataRead - we failed to read the object header.
+ */
+ ULONG GetHeader() const;
+
+ /* Gets the header for the current object, does not throw any exception.
+ * Params:
+ * outHeader - filled with the header if this function was successful.
+ * Returns:
+ * True if we successfully read the object header, false otherwise.
+ */
+ bool TryGetHeader(ULONG &outHeader) const;
+
+ /* Returns the method table of the object this represents.
+ * Throws:
+ * DataRead - If we failed to read the method table from the address.
+ * This is usually indicative of heap corruption.
+ * HeapCorruption - If we successfully read the target method table
+ * but it is invalid. (We do not do a very deep
+ * verification here.)
+ */
+ TADDR GetMT() const;
+
+ /* Returns the component method table of the object. For example, if
+ * this object is an array, the method table will be the general array
+ * MT. Calling this function tells you what type of objects can be
+ * placed in the array.
+ * Throws:
+ * DataRead - If we failed to read the method table from the address.
+ * This is usually indicative of heap corruption.
+ * HeapCorruption - If we successfully read the target method table
+ * but it is invalid. (We do not do a very deep
+ * verification here.)
+ */
+ TADDR GetComponentMT() const;
+
+ /* Returns the size of the object this represents. Note that this size
+ * may not be pointer aligned.
+ * Throws:
+ * DataRead - If we failed to read the method table data (which contains
+ * the size of the object).
+ */
+ size_t GetSize() const;
+
+ /* Returns true if this object contains pointers to other objects.
+ * Throws:
+ * DataRead - if we failed to read out of the object's method table.
+ */
+ bool HasPointers() const;
+
+ /* Gets the thinlock information for this object.
+ * Params:
+ * out - The ThinLockInfo to be filled.
+ * Returns:
+ * True if the object has a thinlock, false otherwise. If this function
+ * returns false, then out will be untouched.
+ * Throws:
+ * DataRead - If we could not read the object header from the object.
+ */
+ bool GetThinLock(ThinLockInfo &out) const;
+
+ /* Returns true if this object is a Free object (meaning it points to free
+ * space in the GC heap.
+ * Throws:
+ * The same as GetMT().
+ */
+ inline bool IsFree() const
+ {
+ return GetMT() == MethodTable::GetFreeMT();
+ }
+
+ /* Returns true if this object is a string.
+ * Throws:
+ * The same as GetMT().
+ */
+ inline bool IsString() const
+ {
+ return GetMT() == MethodTable::GetStringMT();
+ }
+
+ /* Returns the length of the String, if this is a string object. This
+ * function assumes that you have called IsString first to ensure that
+ * the object is indeed a string.
+ * Throws:
+ * DataRead if we could not read the contents of the object.
+ */
+ size_t GetStringLength() const;
+
+ /* Fills the given buffer with the contents of the String. This
+ * function assumes you have called IsString first to ensure that this
+ * object is actually a System.String. This function does not throw,
+ * but the results are undefined if this object is not a string.
+ * Params:
+ * buffer - The buffer to fill with the string contents.
+ * size - The total size of the buffer.
+ * Returns:
+ * True if the string data was successfully requested and placed in
+ * buffer, false otherwise.
+ */
+ bool GetStringData(__out_ecount(size) WCHAR *buffer, size_t size) const;
+
+ /* Returns the name of the type of this object. E.g. System.String.
+ * Throws:
+ * DataRead if we could not read the contents of the object.
+ * Returns:
+ * A string containing the type of the object.
+ */
+ const WCHAR *GetTypeName() const;
+
+ private:
+ void FillMTData() const;
+ void CalculateSizeAndPointers() const;
+ static bool VerifyMemberFields(TADDR pMT, TADDR obj);
+ static bool VerifyMemberFields(TADDR pMT, TADDR obj, WORD &numInstanceFields);
+
+ protected:
+ // Conceptually, this class is never modified after you pass in the the object address.
+ // That is, there can't be anything the user does to point this object to a different
+ // object after construction. Since we lazy evaluate *everything*, we must be able to
+ // modify these variables. Hence they are mutable.
+ TADDR mAddress;
+ mutable TADDR mMT;
+ mutable size_t mSize;
+ mutable bool mPointers;
+ mutable DacpMethodTableData *mMTData;
+ mutable WCHAR *mTypeName;
+ };
+
+ /* Enumerates all the GC references (objects) contained in an object. This uses the GCDesc
+ * map exactly as the GC does.
+ */
+ class RefIterator
+ {
+ public:
+ RefIterator(TADDR obj, LinearReadCache *cache = NULL);
+ RefIterator(TADDR obj, CGCDesc *desc, bool arrayOfVC, LinearReadCache *cache = NULL);
+ ~RefIterator();
+
+ /* Moves to the next reference in the object.
+ */
+ const RefIterator &operator++();
+
+ /* Returns the address of the current reference.
+ */
+ TADDR operator*() const;
+
+ /* Gets the offset into the object where the current reference comes from.
+ */
+ TADDR GetOffset() const;
+
+ /* Returns true if there are more objects in the iteration, false otherwise.
+ * Used as:
+ * if (itr)
+ * ...
+ */
+ inline operator void *() const
+ {
+ return (void*)!mDone;
+ }
+
+ private:
+ void Init();
+ inline TADDR ReadPointer(TADDR addr) const
+ {
+ if (mCache)
+ {
+ if (!mCache->Read(addr, &addr, false))
+ Throw<DataRead>("Could not read address %p.", addr);
+ }
+ else
+ {
+ MOVE(addr, addr);
+ }
+
+ return addr;
+ }
+
+ private:
+ LinearReadCache *mCache;
+ CGCDesc *mGCDesc;
+ bool mArrayOfVC, mDone;
+
+ TADDR *mBuffer;
+ CGCDescSeries *mCurrSeries;
+
+ int i, mCount;
+
+ TADDR mCurr, mStop, mObject;
+ size_t mObjSize;
+ };
+
+
+ /* The Iterator used to walk the managed objects on the GC heap.
+ * The general usage pattern for this class is:
+ * for (ObjectIterator itr = gcheap.WalkHeap(); itr; ++itr)
+ * itr->SomeObjectMethod();
+ */
+ class ObjectIterator
+ {
+ friend class GCHeap;
+ public:
+
+ /* Returns the next object in the GCHeap. Note that you must ensure
+ * that there are more objects to walk before calling this function by
+ * checking "if (iterator)". If this function throws an exception,
+ * the the iterator is invalid, and should no longer be used to walk
+ * the heap. This should generally only happen if we cannot read the
+ * MethodTable of the object to move to the next object.
+ * Throws:
+ * DataRead
+ */
+ const ObjectIterator &operator++();
+
+ /* Dereference operator. This allows you to take a reference to the
+ * current object. Note the lifetime of this reference is valid for
+ * either the lifetime of the iterator or until you call operator++,
+ * whichever is shorter. For example.
+ * void Foo(const Object &param);
+ * void Bar(const ObjectIterator &itr)
+ * {
+ * Foo(*itr);
+ * }
+ */
+ const Object &operator*() const;
+
+ /* Returns a pointer to the current Object to call members on it.
+ * The usage pattern for the iterator is to simply use operator->
+ * to call methods on the Object it points to without taking a
+ * direct reference to the underlying Object if at all possible.
+ */
+ const Object *operator->() const;
+
+ /* Returns false when the iterator has reached the end of the managed
+ * heap.
+ */
+ inline operator void *() const
+ {
+ return (void*)(SIZE_T)(mCurrHeap == mNumHeaps ? 0 : 1);
+ }
+
+ /* Do not use.
+ * TODO: Replace this functionality with int Object::GetGeneration().
+ */
+ bool IsCurrObjectOnLOH() const
+ {
+ SOS_Assert(*this);
+ return bLarge;
+ }
+
+ /* Verifies the current object. Returns true if the current object is valid.
+ * Returns false and fills 'buffer' with the reason the object is corrupted.
+ * This is a deeper validation than Object::IsValid as it checks the card
+ * table entires for the object in addition to the rest of the references.
+ * This function does not throw exceptions.
+ * Params:
+ * buffer - out buffer that is filled if and only if this function returns
+ * false.
+ * size - the total size of the buffer
+ * Returns:
+ * True if the object is valid, false otherwise.
+ */
+ bool Verify(__out_ecount(size) char *buffer, size_t size) const;
+
+ /* The same as Verify(char*, size_t), except it does not write out the failure
+ * reason to a provided buffer.
+ * See:
+ * ObjectIterator::Verify(char *, size_t)
+ */
+ bool Verify() const;
+
+ /* Attempts to move to the next object (similar to ObjectIterator++), but
+ * attempts to recover from any heap corruption by skipping to the next
+ * segment. If Verify returns false, meaning it detected heap corruption
+ * at the current object, you can use MoveToNextObjectCarefully instead of
+ * ObjectIterator++ to attempt to keep reading from the heap. If possible,
+ * this function attempts to move to the next object in the same segment,
+ * but if that's not possible then it skips to the next segment and
+ * continues from there.
+ * Note:
+ * This function can throw, and if it does then the iterator is no longer
+ * in a valid state. No further attempts to move to the next object will
+ * be possible.
+ * Throws:
+ * DataRead - if the heap is corrupted and it's not possible to continue
+ * walking the heap
+ */
+ void MoveToNextObjectCarefully();
+
+ private:
+ ObjectIterator(const DacpGcHeapDetails *heap, int numHeaps, TADDR start, TADDR stop);
+
+ bool VerifyObjectMembers(__out_ecount(size) char *buffer, size_t size) const;
+ void BuildError(__out_ecount(count) char *out, size_t count, const char *format, ...) const;
+
+ void AssertSanity() const;
+ bool NextSegment();
+ bool CheckSegmentRange();
+ void MoveToNextObject();
+
+ private:
+ DacpHeapSegmentData mSegment;
+ bool bLarge;
+ Object mCurrObj;
+ TADDR mLastObj, mStart, mEnd, mSegmentEnd;
+ AllocInfo mAllocInfo;
+ const DacpGcHeapDetails *mHeaps;
+ int mNumHeaps;
+ int mCurrHeap;
+ };
+
+ /* Reprensents an entry in the sync block table.
+ */
+ class SyncBlk
+ {
+ friend class SyncBlkIterator;
+ public:
+ /* Constructor.
+ * Params:
+ * index - the index of the syncblk entry you wish to inspect.
+ * This should be in range [1, MaxEntries], but in general
+ * you should always use the SyncBlk iterator off of GCHeap
+ * and not construct these directly.
+ * Throws:
+ * DataRead - if we could not read the syncblk entry for the given index.
+ */
+ explicit SyncBlk(int index);
+
+ /* Returns whether or not the current entry is a "Free" SyncBlk table entry
+ * or not. This should be called *before* any other function here.
+ */
+ bool IsFree() const;
+
+ /* Returns the address of this syncblk entry (generally for display purposes).
+ */
+ TADDR GetAddress() const;
+
+ /* Returns the address of the object which this is syncblk is pointing to.
+ */
+ TADDR GetObject() const;
+
+ /* Returns the index of this entry.
+ */
+ int GetIndex() const;
+
+ /* Returns the COMFlags for the SyncBlk object. The return value of this
+ * function is undefined if FEATURE_COMINTEROP is not defined, so you should
+ * #ifdef the calling region yourself.
+ */
+ DWORD GetCOMFlags() const;
+
+ unsigned int GetMonitorHeldCount() const;
+ unsigned int GetRecursion() const;
+ unsigned int GetAdditionalThreadCount() const;
+
+ /* Returns the thread which holds this monitor (this is the clr!Thread object).
+ */
+ TADDR GetHoldingThread() const;
+ TADDR GetAppDomain() const;
+
+ private:
+ /* Copy constructor unimplemented due to how expensive this is. Use references
+ * instead.
+ */
+ SyncBlk(const SyncBlk &rhs);
+ SyncBlk();
+ void Init();
+ const SyncBlk &operator=(int index);
+
+ private:
+ int mIndex;
+ DacpSyncBlockData mData;
+ };
+
+ /* An iterator over syncblks. The common usage for this class is:
+ * for (SyncBlkIterator itr; itr; ++itr)
+ * itr->SomeSyncBlkFunction();
+ */
+ class SyncBlkIterator
+ {
+ public:
+ SyncBlkIterator();
+
+ /* Moves to the next SyncBlk in the table.
+ */
+ inline const SyncBlkIterator &operator++()
+ {
+ SOS_Assert(mCurr <= mTotal);
+ mSyncBlk = ++mCurr;
+
+ return *this;
+ }
+
+ inline const SyncBlk &operator*() const
+ {
+ SOS_Assert(mCurr <= mTotal);
+ return mSyncBlk;
+ }
+
+ inline const SyncBlk *operator->() const
+ {
+ SOS_Assert(mCurr <= mTotal);
+ return &mSyncBlk;
+ }
+
+ inline operator void *() const
+ {
+ return (void*)(SIZE_T)(mCurr <= mTotal ? 1 : 0);
+ }
+
+ private:
+ int mCurr, mTotal;
+ SyncBlk mSyncBlk;
+ };
+
+ /* An class which contains information about the GCHeap.
+ */
+ class GCHeap
+ {
+ public:
+ static const TADDR HeapStart; // A constant signifying the start of the GC heap.
+ static const TADDR HeapEnd; // A constant signifying the end of the GC heap.
+
+ public:
+ /* Constructor.
+ * Throws:
+ * DataRead
+ */
+ GCHeap();
+
+ /* Returns an ObjectIterator which allows you to walk the objects on the managed heap.
+ * This ObjectIterator is valid for the duration of the GCHeap's lifetime. Note that
+ * if you specify an address at which you wish to start walking the heap it need
+ * not point directly to a managed object. However, if it does not, WalkHeap
+ * will need to walk the segment that address resides in to find the first object
+ * after that address, and if it encounters any heap corruption along the way,
+ * it may be impossible to walk the heap from the address specified.
+ *
+ * Params:
+ * start - The starting address at which you want to start walking the heap.
+ * This need not point directly to an object on the heap.
+ * end - The ending address at which you want to stop walking the heap. This
+ * need not point directly to an object on the heap.
+ * validate - Whether or not you wish to validate the GC heap as you walk it.
+ * Throws:
+ * DataRead
+ */
+ ObjectIterator WalkHeap(TADDR start = HeapStart, TADDR stop = HeapEnd) const;
+
+ /* Returns true if the GC Heap structures are in a valid state for traversal.
+ * Returns false if not (e.g. if we are in the middle of a relocation).
+ */
+ bool AreGCStructuresValid() const;
+
+ private:
+ DacpGcHeapDetails *mHeaps;
+ DacpGcHeapData mHeapData;
+ int mNumHeaps;
+ };
+
+ // convenience functions
+ /* A temporary wrapper function for Object::IsValid. There are too many locations
+ * in SOS which need to use IsObject but have a wide variety of internal
+ * representations for an object address. Until it can all be unified as TADDR,
+ * this is what they will use.
+ */
+ template <class T>
+ bool IsObject(T addr, bool verifyFields=false)
+ {
+ return Object::IsValid(TO_TADDR(addr), verifyFields);
+ }
+
+
+ void BuildTypeWithExtraInfo(TADDR addr, unsigned int size, __inout_ecount(size) WCHAR *buffer);
+}
diff --git a/src/ToolBox/SOS/Strike/sos.targets b/src/ToolBox/SOS/Strike/sos.targets
new file mode 100644
index 0000000000..3a8185c6da
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/sos.targets
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood">
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+ <Import Project="$(ClrBase)\src\debug\SetDebugTargetLocal.props" />
+ <Import Project="$(ClrBase)\src\debug\XPlatCommon.props" />
+
+ <!-- Handle cross platform debugging: SOS is different from the DAC -->
+ <!-- in that one binary includes code targeting multiple platforms. -->
+ <!-- This means that there may be more than one SosTarget msbuild -->
+ <!-- symbol or SOS_TARGET_XYZ conditional defined at the same time. -->
+ <!-- The next section is what determines what code is built in which-->
+ <!-- binary. -->
+ <PropertyGroup>
+ <!-- Code level conditional compilation symbols to support x-plat -->
+ <CDefines Condition="'$(BuildArchitecture)' == 'i386'">$(CDefines);SOS_TARGET_X86=1</CDefines>
+ <CDefines Condition="'$(BuildArchitecture)' == 'arm' or '$(BuildArchitecture)' == 'i386'">$(CDefines);SOS_TARGET_ARM=1</CDefines>
+ <CDefines Condition="'$(BuildArchitecture)' == 'amd64'">$(CDefines);SOS_TARGET_AMD64=1</CDefines>
+ <CDefines Condition="'$(BuildArchitecture)' == 'amd64' or '$(BuildArchitecture)' == 'arm64'">$(CDefines);SOS_TARGET_ARM64=1</CDefines>
+
+ <CDefines Condition="'$(BuildArchitecture)' == 'amd64' or '$(BuildArchitecture)' == 'arm64'">$(CDefines);_TARGET_WIN64_=1</CDefines>
+ </PropertyGroup>
+
+ <!--Leaf project Properties-->
+ <PropertyGroup>
+ <SosSourceDir>$(ClrBase)\src\toolbox\sos\strike</SosSourceDir>
+ <CompilerWarnings>
+ </CompilerWarnings>
+ <UserIncludes>
+ $(SosSourceDir)\inc;
+ $(UserIncludes);
+ $(Clrbase)\src\vm;
+ $(Clrbase)\src\inc;
+ $(Clrbase)\src\gcdump;
+ $(Clrbase)\src\Debug\shim;
+ $(Clrbase)\src\coreclr\hosts\inc
+ </UserIncludes>
+ <OutputName>SOS</OutputName>
+ <FileToMarkForSigning>$(BinariesDirectory)\$(OutputName).dll</FileToMarkForSigning>
+ <TargetType>DYNLINK</TargetType>
+ <LinkSubsystem>windows</LinkSubsystem>
+ <Nxcompat>true</Nxcompat>
+ <SynchronizePass2Drain>true</SynchronizePass2Drain>
+ <DllDef>$(IntermediateOutputDirectory)\SOS.def</DllDef>
+ <DllEntryPoint>_DllMainCRTStartup</DllEntryPoint>
+ <LinkNoLibraries>true</LinkNoLibraries>
+ <UseMsvcrt>false</UseMsvcrt>
+ <Ltcg>false</Ltcg>
+ <LinkUseCMT>true</LinkUseCMT>
+ <UseStl>true</UseStl>
+ <ClAdditionalOptions Condition="'$(DebugBuild)' == 'true'">$(ClAdditionalOptions) -DDEBUG -D_DEBUG</ClAdditionalOptions>
+ <ClAdditionalOptions Condition="!('$(DebugBuild)' == 'true')">$(ClAdditionalOptions) -DFAST=1</ClAdditionalOptions>
+ <ClOptimization Condition="!('$(DebugBuild)' == 'true')">MaxSpeed</ClOptimization>
+ <ClAdditionalOptions Condition="'$(FeatureCoreSystem)' == 'true'">$(ClAdditionalOptions) -DFEATURE_CORESYSTEM</ClAdditionalOptions>
+ <MscOptimizations Condition="!('$(DebugBuild)' == 'true')">
+ </MscOptimizations>
+ <ClAdditionalOptions>$(ClAdditionalOptions) -DSTRIKE -D_MT=1 -DORCAS=0 -DMDA_SUPPORTED -DDEBUGGING_SUPPORTED -DEnC_SUPPORTED -DPROFILING_SUPPORTED -DFEATURE_COMINTEROP -DFEATURE_COMINTEROP_UNMANAGED_ACTIVATION -DFEATURE_COMINTEROP_MANAGED_ACTIVATION -DFEATURE_COMINTEROP_APARTMENT_SUPPORT -DFEATURE_RWLOCK -DFEATURE_PREJIT -DFEATURE_STACK_PROBE -DFEATURE_SVR_GC -DFEATURE_CAS_POLICY -DFEATURE_CLICKONCE -DFEATURE_CRYPTO -DFEATURE_IMPERSONATION -DFEATURE_ISOSTORE -DFEATURE_MACL -DFEATURE_WATSON -DFEATURE_X509 -DFEATURE_X509_SECURESTRINGS -DFEATURE_COMINTEROP_REGISTRATION -DFEATURE_MIXEDMODE -DFEATURE_PERFMON -DFEATURE_REFLECTION_ONLY_LOAD -DFEATURE_FUSION -DFEATURE_SYNTHETIC_CULTURES -DFEATURE_SORT_TABLES -DFEATURE_CODEPAGES_FILE -DFEATURE_VALIDATOR -DFEATURE_WIN32_REGISTRY -DFEATURE_REMOTING -DFEATURE_SERIALIZATION -DFEATURE_ISYM_READER -DFEATURE_LOADER_OPTIMIZATION -DFEATURE_IPCMAN -DFEATURE_STRONGNAME_DELAY_SIGNING_ALLOWED -DFEATURE_MULTIMODULE_ASSEMBLIES -DFEATURE_METHOD_RENTAL -DFEATURE_APTCA -DFEATURE_USE_LCID -DFEATURE_BCL_FORMATTING -DENABLE_DOWNLEVEL_FOR_NLS -DFEATURE_INCLUDE_ALL_INTERFACES -DFEATURE_NONGENERIC_COLLECTIONS -DFEATURE_APPDOMAINMANAGER_INITOPTIONS -DFEATURE_COMPRESSEDSTACK -DFEATURE_PLS -DFEATURE_SYNCHRONIZATIONCONTEXT -DFEATURE_SYNCHRONIZATIONCONTEXT_WAIT -DUEF_CHAINING_SUPPORTED -DFEATURE_LEAK_CULTURE_INFO -DFEATURE_UEF_CHAINMANAGER -DFEATURE_CORRUPTING_EXCEPTIONS -DFEATURE_APPDOMAIN_RESOURCE_MONITORING -DFEATURE_EXCEPTION_NOTIFICATIONS -DFEATURE_LEGACY_THREADPOOL -DFEATURE_INTEROP_DEBUGGING</ClAdditionalOptions>
+ <ExceptionHandling>$(Fullcxxeh)</ExceptionHandling>
+ <!-- As part of X-plat DAC feature work we decided to put x86 SOS inside binaries\x86 subdirectory.
+ The pdb file will be places following the same subdir rule SymbolsDir\x86.
+ -->
+ <BinplaceRoot Condition="'$(BuildX86Sos)' == 'true'">$(BinariesDirectory)\x86</BinplaceRoot>
+ <BinplaceRoot Condition="'$(BuildX64Sos)' == 'true'">$(BinariesDirectory)\x64</BinplaceRoot>
+ <!-- Inhibit default behaviour of copying sos.pdb to Symbols.pri dir. -->
+ <BinplaceSymbols Condition="'$(BuildX86Sos)' == 'true' or '$(BuildX64Sos)' == 'true'">false</BinplaceSymbols>
+ <Verdir>$(InternalPath)\NDP\inc</Verdir>
+ <Bindir>$(BinariesDirectory)</Bindir>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <TargetLib Include="$(SdkLibPath)\kernel32.lib" />
+ <TargetLib Include="$(SdkLibPath)\user32.lib" />
+ <TargetLib Include="$(SdkLibPath)\ole32.lib" />
+ <TargetLib Include="$(SdkLibPath)\oleaut32.lib" />
+ <TargetLib Include="$(SdkLibPath)\dbghelp.lib" />
+ <TargetLib Include="$(SdkLibPath)\uuid.lib" />
+ <TargetLib Include="$(SdkLibPath)\version.lib" />
+ <TargetLib Include="$(SdkLibPath)\dbgeng.lib" />
+ <TargetLib Include="$(SdkLibPath)\advapi32.lib" />
+ <TargetLib Include="$(SdkLibPath)\psapi.lib" />
+
+ <TargetLib Condition="'$(DebugBuild)' == 'true'" Include="$(VCPublicLibPath)\libcmtd.lib" />
+ <TargetLib Condition="'$(DebugBuild)' != 'true'" Include="$(VCPublicLibPath)\libcmt.lib" />
+
+ <TargetLib Condition="'$(BuildX86Sos)' != 'true' and '$(BuildX64Sos)' != 'true'" Include="$(ClrLibPath)\corguids.lib">
+ <ProjectReference>$(ClrSrcDirectory)inc\corguids.nativeproj</ProjectReference>
+ </TargetLib>
+ <TargetLib Condition="'$(BuildX86Sos)' == 'true'" Include="$(ClrLibPath)\corguids_x86.lib">
+ <ProjectReference>$(ClrSrcDirectory)incx86\corguids.nativeproj</ProjectReference>
+ </TargetLib>
+ <TargetLib Condition="'$(BuildX64Sos)' == 'true'" Include="$(ClrLibPath)\corguids_amd64.lib">
+ <ProjectReference>$(ClrSrcDirectory)incamd64\corguids.nativeproj</ProjectReference>
+ </TargetLib>
+
+ <TargetLib Include="$(ClrLibPath)\debugshim$(XPlatHostLibSuffix).lib">
+ <ProjectReference>$(ClrSrcDirectory)\Debug\shim\$(XPlatHostLibBuildDir)\debugshim.nativeproj</ProjectReference>
+ </TargetLib>
+ <TargetLib Include="$(ClrLibPath)\dbgutil$(XPlatHostLibSuffix).lib">
+ <ProjectReference>$(ClrSrcDirectory)\Debug\dbgutil\$(XPlatHostLibBuildDir)\dbgutil.nativeproj</ProjectReference>
+ </TargetLib>
+ </ItemGroup>
+ <ItemGroup>
+ <TargetLib Include="$(SdkLibPath)\ntdll.lib" />
+ </ItemGroup>
+ <ItemGroup>
+ <RCResourceFile Condition="'$(FeatureCoreSystem)'!='true'" Include="$(SosSourceDir)\Native.rc" />
+ <RCResourceFile Condition="'$(FeatureCoreSystem)'=='true'" Include="$(SosSourceDir)\ApolloNative.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <CppCompile Include="$(SosSourceDir)\disasm.cpp" />
+ <CppCompile Include="$(SosSourceDir)\dllsext.cpp" />
+ <CppCompile Include="$(SosSourceDir)\eeheap.cpp" />
+ <CppCompile Include="$(SosSourceDir)\EventCallbacks.cpp" />
+ <CppCompile Include="$(SosSourceDir)\ExpressionNode.cpp" />
+ <CppCompile Include="$(SosSourceDir)\exts.cpp" />
+ <CppCompile Include="$(SosSourceDir)\gchist.cpp" />
+ <CppCompile Include="$(SosSourceDir)\gcroot.cpp" />
+ <CppCompile Include="$(SosSourceDir)\metadata.cpp" />
+ <CppCompile Include="$(SosSourceDir)\sildasm.cpp" />
+ <CppCompile Include="$(SosSourceDir)\sos.cpp" />
+ <CppCompile Include="$(SosSourceDir)\stressLogDump.cpp" />
+ <CppCompile Include="$(SosSourceDir)\strike.cpp" />
+ <CppCompile Include="$(SosSourceDir)\util.cpp" />
+ <CppCompile Include="$(SosSourceDir)\vm.cpp" />
+ <CppCompile Include="$(SosSourceDir)\WatchCmd.cpp" />
+ <CppPreprocess Include="$(SosSourceDir)\SOS.def">
+ <FinalOutput>$(IntermediateOutputDirectory)\SOS.def</FinalOutput>
+ <AdditionalOptions>$(ClAdditionalOptions) /TC</AdditionalOptions>
+ </CppPreprocess>
+ </ItemGroup>
+ <ItemGroup>
+ <CppCompile Condition="'$(BuildArchitecture)' == 'i386'" Include="$(SosSourceDir)\disasmX86.cpp" />
+ <CppCompile Condition="'$(BuildArchitecture)' == 'amd64'" Include="$(SosSourceDir)\disasmX86.cpp" />
+ <CppCompile Condition="'$(BuildArchitecture)' == 'arm' or '$(BuildArchitecture)' == 'i386'" Include="$(SosSourceDir)\disasmARM.cpp" />
+ <CppCompile Condition="'$(BuildArchitecture)' == 'arm64' or '$(BuildArchitecture)' == 'amd64'" Include="$(SosSourceDir)\disasmARM64.cpp" />
+ <RotorX86Sources Include="$(SosSourceDir)\disasmX86.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <DataFile Include="$(SosSourceDir)\sos_stacktrace.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <PublishPartGenerated Include="$(SosSourceDir)\sos_stacktrace.h">
+ <Visibility>Intra</Visibility>
+ <FileType>Include</FileType>
+ </PublishPartGenerated>
+ </ItemGroup>
+
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" />
+
+ <PropertyGroup>
+ <BuildLinkedDependsOn>$(BuildLinkedDependsOn);GenLongNameSOS</BuildLinkedDependsOn>
+ </PropertyGroup>
+
+ <UsingTask TaskName="GetFileVersionTask" AssemblyFile="$(ClrSrcDirectory)dlls\mscordac\GetFileVersion.dll"/>
+
+ <Target Name="GenLongNameSOS"
+ Inputs="$(Verdir)\version.h;$(IntermediateOutputDirectory)\sos.dll"
+ Outputs="sos_upd">
+ <GetFileVersionTask FilePath="$(IntermediateOutputDirectory)\sos.dll">
+ <Output TaskParameter="FileVersion" PropertyName="SOSFileVersion"/>
+ </GetFileVersionTask>
+ <Exec Command="$(PerlCommand) -I$(DevDivToolsPath) $(ClrSrcDirectory)dlls\mscordac\Update.pl $(IntermediateOutputDirectory)\sos.dll sos $(HostMachineArch) $(_EnvironmentMachineArch) $(SOSFileVersion) $(Bindir) echo" StandardOutputImportance="Normal" />
+
+ </Target>
+
+</Project>
diff --git a/src/ToolBox/SOS/Strike/sos_md.h b/src/ToolBox/SOS/Strike/sos_md.h
new file mode 100644
index 0000000000..f1a34cd706
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/sos_md.h
@@ -0,0 +1,926 @@
+// 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.
+
+// ==++==
+//
+
+//
+// ==--==
+
+#ifndef __SOS_MD_H__
+#define __SOS_MD_H__
+
+#define IfErrGoTo(s, label) { \
+ hresult = (s); \
+ if(FAILED(hresult)){ \
+ goto label; }}
+
+class CQuickBytes;
+
+// TODO: Cleanup code to allow SOS to directly include the metadata header files.
+//#include "MetaData.h"
+//#include "corpriv.h"
+
+/*
+ *
+ * Metadata definitions needed for PrettyPrint functions.
+ * The original definitions for the types and interfaces below exist in
+ * inc\MetaData.h and inc\CorPriv.h
+ * TODO:
+ * Cleanup code to allow SOS to directly include the metadata header files.
+ * Currently it's extremely difficult due to symbol redefinitions.
+ * Always keep the definitions below in sync with the originals.
+ * NOTES:
+ * Since SOS runs in a native debugger session, since it does not use EnC,
+ * and in roder to minimize the amount of duplication we changed the
+ * method definitions that deal with UTSemReadWrite* arguments to take
+ * void* arguments instead.
+ * Also, some of the interface methods take CQuickBytes as arguments.
+ * If these methods are ever used it becomes crucial to maintain binary
+ * compatibility b/w the CQuickBytes defined in SOS and the definition
+ * from the EE.
+ *
+ */
+typedef enum tagEnumType
+{
+ MDSimpleEnum = 0x0, // simple enumerator that doesn't allocate memory
+ MDDynamicArrayEnum = 0x2, // dynamic array that holds tokens
+ MDCustomEnum = 0x3, // Custom enumerator that doesnt work with the enum functions
+} EnumType;
+
+struct HENUMInternal
+{
+ DWORD m_tkKind; // kind of tables that the enum is holding the result
+ ULONG m_ulCount; // count of total entries holding by the enumerator
+
+ EnumType m_EnumType;
+
+ struct {
+ ULONG m_ulStart;
+ ULONG m_ulEnd;
+ ULONG m_ulCur;
+ } u;
+
+ // m_cursor will go away when we no longer support running EE with uncompressed
+ // format. WHEN WE REMOVE THIS, REMOVE ITS VESTIAGES FROM ZeroEnum as well
+ //
+ char m_cursor[32]; // cursor holding query result for read/write mode
+};
+
+typedef struct _MDDefaultValue
+{
+#if DBG_TARGET_BIGENDIAN
+ _MDDefaultValue(void)
+ {
+ m_bType = ELEMENT_TYPE_END;
+ }
+ ~_MDDefaultValue(void)
+ {
+ if (m_bType == ELEMENT_TYPE_STRING)
+ {
+ delete[] m_wzValue;
+ }
+ }
+#endif
+
+ // type of default value
+ BYTE m_bType; // CorElementType for the default value
+
+ // the default value
+ union
+ {
+ BOOL m_bValue; // ELEMENT_TYPE_BOOLEAN
+ CHAR m_cValue; // ELEMENT_TYPE_I1
+ BYTE m_byteValue; // ELEMENT_TYPE_UI1
+ SHORT m_sValue; // ELEMENT_TYPE_I2
+ USHORT m_usValue; // ELEMENT_TYPE_UI2
+ LONG m_lValue; // ELEMENT_TYPE_I4
+ ULONG m_ulValue; // ELEMENT_TYPE_UI4
+ LONGLONG m_llValue; // ELEMENT_TYPE_I8
+ ULONGLONG m_ullValue; // ELEMENT_TYPE_UI8
+ FLOAT m_fltValue; // ELEMENT_TYPE_R4
+ DOUBLE m_dblValue; // ELEMENT_TYPE_R8
+ LPCWSTR m_wzValue; // ELEMENT_TYPE_STRING
+ IUnknown *m_unkValue; // ELEMENT_TYPE_CLASS
+ };
+ ULONG m_cbSize; // default value size (for blob)
+
+} MDDefaultValue;
+
+typedef struct
+{
+ RID m_ridFieldCur; // indexing to the field table
+ RID m_ridFieldEnd; // end index to field table
+} MD_CLASS_LAYOUT;
+
+typedef struct
+{
+ USHORT usMajorVersion; // Major Version.
+ USHORT usMinorVersion; // Minor Version.
+ USHORT usBuildNumber; // Build Number.
+ USHORT usRevisionNumber; // Revision Number.
+ LPCSTR szLocale; // Locale.
+ DWORD *rProcessor; // Processor array.
+ ULONG ulProcessor; // [IN/OUT] Size of the processor array/Actual # of entries filled in.
+ OSINFO *rOS; // OSINFO array.
+ ULONG ulOS; // [IN/OUT]Size of the OSINFO array/Actual # of entries filled in.
+} AssemblyMetaDataInternal;
+
+typedef struct
+{
+ mdMethodDef m_memberdef;
+ DWORD m_dwSemantics;
+} ASSOCIATE_RECORD;
+
+typedef BOOL (*PSIGCOMPARE)(PCCOR_SIGNATURE, DWORD, PCCOR_SIGNATURE, DWORD, void*);
+
+EXTERN_GUID(IID_IMDInternalImport, 0xce0f34ed, 0xbbc6, 0x11d2, 0x94, 0x1e, 0x0, 0x0, 0xf8, 0x8, 0x34, 0x60);
+#undef INTERFACE
+#define INTERFACE IMDInternalImport
+DECLARE_INTERFACE_(IMDInternalImport, IUnknown)
+{
+ //*****************************************************************************
+ // return the count of entries of a given kind in a scope
+ // For example, pass in mdtMethodDef will tell you how many MethodDef
+ // contained in a scope
+ //*****************************************************************************
+ STDMETHOD_(ULONG, GetCountWithTokenKind)(// return hresult
+ DWORD tkKind) PURE; // [IN] pass in the kind of token.
+
+ //*****************************************************************************
+ // enumerator for typedef
+ //*****************************************************************************
+ STDMETHOD(EnumTypeDefInit)( // return hresult
+ HENUMInternal *phEnum) PURE; // [OUT] buffer to fill for enumerator data
+
+ STDMETHOD_(ULONG, EnumTypeDefGetCount)(
+ HENUMInternal *phEnum) PURE; // [IN] the enumerator to retrieve information
+
+ STDMETHOD_(void, EnumTypeDefReset)(
+ HENUMInternal *phEnum) PURE; // [IN] the enumerator to retrieve information
+
+ STDMETHOD_(bool, EnumTypeDefNext)( // return hresult
+ HENUMInternal *phEnum, // [IN] input enum
+ mdTypeDef *ptd) PURE; // [OUT] return token
+
+ STDMETHOD_(void, EnumTypeDefClose)(
+ HENUMInternal *phEnum) PURE; // [IN] the enumerator to retrieve information
+
+ //*****************************************************************************
+ // enumerator for MethodImpl
+ //*****************************************************************************
+ STDMETHOD(EnumMethodImplInit)( // return hresult
+ mdTypeDef td, // [IN] TypeDef over which to scope the enumeration.
+ HENUMInternal *phEnumBody, // [OUT] buffer to fill for enumerator data for MethodBody tokens.
+ HENUMInternal *phEnumDecl) PURE; // [OUT] buffer to fill for enumerator data for MethodDecl tokens.
+
+ STDMETHOD_(ULONG, EnumMethodImplGetCount)(
+ HENUMInternal *phEnumBody, // [IN] MethodBody enumerator.
+ HENUMInternal *phEnumDecl) PURE; // [IN] MethodDecl enumerator.
+
+ STDMETHOD_(void, EnumMethodImplReset)(
+ HENUMInternal *phEnumBody, // [IN] MethodBody enumerator.
+ HENUMInternal *phEnumDecl) PURE; // [IN] MethodDecl enumerator.
+
+ STDMETHOD(EnumMethodImplNext)( // return hresult (S_OK = TRUE, S_FALSE = FALSE or error code)
+ HENUMInternal *phEnumBody, // [IN] input enum for MethodBody
+ HENUMInternal *phEnumDecl, // [IN] input enum for MethodDecl
+ mdToken *ptkBody, // [OUT] return token for MethodBody
+ mdToken *ptkDecl) PURE; // [OUT] return token for MethodDecl
+
+ STDMETHOD_(void, EnumMethodImplClose)(
+ HENUMInternal *phEnumBody, // [IN] MethodBody enumerator.
+ HENUMInternal *phEnumDecl) PURE; // [IN] MethodDecl enumerator.
+
+ //*****************************************
+ // Enumerator helpers for memberdef, memberref, interfaceimp,
+ // event, property, exception, param
+ //*****************************************
+
+ STDMETHOD(EnumGlobalFunctionsInit)( // return hresult
+ HENUMInternal *phEnum) PURE; // [OUT] buffer to fill for enumerator data
+
+ STDMETHOD(EnumGlobalFieldsInit)( // return hresult
+ HENUMInternal *phEnum) PURE; // [OUT] buffer to fill for enumerator data
+
+ STDMETHOD(EnumInit)( // return S_FALSE if record not found
+ DWORD tkKind, // [IN] which table to work on
+ mdToken tkParent, // [IN] token to scope the search
+ HENUMInternal *phEnum) PURE; // [OUT] the enumerator to fill
+
+ STDMETHOD(EnumAllInit)( // return S_FALSE if record not found
+ DWORD tkKind, // [IN] which table to work on
+ HENUMInternal *phEnum) PURE; // [OUT] the enumerator to fill
+
+ STDMETHOD_(bool, EnumNext)(
+ HENUMInternal *phEnum, // [IN] the enumerator to retrieve information
+ mdToken *ptk) PURE; // [OUT] token to scope the search
+
+ STDMETHOD_(ULONG, EnumGetCount)(
+ HENUMInternal *phEnum) PURE; // [IN] the enumerator to retrieve information
+
+ STDMETHOD_(void, EnumReset)(
+ HENUMInternal *phEnum) PURE; // [IN] the enumerator to be reset
+
+ STDMETHOD_(void, EnumClose)(
+ HENUMInternal *phEnum) PURE; // [IN] the enumerator to be closed
+
+ //*****************************************
+ // Enumerator helpers for declsecurity.
+ //*****************************************
+ STDMETHOD(EnumPermissionSetsInit)( // return S_FALSE if record not found
+ mdToken tkParent, // [IN] token to scope the search
+ CorDeclSecurity Action, // [IN] Action to scope the search
+ HENUMInternal *phEnum) PURE; // [OUT] the enumerator to fill
+
+ //*****************************************
+ // Enumerator helpers for CustomAttribute
+ //*****************************************
+ STDMETHOD(EnumCustomAttributeByNameInit)(// return S_FALSE if record not found
+ mdToken tkParent, // [IN] token to scope the search
+ LPCSTR szName, // [IN] CustomAttribute's name to scope the search
+ HENUMInternal *phEnum) PURE; // [OUT] the enumerator to fill
+
+ //*****************************************
+ // Nagivator helper to navigate back to the parent token given a token.
+ // For example, given a memberdef token, it will return the containing typedef.
+ //
+ // the mapping is as following:
+ // ---given child type---------parent type
+ // mdMethodDef mdTypeDef
+ // mdFieldDef mdTypeDef
+ // mdInterfaceImpl mdTypeDef
+ // mdParam mdMethodDef
+ // mdProperty mdTypeDef
+ // mdEvent mdTypeDef
+ //
+ //*****************************************
+ STDMETHOD(GetParentToken)(
+ mdToken tkChild, // [IN] given child token
+ mdToken *ptkParent) PURE; // [OUT] returning parent
+
+ //*****************************************
+ // Custom value helpers
+ //*****************************************
+ STDMETHOD(GetCustomAttributeProps)( // S_OK or error.
+ mdCustomAttribute at, // [IN] The attribute.
+ mdToken *ptkType) PURE; // [OUT] Put attribute type here.
+
+ STDMETHOD(GetCustomAttributeAsBlob)(
+ mdCustomAttribute cv, // [IN] given custom value token
+ void const **ppBlob, // [OUT] return the pointer to internal blob
+ ULONG *pcbSize) PURE; // [OUT] return the size of the blob
+
+ // returned void in v1.0/v1.1
+ STDMETHOD (GetScopeProps)(
+ LPCSTR *pszName, // [OUT] scope name
+ GUID *pmvid) PURE; // [OUT] version id
+
+ // finding a particular method
+ STDMETHOD(FindMethodDef)(
+ mdTypeDef classdef, // [IN] given typedef
+ LPCSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of CLR signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ mdMethodDef *pmd) PURE; // [OUT] matching memberdef
+
+ // return a iSeq's param given a MethodDef
+ STDMETHOD(FindParamOfMethod)( // S_OK or error.
+ mdMethodDef md, // [IN] The owning method of the param.
+ ULONG iSeq, // [IN] The sequence # of the param.
+ mdParamDef *pparamdef) PURE; // [OUT] Put ParamDef token here.
+
+ //*****************************************
+ //
+ // GetName* functions
+ //
+ //*****************************************
+
+ // return the name and namespace of typedef
+ STDMETHOD(GetNameOfTypeDef)(
+ mdTypeDef classdef, // given classdef
+ LPCSTR *pszname, // return class name(unqualified)
+ LPCSTR *psznamespace) PURE; // return the name space name
+
+ STDMETHOD(GetIsDualOfTypeDef)(
+ mdTypeDef classdef, // [IN] given classdef.
+ ULONG *pDual) PURE; // [OUT] return dual flag here.
+
+ STDMETHOD(GetIfaceTypeOfTypeDef)(
+ mdTypeDef classdef, // [IN] given classdef.
+ ULONG *pIface) PURE; // [OUT] 0=dual, 1=vtable, 2=dispinterface
+
+ // get the name of either methoddef
+ STDMETHOD(GetNameOfMethodDef)( // return the name of the memberdef in UTF8
+ mdMethodDef md, // given memberdef
+ LPCSTR *pszName) PURE;
+
+ STDMETHOD(GetNameAndSigOfMethodDef)(
+ mdMethodDef methoddef, // [IN] given memberdef
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to a blob value of CLR signature
+ ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob
+ LPCSTR *pszName) PURE;
+
+ // return the name of a FieldDef
+ STDMETHOD(GetNameOfFieldDef)(
+ mdFieldDef fd, // given memberdef
+ LPCSTR *pszName) PURE;
+
+ // return the name of typeref
+ STDMETHOD(GetNameOfTypeRef)(
+ mdTypeRef classref, // [IN] given typeref
+ LPCSTR *psznamespace, // [OUT] return typeref name
+ LPCSTR *pszname) PURE; // [OUT] return typeref namespace
+
+ // return the resolutionscope of typeref
+ STDMETHOD(GetResolutionScopeOfTypeRef)(
+ mdTypeRef classref, // given classref
+ mdToken *ptkResolutionScope) PURE;
+
+ // Find the type token given the name.
+ STDMETHOD(FindTypeRefByName)(
+ LPCSTR szNamespace, // [IN] Namespace for the TypeRef.
+ LPCSTR szName, // [IN] Name of the TypeRef.
+ mdToken tkResolutionScope, // [IN] Resolution Scope fo the TypeRef.
+ mdTypeRef *ptk) PURE; // [OUT] TypeRef token returned.
+
+ // return the TypeDef properties
+ // returned void in v1.0/v1.1
+ STDMETHOD(GetTypeDefProps)(
+ mdTypeDef classdef, // given classdef
+ DWORD *pdwAttr, // return flags on class, tdPublic, tdAbstract
+ mdToken *ptkExtends) PURE; // [OUT] Put base class TypeDef/TypeRef here
+
+ // return the item's guid
+ STDMETHOD(GetItemGuid)(
+ mdToken tkObj, // [IN] given item.
+ CLSID *pGuid) PURE; // [out[ put guid here.
+
+ // Get enclosing class of the NestedClass.
+ STDMETHOD(GetNestedClassProps)( // S_OK or error
+ mdTypeDef tkNestedClass, // [IN] NestedClass token.
+ mdTypeDef *ptkEnclosingClass) PURE; // [OUT] EnclosingClass token.
+
+ // Get count of Nested classes given the enclosing class.
+ STDMETHOD(GetCountNestedClasses)( // return count of Nested classes.
+ mdTypeDef tkEnclosingClass, // Enclosing class.
+ ULONG *pcNestedClassesCount) PURE;
+
+ // Return array of Nested classes given the enclosing class.
+ STDMETHOD(GetNestedClasses)( // Return actual count.
+ mdTypeDef tkEnclosingClass, // [IN] Enclosing class.
+ mdTypeDef *rNestedClasses, // [OUT] Array of nested class tokens.
+ ULONG ulNestedClasses, // [IN] Size of array.
+ ULONG *pcNestedClasses) PURE;
+
+ // return the ModuleRef properties
+ // returned void in v1.0/v1.1
+ STDMETHOD(GetModuleRefProps)(
+ mdModuleRef mur, // [IN] moduleref token
+ LPCSTR *pszName) PURE; // [OUT] buffer to fill with the moduleref name
+
+ //*****************************************
+ //
+ // GetSig* functions
+ //
+ //*****************************************
+ STDMETHOD(GetSigOfMethodDef)(
+ mdMethodDef methoddef, // [IN] given memberdef
+ ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob
+ PCCOR_SIGNATURE *ppSig) PURE;
+
+ STDMETHOD(GetSigOfFieldDef)(
+ mdMethodDef methoddef, // [IN] given memberdef
+ ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob
+ PCCOR_SIGNATURE *ppSig) PURE;
+
+ STDMETHOD(GetSigFromToken)(
+ mdToken tk,
+ ULONG * pcbSig,
+ PCCOR_SIGNATURE * ppSig) PURE;
+
+
+
+ //*****************************************
+ // get method property
+ //*****************************************
+ STDMETHOD(GetMethodDefProps)(
+ mdMethodDef md, // The method for which to get props.
+ DWORD *pdwFlags) PURE;
+
+ //*****************************************
+ // return method implementation informaiton, like RVA and implflags
+ //*****************************************
+ // returned void in v1.0/v1.1
+ STDMETHOD(GetMethodImplProps)(
+ mdToken tk, // [IN] MethodDef
+ ULONG *pulCodeRVA, // [OUT] CodeRVA
+ DWORD *pdwImplFlags) PURE; // [OUT] Impl. Flags
+
+ //*****************************************
+ // return method implementation informaiton, like RVA and implflags
+ //*****************************************
+ STDMETHOD(GetFieldRVA)(
+ mdFieldDef fd, // [IN] fielddef
+ ULONG *pulCodeRVA) PURE; // [OUT] CodeRVA
+
+ //*****************************************
+ // get field property
+ //*****************************************
+ STDMETHOD(GetFieldDefProps)(
+ mdFieldDef fd, // [IN] given fielddef
+ DWORD *pdwFlags) PURE; // [OUT] return fdPublic, fdPrive, etc flags
+
+ //*****************************************************************************
+ // return default value of a token(could be paramdef, fielddef, or property
+ //*****************************************************************************
+ STDMETHOD(GetDefaultValue)(
+ mdToken tk, // [IN] given FieldDef, ParamDef, or Property
+ MDDefaultValue *pDefaultValue) PURE;// [OUT] default value to fill
+
+
+ //*****************************************
+ // get dispid of a MethodDef or a FieldDef
+ //*****************************************
+ STDMETHOD(GetDispIdOfMemberDef)( // return hresult
+ mdToken tk, // [IN] given methoddef or fielddef
+ ULONG *pDispid) PURE; // [OUT] Put the dispid here.
+
+ //*****************************************
+ // return TypeRef/TypeDef given an InterfaceImpl token
+ //*****************************************
+ STDMETHOD(GetTypeOfInterfaceImpl)( // return the TypeRef/typedef token for the interfaceimpl
+ mdInterfaceImpl iiImpl, // given a interfaceimpl
+ mdToken *ptkType) PURE;
+
+ //*****************************************
+ // look up function for TypeDef
+ //*****************************************
+ STDMETHOD(FindTypeDef)(
+ LPCSTR szNamespace, // [IN] Namespace for the TypeDef.
+ LPCSTR szName, // [IN] Name of the TypeDef.
+ mdToken tkEnclosingClass, // [IN] TypeRef/TypeDef Token for the enclosing class.
+ mdTypeDef *ptypedef) PURE; // [IN] return typedef
+
+ //*****************************************
+ // return name and sig of a memberref
+ //*****************************************
+ STDMETHOD(GetNameAndSigOfMemberRef)( // return name here
+ mdMemberRef memberref, // given memberref
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to a blob value of CLR signature
+ ULONG *pcbSigBlob, // [OUT] count of bytes in the signature blob
+ LPCSTR *pszName) PURE;
+
+ //*****************************************************************************
+ // Given memberref, return the parent. It can be TypeRef, ModuleRef, MethodDef
+ //*****************************************************************************
+ STDMETHOD(GetParentOfMemberRef)(
+ mdMemberRef memberref, // given memberref
+ mdToken *ptkParent) PURE; // return the parent token
+
+ STDMETHOD(GetParamDefProps)(
+ mdParamDef paramdef, // given a paramdef
+ USHORT *pusSequence, // [OUT] slot number for this parameter
+ DWORD *pdwAttr, // [OUT] flags
+ LPCSTR *pszName) PURE; // [OUT] return the name of the parameter
+
+ STDMETHOD(GetPropertyInfoForMethodDef)( // Result.
+ mdMethodDef md, // [IN] memberdef
+ mdProperty *ppd, // [OUT] put property token here
+ LPCSTR *pName, // [OUT] put pointer to name here
+ ULONG *pSemantic) PURE; // [OUT] put semantic here
+
+ //*****************************************
+ // class layout/sequence information
+ //*****************************************
+ STDMETHOD(GetClassPackSize)( // return error if class doesn't have packsize
+ mdTypeDef td, // [IN] give typedef
+ ULONG *pdwPackSize) PURE; // [OUT] 1, 2, 4, 8, or 16
+
+ STDMETHOD(GetClassTotalSize)( // return error if class doesn't have total size info
+ mdTypeDef td, // [IN] give typedef
+ ULONG *pdwClassSize) PURE; // [OUT] return the total size of the class
+
+ STDMETHOD(GetClassLayoutInit)(
+ mdTypeDef td, // [IN] give typedef
+ MD_CLASS_LAYOUT *pLayout) PURE; // [OUT] set up the status of query here
+
+ STDMETHOD(GetClassLayoutNext)(
+ MD_CLASS_LAYOUT *pLayout, // [IN|OUT] set up the status of query here
+ mdFieldDef *pfd, // [OUT] return the fielddef
+ ULONG *pulOffset) PURE; // [OUT] return the offset/ulSequence associate with it
+
+ //*****************************************
+ // marshal information of a field
+ //*****************************************
+ STDMETHOD(GetFieldMarshal)( // return error if no native type associate with the token
+ mdFieldDef fd, // [IN] given fielddef
+ PCCOR_SIGNATURE *pSigNativeType, // [OUT] the native type signature
+ ULONG *pcbNativeType) PURE; // [OUT] the count of bytes of *ppvNativeType
+
+
+ //*****************************************
+ // property APIs
+ //*****************************************
+ // find a property by name
+ STDMETHOD(FindProperty)(
+ mdTypeDef td, // [IN] given a typdef
+ LPCSTR szPropName, // [IN] property name
+ mdProperty *pProp) PURE; // [OUT] return property token
+
+ // returned void in v1.0/v1.1
+ STDMETHOD(GetPropertyProps)(
+ mdProperty prop, // [IN] property token
+ LPCSTR *szProperty, // [OUT] property name
+ DWORD *pdwPropFlags, // [OUT] property flags.
+ PCCOR_SIGNATURE *ppvSig, // [OUT] property type. pointing to meta data internal blob
+ ULONG *pcbSig) PURE; // [OUT] count of bytes in *ppvSig
+
+ //**********************************
+ // Event APIs
+ //**********************************
+ STDMETHOD(FindEvent)(
+ mdTypeDef td, // [IN] given a typdef
+ LPCSTR szEventName, // [IN] event name
+ mdEvent *pEvent) PURE; // [OUT] return event token
+
+ // returned void in v1.0/v1.1
+ STDMETHOD(GetEventProps)(
+ mdEvent ev, // [IN] event token
+ LPCSTR *pszEvent, // [OUT] Event name
+ DWORD *pdwEventFlags, // [OUT] Event flags.
+ mdToken *ptkEventType) PURE; // [OUT] EventType class
+
+
+ //**********************************
+ // find a particular associate of a property or an event
+ //**********************************
+ STDMETHOD(FindAssociate)(
+ mdToken evprop, // [IN] given a property or event token
+ DWORD associate, // [IN] given a associate semantics(setter, getter, testdefault, reset, AddOn, RemoveOn, Fire)
+ mdMethodDef *pmd) PURE; // [OUT] return method def token
+
+ // Note, void function in v1.0/v1.1
+ STDMETHOD(EnumAssociateInit)(
+ mdToken evprop, // [IN] given a property or an event token
+ HENUMInternal *phEnum) PURE; // [OUT] cursor to hold the query result
+
+ // returned void in v1.0/v1.1
+ STDMETHOD(GetAllAssociates)(
+ HENUMInternal *phEnum, // [IN] query result form GetPropertyAssociateCounts
+ ASSOCIATE_RECORD *pAssociateRec, // [OUT] struct to fill for output
+ ULONG cAssociateRec) PURE; // [IN] size of the buffer
+
+
+ //**********************************
+ // Get info about a PermissionSet.
+ //**********************************
+ // returned void in v1.0/v1.1
+ STDMETHOD(GetPermissionSetProps)(
+ mdPermission pm, // [IN] the permission token.
+ DWORD *pdwAction, // [OUT] CorDeclSecurity.
+ void const **ppvPermission, // [OUT] permission blob.
+ ULONG *pcbPermission) PURE; // [OUT] count of bytes of pvPermission.
+
+ //****************************************
+ // Get the String given the String token.
+ // Returns a pointer to the string, or NULL in case of error.
+ //****************************************
+ STDMETHOD(GetUserString)(
+ mdString stk, // [IN] the string token.
+ ULONG *pchString, // [OUT] count of characters in the string.
+ BOOL *pbIs80Plus, // [OUT] specifies where there are extended characters >= 0x80.
+ LPCWSTR *pwszUserString) PURE;
+
+ //*****************************************************************************
+ // p-invoke APIs.
+ //*****************************************************************************
+ STDMETHOD(GetPinvokeMap)(
+ mdToken tk, // [IN] FieldDef, MethodDef.
+ DWORD *pdwMappingFlags, // [OUT] Flags used for mapping.
+ LPCSTR *pszImportName, // [OUT] Import name.
+ mdModuleRef *pmrImportDLL) PURE; // [OUT] ModuleRef token for the target DLL.
+
+ //*****************************************************************************
+ // helpers to convert a text signature to a com format
+ //*****************************************************************************
+ STDMETHOD(ConvertTextSigToComSig)( // Return hresult.
+ BOOL fCreateTrIfNotFound, // [IN] create typeref if not found
+ LPCSTR pSignature, // [IN] class file format signature
+ CQuickBytes *pqbNewSig, // [OUT] place holder for CLR signature
+ ULONG *pcbCount) PURE; // [OUT] the result size of signature
+
+ //*****************************************************************************
+ // Assembly MetaData APIs.
+ //*****************************************************************************
+ // returned void in v1.0/v1.1
+ STDMETHOD(GetAssemblyProps)(
+ mdAssembly mda, // [IN] The Assembly for which to get the properties.
+ const void **ppbPublicKey, // [OUT] Pointer to the public key.
+ ULONG *pcbPublicKey, // [OUT] Count of bytes in the public key.
+ ULONG *pulHashAlgId, // [OUT] Hash Algorithm.
+ LPCSTR *pszName, // [OUT] Buffer to fill with name.
+ AssemblyMetaDataInternal *pMetaData,// [OUT] Assembly MetaData.
+ DWORD *pdwAssemblyFlags) PURE;// [OUT] Flags.
+
+ // returned void in v1.0/v1.1
+ STDMETHOD(GetAssemblyRefProps)(
+ mdAssemblyRef mdar, // [IN] The AssemblyRef for which to get the properties.
+ const void **ppbPublicKeyOrToken, // [OUT] Pointer to the public key or token.
+ ULONG *pcbPublicKeyOrToken, // [OUT] Count of bytes in the public key or token.
+ LPCSTR *pszName, // [OUT] Buffer to fill with name.
+ AssemblyMetaDataInternal *pMetaData,// [OUT] Assembly MetaData.
+ const void **ppbHashValue, // [OUT] Hash blob.
+ ULONG *pcbHashValue, // [OUT] Count of bytes in the hash blob.
+ DWORD *pdwAssemblyRefFlags) PURE; // [OUT] Flags.
+
+ // returned void in v1.0/v1.1
+ STDMETHOD(GetFileProps)(
+ mdFile mdf, // [IN] The File for which to get the properties.
+ LPCSTR *pszName, // [OUT] Buffer to fill with name.
+ const void **ppbHashValue, // [OUT] Pointer to the Hash Value Blob.
+ ULONG *pcbHashValue, // [OUT] Count of bytes in the Hash Value Blob.
+ DWORD *pdwFileFlags) PURE; // [OUT] Flags.
+
+ // returned void in v1.0/v1.1
+ STDMETHOD(GetExportedTypeProps)(
+ mdExportedType mdct, // [IN] The ExportedType for which to get the properties.
+ LPCSTR *pszNamespace, // [OUT] Namespace.
+ LPCSTR *pszName, // [OUT] Name.
+ mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef that provides the ExportedType.
+ mdTypeDef *ptkTypeDef, // [OUT] TypeDef token within the file.
+ DWORD *pdwExportedTypeFlags) PURE; // [OUT] Flags.
+
+ // returned void in v1.0/v1.1
+ STDMETHOD(GetManifestResourceProps)(
+ mdManifestResource mdmr, // [IN] The ManifestResource for which to get the properties.
+ LPCSTR *pszName, // [OUT] Buffer to fill with name.
+ mdToken *ptkImplementation, // [OUT] mdFile or mdAssemblyRef that provides the ExportedType.
+ DWORD *pdwOffset, // [OUT] Offset to the beginning of the resource within the file.
+ DWORD *pdwResourceFlags) PURE;// [OUT] Flags.
+
+ STDMETHOD(FindExportedTypeByName)( // S_OK or error
+ LPCSTR szNamespace, // [IN] Namespace of the ExportedType.
+ LPCSTR szName, // [IN] Name of the ExportedType.
+ mdExportedType tkEnclosingType, // [IN] ExportedType for the enclosing class.
+ mdExportedType *pmct) PURE; // [OUT] Put ExportedType token here.
+
+ STDMETHOD(FindManifestResourceByName)( // S_OK or error
+ LPCSTR szName, // [IN] Name of the ManifestResource.
+ mdManifestResource *pmmr) PURE; // [OUT] Put ManifestResource token here.
+
+ STDMETHOD(GetAssemblyFromScope)( // S_OK or error
+ mdAssembly *ptkAssembly) PURE; // [OUT] Put token here.
+
+ STDMETHOD(GetCustomAttributeByName)( // S_OK or error
+ mdToken tkObj, // [IN] Object with Custom Attribute.
+ LPCUTF8 szName, // [IN] Name of desired Custom Attribute.
+ const void **ppData, // [OUT] Put pointer to data here.
+ ULONG *pcbData) PURE; // [OUT] Put size of data here.
+
+ // Note: The return type of this method was void in v1
+ STDMETHOD(GetTypeSpecFromToken)( // S_OK or error.
+ mdTypeSpec typespec, // [IN] Signature token.
+ PCCOR_SIGNATURE *ppvSig, // [OUT] return pointer to token.
+ ULONG *pcbSig) PURE; // [OUT] return size of signature.
+
+ STDMETHOD(SetUserContextData)( // S_OK or E_NOTIMPL
+ IUnknown *pIUnk) PURE; // The user context.
+
+ STDMETHOD_(BOOL, IsValidToken)( // True or False.
+ mdToken tk) PURE; // [IN] Given token.
+
+ STDMETHOD(TranslateSigWithScope)(
+ IMDInternalImport *pAssemImport, // [IN] import assembly scope.
+ const void *pbHashValue, // [IN] hash value for the import assembly.
+ ULONG cbHashValue, // [IN] count of bytes in the hash value.
+ PCCOR_SIGNATURE pbSigBlob, // [IN] signature in the importing scope
+ ULONG cbSigBlob, // [IN] count of bytes of signature
+ IMetaDataAssemblyEmit *pAssemEmit, // [IN] assembly emit scope.
+ IMetaDataEmit *emit, // [IN] emit interface
+ CQuickBytes *pqkSigEmit, // [OUT] buffer to hold translated signature
+ ULONG *pcbSig) PURE; // [OUT] count of bytes in the translated signature
+
+ // since SOS does not need to call method below, change return value to IUnknown* (from IMetaModelCommon*)
+ STDMETHOD_(IUnknown*, GetMetaModelCommon)( // Return MetaModelCommon interface.
+ ) PURE;
+
+ STDMETHOD_(IUnknown *, GetCachedPublicInterface)(BOOL fWithLock) PURE; // return the cached public interface
+ STDMETHOD(SetCachedPublicInterface)(IUnknown *pUnk) PURE; // no return value
+ // since SOS does not use the next 2 methods replace UTSemReadWrite* with void* in the signature
+ STDMETHOD_(void*, GetReaderWriterLock)() PURE; // return the reader writer lock
+ STDMETHOD(SetReaderWriterLock)(void * pSem) PURE;
+
+ STDMETHOD_(mdModule, GetModuleFromScope)() PURE; // [OUT] Put mdModule token here.
+
+
+ //-----------------------------------------------------------------
+ // Additional custom methods
+
+ // finding a particular method
+ STDMETHOD(FindMethodDefUsingCompare)(
+ mdTypeDef classdef, // [IN] given typedef
+ LPCSTR szName, // [IN] member name
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob value of CLR signature
+ ULONG cbSigBlob, // [IN] count of bytes in the signature blob
+ PSIGCOMPARE pSignatureCompare, // [IN] Routine to compare signatures
+ void* pSignatureArgs, // [IN] Additional info to supply the compare function
+ mdMethodDef *pmd) PURE; // [OUT] matching memberdef
+
+ // Additional v2 methods.
+
+ //*****************************************
+ // return a field offset for a given field
+ //*****************************************
+ STDMETHOD(GetFieldOffset)(
+ mdFieldDef fd, // [IN] fielddef
+ ULONG *pulOffset) PURE; // [OUT] FieldOffset
+
+ STDMETHOD(GetMethodSpecProps)(
+ mdMethodSpec ms, // [IN] The method instantiation
+ mdToken *tkParent, // [OUT] MethodDef or MemberRef
+ PCCOR_SIGNATURE *ppvSigBlob, // [OUT] point to the blob value of meta data
+ ULONG *pcbSigBlob) PURE; // [OUT] actual size of signature blob
+
+ STDMETHOD(GetTableInfoWithIndex)(
+ ULONG index, // [IN] pass in the table index
+ void **pTable, // [OUT] pointer to table at index
+ void **pTableSize) PURE; // [OUT] size of table at index
+
+ STDMETHOD(ApplyEditAndContinue)(
+ void *pDeltaMD, // [IN] the delta metadata
+ ULONG cbDeltaMD, // [IN] length of pData
+ IMDInternalImport **ppv) PURE; // [OUT] the resulting metadata interface
+
+ //**********************************
+ // Generics APIs
+ //**********************************
+ STDMETHOD(GetGenericParamProps)( // S_OK or error.
+ mdGenericParam rd, // [IN] The type parameter
+ ULONG* pulSequence, // [OUT] Parameter sequence number
+ DWORD* pdwAttr, // [OUT] Type parameter flags (for future use)
+ mdToken *ptOwner, // [OUT] The owner (TypeDef or MethodDef)
+ DWORD *reserved, // [OUT] The kind (TypeDef/Ref/Spec, for future use)
+ LPCSTR *szName) PURE; // [OUT] The name
+
+ STDMETHOD(GetGenericParamConstraintProps)( // S_OK or error.
+ mdGenericParamConstraint rd, // [IN] The constraint token
+ mdGenericParam *ptGenericParam, // [OUT] GenericParam that is constrained
+ mdToken *ptkConstraintType) PURE; // [OUT] TypeDef/Ref/Spec constraint
+
+ //*****************************************************************************
+ // This function gets the "built for" version of a metadata scope.
+ // NOTE: if the scope has never been saved, it will not have a built-for
+ // version, and an empty string will be returned.
+ //*****************************************************************************
+ STDMETHOD(GetVersionString)( // S_OK or error.
+ LPCSTR *pVer) PURE; // [OUT] Put version string here.
+
+
+ STDMETHOD(SafeAndSlowEnumCustomAttributeByNameInit)(// return S_FALSE if record not found
+ mdToken tkParent, // [IN] token to scope the search
+ LPCSTR szName, // [IN] CustomAttribute's name to scope the search
+ HENUMInternal *phEnum) PURE; // [OUT] The enumerator
+
+ STDMETHOD(SafeAndSlowEnumCustomAttributeByNameNext)(// return S_FALSE if record not found
+ mdToken tkParent, // [IN] token to scope the search
+ LPCSTR szName, // [IN] CustomAttribute's name to scope the search
+ HENUMInternal *phEnum, // [IN] The enumerator
+ mdCustomAttribute *mdAttribute) PURE; // [OUT] The custom attribute that was found
+
+
+ STDMETHOD(GetTypeDefRefTokenInTypeSpec)(// return S_FALSE if enclosing type does not have a token
+ mdTypeSpec tkTypeSpec, // [IN] TypeSpec token to look at
+ mdToken *tkEnclosedToken) PURE; // [OUT] The enclosed type token
+
+#define MD_STREAM_VER_1X 0x10000
+#define MD_STREAM_VER_2_B1 0x10001
+#define MD_STREAM_VER_2 0x20000
+ STDMETHOD_(DWORD, GetMetadataStreamVersion)() PURE; //returns DWORD with major version of
+ // MD stream in senior word and minor version--in junior word
+
+ STDMETHOD(GetNameOfCustomAttribute)(// S_OK or error
+ mdCustomAttribute mdAttribute, // [IN] The Custom Attribute
+ LPCUTF8 *pszNamespace, // [OUT] Namespace of Custom Attribute.
+ LPCUTF8 *pszName) PURE; // [OUT] Name of Custom Attribute.
+
+ STDMETHOD(SetOptimizeAccessForSpeed)(// S_OK or error
+ BOOL fOptSpeed) PURE;
+
+ STDMETHOD(SetVerifiedByTrustedSource)(// S_OK or error
+ BOOL fVerified) PURE;
+
+}; // IMDInternalImport
+
+EXTERN_GUID(IID_IMetaDataHelper, 0xad93d71d, 0xe1f2, 0x11d1, 0x94, 0x9, 0x0, 0x0, 0xf8, 0x8, 0x34, 0x60);
+
+#undef INTERFACE
+#define INTERFACE IMetaDataHelper
+DECLARE_INTERFACE_(IMetaDataHelper, IUnknown)
+{
+ // helper functions
+ // This function is exposing the ability to translate signature from a given
+ // source scope to a given target scope.
+ //
+ STDMETHOD(TranslateSigWithScope)(
+ IMetaDataAssemblyImport *pAssemImport, // [IN] importing assembly interface
+ const void *pbHashValue, // [IN] Hash Blob for Assembly.
+ ULONG cbHashValue, // [IN] Count of bytes.
+ IMetaDataImport *import, // [IN] importing interface
+ PCCOR_SIGNATURE pbSigBlob, // [IN] signature in the importing scope
+ ULONG cbSigBlob, // [IN] count of bytes of signature
+ IMetaDataAssemblyEmit *pAssemEmit, // [IN] emit assembly interface
+ IMetaDataEmit *emit, // [IN] emit interface
+ PCOR_SIGNATURE pvTranslatedSig, // [OUT] buffer to hold translated signature
+ ULONG cbTranslatedSigMax,
+ ULONG *pcbTranslatedSig) PURE;// [OUT] count of bytes in the translated signature
+
+ STDMETHOD(GetMetadata)(
+ ULONG ulSelect, // [IN] Selector.
+ void **ppData) PURE; // [OUT] Put pointer to data here.
+
+ STDMETHOD_(IUnknown *, GetCachedInternalInterface)(BOOL fWithLock) PURE; // S_OK or error
+ STDMETHOD(SetCachedInternalInterface)(IUnknown * pUnk) PURE; // S_OK or error
+ // since SOS does not use the next 2 methods replace UTSemReadWrite* with void* in the signature
+ STDMETHOD_(void*, GetReaderWriterLock)() PURE; // return the reader writer lock
+ STDMETHOD(SetReaderWriterLock)(void * pSem) PURE;
+};
+
+/********************************************************************************/
+
+// Fine grained formatting flags used by the PrettyPrint APIs below.
+// Upto FormatStubInfo they mirror the values used by TypeString, after that
+// they're used to enable specifying differences between the ILDASM-style
+// output and the C#-like output prefered by the rest of SOS.
+typedef enum
+{
+ FormatBasic = 0x00000000, // Not a bitmask, simply the tersest flag settings possible
+ FormatNamespace = 0x00000001, // Include namespace and/or enclosing class names in type names
+ FormatFullInst = 0x00000002, // Include namespace and assembly in generic types (regardless of other flag settings)
+ FormatAssembly = 0x00000004, // Include assembly display name in type names
+ FormatSignature = 0x00000008, // Include signature in method names
+ FormatNoVersion = 0x00000010, // Suppress version and culture information in all assembly names
+ FormatDebug = 0x00000020, // For debug printing of types only
+ FormatAngleBrackets = 0x00000040, // Whether generic types are C<T> or C[T]
+ FormatStubInfo = 0x00000080, // Include stub info like {unbox-stub}
+ // following flags are not present in TypeString::FormatFlags
+ FormatSlashSep = 0x00000100, // Whether nested types are NS.C1/C2 or NS.C1+C2
+ FormatKwInNames = 0x00000200, // Whether "class" and "valuetype" appear in type names in certain instances
+ FormatCSharp = 0x0000004b, // Used to generate a C#-like string representation of the token
+ FormatILDasm = 0x000003ff, // Used to generate an ILDASM-style string representation of the token
+}
+PPFormatFlags;
+
+char* asString(CQuickBytes *out);
+
+PCCOR_SIGNATURE PrettyPrintType(
+ PCCOR_SIGNATURE typePtr, // type to convert,
+ CQuickBytes *out, // where to put the pretty printed string
+ IMDInternalImport *pIMDI, // ptr to IMDInternal class with ComSig
+ DWORD formatFlags = FormatILDasm);
+
+const char* PrettyPrintClass(
+ CQuickBytes *out, // where to put the pretty printed string
+ mdToken tk, // The class token to look up
+ IMDInternalImport *pIMDI, // ptr to IMDInternalImport class with ComSig
+ DWORD formatFlags = FormatILDasm);
+
+// We have a proliferation of functions that translate a (module/token) pair to
+// a string, but none of them were as complete as PrettyPrintClass. Most were
+// not handling generic instantiations appropriately. PrettyPrintClassFromToken
+// provides this missing functionality. If passed "FormatCSharp" it will generate
+// a name fitting the format used throughout SOS, with the exception of !dumpil
+// (due to its ILDASM ancestry).
+// TODO: Refactor the code in PrettyPrintClassFromToken, NameForTypeDef_s,
+// TODO: NameForToken_s, MDInfo::TypeDef/RefName
+void PrettyPrintClassFromToken(
+ TADDR moduleAddr, // the module containing the token
+ mdToken tok, // the class token to look up
+ __out_ecount(cbName) WCHAR* mdName, // where to put the pretty printed string
+ size_t cbName, // the capacity of the buffer
+ DWORD formatFlags = FormatCSharp); // the format flags for the types
+
+inline HRESULT GetMDInternalFromImport(IMetaDataImport* pIMDImport, IMDInternalImport **ppIMDI)
+{
+ HRESULT hresult = E_FAIL;
+ IUnknown *pUnk = NULL;
+ IMetaDataHelper *pIMDH = NULL;
+
+ IfErrGoTo(pIMDImport->QueryInterface(IID_IMetaDataHelper, (void**)&pIMDH), Cleanup);
+ pUnk = pIMDH->GetCachedInternalInterface(FALSE);
+ if (pUnk == NULL)
+ goto Cleanup;
+ IfErrGoTo(pUnk->QueryInterface(IID_IMDInternalImport, (void**)ppIMDI), Cleanup);
+
+Cleanup:
+ if (pUnk)
+ pUnk->Release();
+ if (pIMDH != NULL)
+ pIMDH->Release();
+
+ return hresult;
+}
+
+#endif
+
diff --git a/src/ToolBox/SOS/Strike/sos_stacktrace.h b/src/ToolBox/SOS/Strike/sos_stacktrace.h
new file mode 100644
index 0000000000..4aba4ea52c
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/sos_stacktrace.h
@@ -0,0 +1,174 @@
+// 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.
+
+// ==++==
+//
+
+//
+// ==--==
+/* ---------------------------------------------------------------------------
+
+ SOS_Stacktrace.h
+
+ API exported from SOS.DLL for retrieving managed stack traces.
+ This extension function is called through the Windows Debugger extension
+ interfaces (dbgeng.h).
+
+ Additional functions exported from SOS are documented here as well.
+
+Notes:
+
+HRESULT CALLBACK _EFN_StackTrace(
+ PDEBUG_CLIENT client,
+ WCHAR wszTextOut[],
+ UINT *puiTextLength,
+ LPVOID pTransitionContexts,
+ UINT *puiTransitionContextCount,
+ UINT uiSizeOfContext);
+
+uiSizeOfContext must be either sizeof(SimpleContext) or sizeof(CONTEXT) for the
+architecture (x86, IA64, x64).
+
+if wszTextOut is NULL and *puiTextLength is non-NULL, the function will return
+the necessary string length in *puiTextLength.
+
+If wszTextOut is non-NULL, the function will fill wszTextOut up to the point
+given by *puiTextLength, returning success if there was enough room in the
+buffer or E_OUTOFMEMORY if the buffer wasn't long enough.
+
+The transition portion of the function will be completely ignored if
+pTransitionContexts and puiTransitionContextCount are both NULL. Some callers
+would just like text output of the function names.
+
+If pTransitionContexts is NULL and puiTransitionContextCount is non NULL, the
+function will return the necessary number of context entries in
+*puiTransitionContextCount.
+
+If pTransitionContexts is non NULL, the function will treat it as an array of
+structures of length *puiTransitionContextCount. The structure size is given
+by uiSizeOfContext, and must be the size of SimpleContext or CONTEXT for the
+architecture.
+
+wszTextOut will be written in the following format:
+
+"<ModuleName>!<Function Name>[+<offset in hex>]
+...
+(TRANSITION)
+..."
+
+if the offset in hex is 0, no offset will be written (this matches KB output).
+
+If there is no managed code on the thread currently in context,
+SOS_E_NOMANAGEDCODE will be returned.
+ ------------------------------------------------------------------------ */
+#ifndef __STACKTRACE_H
+#define __STACKTRACE_H
+#include <windows.h>
+#include <winerror.h>
+
+#ifndef FACILITY_SOS
+#define FACILITY_SOS 0xa0
+#endif
+
+#ifndef EMAKEHR
+#define EMAKEHR(val) MAKE_HRESULT(SEVERITY_ERROR, FACILITY_SOS, val)
+#endif
+
+// Custom Error returns
+#define SOS_E_NOMANAGEDCODE EMAKEHR(0x1000) // No managed code on the stack
+
+// Flags
+//
+// Turn on SOS_STACKTRACE_SHOWADDRESSES to see EBP ESP in front of each
+// module!functionname line. By default this is off.
+#define SOS_STACKTRACE_SHOWADDRESSES 0x00000001
+
+struct StackTrace_SimpleContext
+{
+ ULONG64 StackOffset; // esp on x86
+ ULONG64 FrameOffset; // ebp
+ ULONG64 InstructionOffset; // eip
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+HRESULT CALLBACK _EFN_StackTrace(
+ PDEBUG_CLIENT client,
+ __out_ecount(*puiTextLength) WCHAR wszTextOut[],
+ size_t *puiTextLength,
+ LPVOID pTransitionContexts,
+ size_t *puiTransitionContextCount,
+ size_t uiSizeOfContext,
+ DWORD Flags);
+
+/* ---------------------------------------------------------------------------
+
+ Additional functions are exported from SOS, and are useful
+ for debugging tasks with managed object pointers.
+
+ ------------------------------------------------------------------------ */
+
+// _EFN_GetManagedExcepStack - given a managed exception object address, returns a string
+// version of the stack trace contained inside.
+//
+// StackObjAddr - a managed object pointer, must be derived from System.Exception
+// szStackString - the string returned (out parameter)
+// cbString - number of characters available in the string buffer.
+//
+// The output will be truncated of cbString is not long enough for the full stack trace.
+HRESULT _EFN_GetManagedExcepStack(
+ PDEBUG_CLIENT client,
+ ULONG64 StackObjAddr,
+ __out_ecount(cbString) PSTR szStackString,
+ ULONG cbString
+ );
+
+// _EFN_GetManagedExcepStackW - same as _EFN_GetManagedExcepStack, but returns
+// the stack as a wide string.
+HRESULT _EFN_GetManagedExcepStackW(
+ PDEBUG_CLIENT client,
+ ULONG64 StackObjAddr,
+ __out_ecount(cchString) PWSTR wszStackString,
+ ULONG cchString
+ );
+
+// _EFN_GetManagedObjectName - given a managed object pointer, return the type name
+//
+// objAddr - a managed object pointer
+// szName - a buffer to be filled with the full type name
+// cbName - the number of characters available in the buffer
+//
+HRESULT _EFN_GetManagedObjectName(
+ PDEBUG_CLIENT client,
+ ULONG64 objAddr,
+ __out_ecount(cbName) PSTR szName,
+ ULONG cbName
+ );
+
+// _EFN_GetManagedObjectFieldInfo - given an object pointer and a field name, returns
+// the offset to the field from the start of the object,
+// and the field's value.
+//
+// objAddr - a managed object pointer
+// szFieldName - the field name you are interested in
+// pValue - the field value is written here. This parameter can be NULL.
+// pOffset - the offset from objAddr to the field. This parameter can be NULL.
+//
+// At least one of pValue and pOffset must be non-NULL.
+HRESULT _EFN_GetManagedObjectFieldInfo(
+ PDEBUG_CLIENT client,
+ ULONG64 objAddr,
+ __out_ecount (mdNameLen) PSTR szFieldName,
+ PULONG64 pValue,
+ PULONG pOffset
+ );
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus : extern "C"
+
+#endif // __STACKTRACE_H
+
diff --git a/src/ToolBox/SOS/Strike/sos_unixexports.src b/src/ToolBox/SOS/Strike/sos_unixexports.src
new file mode 100644
index 0000000000..ed811b6572
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/sos_unixexports.src
@@ -0,0 +1,54 @@
+; 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.
+
+bpmd
+ClrStack
+DumpArray
+DumpAssembly
+DumpClass
+DumpDomain
+DumpGCData
+DumpHeap
+DumpIL
+DumpLog
+DumpMD
+DumpModule
+DumpMT
+DumpObj
+DumpRuntimeTypes
+DumpSig
+DumpSigElem
+DumpStack
+DumpStackObjects
+DumpVC
+EEHeap
+GCWhere
+EEStack
+EHInfo
+FindAppDomain
+GCInfo
+GCRoot
+Help
+HistClear
+HistInit
+HistObj
+HistObjFind
+HistRoot
+HistStats
+IP2MD
+Name2EE
+PrintException
+StopOnCatch
+Threads
+ThreadState
+Token2EE
+u
+VerifyHeap
+VerifyStackTrace
+
+_EFN_GetManagedExcepStack
+_EFN_GetManagedExcepStackW
+_EFN_GetManagedObjectFieldInfo
+_EFN_GetManagedObjectName
+_EFN_StackTrace
diff --git a/src/ToolBox/SOS/Strike/sosdocs.txt b/src/ToolBox/SOS/Strike/sosdocs.txt
new file mode 100644
index 0000000000..594fca1bc2
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/sosdocs.txt
@@ -0,0 +1,2572 @@
+-------------------------------------------------------------------------------
+NOTE: THIS FILE CONTAINS SOS DOCUMENTATION. THE FORMAT OF THE FILE IS:
+
+<optional comments>
+COMMAND: <cmd name, all lower case>
+<descriptive text of the command>
+\\ <these are two backslashes, immediately followed by a newline>
+
+<repeat the sequence above>
+
+The first command is "contents" which is the general help screen. The rest
+correspond to SOS command names. This file is embedded as a resource in the SOS
+binary. Be sure to list any new commands here.
+-------------------------------------------------------------------------------
+
+
+
+COMMAND: contents.
+SOS is a debugger extension DLL designed to aid in the debugging of managed
+programs. Functions are listed by category, then roughly in order of
+importance. Shortcut names for popular functions are listed in parenthesis.
+Type "!help <functionname>" for detailed info on that function.
+
+Object Inspection Examining code and stacks
+----------------------------- -----------------------------
+DumpObj (do) Threads
+DumpArray (da) ThreadState
+DumpStackObjects (dso) IP2MD
+DumpHeap U
+DumpVC DumpStack
+GCRoot EEStack
+ObjSize CLRStack
+FinalizeQueue GCInfo
+PrintException (pe) EHInfo
+TraverseHeap BPMD
+ COMState
+
+Examining CLR data structures Diagnostic Utilities
+----------------------------- -----------------------------
+DumpDomain VerifyHeap
+EEHeap VerifyObj
+Name2EE FindRoots
+SyncBlk HeapStat
+DumpMT GCWhere
+DumpClass ListNearObj (lno)
+DumpMD GCHandles
+Token2EE GCHandleLeaks
+EEVersion FinalizeQueue (fq)
+DumpModule FindAppDomain
+ThreadPool SaveModule
+DumpAssembly ProcInfo
+DumpSigElem StopOnException (soe)
+DumpRuntimeTypes DumpLog
+DumpSig VMMap
+RCWCleanupList VMStat
+DumpIL MinidumpMode
+DumpRCW AnalyzeOOM (ao)
+DumpCCW
+
+Examining the GC history Other
+----------------------------- -----------------------------
+HistInit FAQ
+HistRoot
+HistObj
+HistObjFind
+HistClear
+\\
+
+COMMAND: faq.
+>> Where can I get the right version of SOS for my build?
+
+If you are running version 1.1 or 2.0 of the CLR, SOS.DLL is installed in the
+same directory as the main CLR dll (CLR.DLL). Newer versions of the
+Windows Debugger provide a command to make it easy to load the right copy of
+SOS.DLL:
+
+ ".loadby sos clr"
+
+That will load the SOS extension DLL from the same place that CLR.DLL is
+loaded in the process. You shouldn't attempt to use a version of SOS.DLL that
+doesn't match the version of CLR.DLL. You can find the version of
+CLR.DLL by running
+
+ "lmvm clr"
+
+in the debugger. Note that if you are running CoreCLR (e.g. Silverlight)
+then you should replace "clr" with "coreclr".
+
+If you are using a dump file created on another machine, it is a little bit
+more complex. You need to make sure the mscordacwks.dll file that came with
+that install is on your symbol path, and you need to load the corresponding
+version of sos.dll (typing .load <full path to sos.dll> rather than using the
+.loadby shortcut). Within the Microsoft corpnet, we keep tagged versions
+of mscordacwks.dll, with names like mscordacwks_<architecture>_<version>.dll
+that the Windows Debugger can load. If you have the correct symbol path to the
+binaries for that version of the Runtime, the Windows Debugger will load the
+correct mscordacwks.dll file.
+
+>> I have a chicken and egg problem. I want to use SOS commands, but the CLR
+ isn't loaded yet. What can I do?
+
+In the debugger at startup you can type:
+
+ "sxe clrn"
+
+Let the program run, and it will stop with the notice
+
+ "CLR notification: module 'mscorlib' loaded"
+
+At this time you can use SOS commands. To turn off spurious notifications,
+type:
+
+ "sxd clrn"
+
+>> I got the following error message. Now what?
+
+ 0:000> .loadby sos clr
+ 0:000> !DumpStackObjects
+ Failed to find runtime DLL (clr.dll), 0x80004005
+ Extension commands need clr.dll in order to have something to do.
+ 0:000>
+
+This means that the CLR is not loaded yet, or has been unloaded. You need to
+wait until your managed program is running in order to use these commands. If
+you have just started the program a good way to do this is to type
+
+ bp clr!EEStartup "g @$ra"
+
+in the debugger, and let it run. After the function EEStartup is finished,
+there will be a minimal managed environment for executing SOS commands.
+
+>> I have a partial memory minidump, and !DumpObj doesn't work. Why?
+
+In order to run SOS commands, many CLR data structures need to be traversed.
+When creating a minidump without full memory, special functions are called at
+dump creation time to bring those structures into the minidump, and allow a
+minimum set of SOS debugging commands to work. At this time, those commands
+that can provide full or partial output are:
+
+CLRStack
+Threads
+Help
+PrintException
+EEVersion
+
+For a minidump created with this minimal set of functionality in mind, you
+will get an error message when running any other commands. A full memory dump
+(obtained with ".dump /ma <filename>" in the Windows Debugger) is often the
+best way to debug a managed program at this level.
+
+>> What other tools can I use to find my bug?
+
+Turn on Managed Debugging Assistants. These enable additional runtime diagnostics,
+particularly in the area of PInvoke/Interop. Adam Nathan has written some great
+information about that:
+
+http://blogs.msdn.com/adam_nathan/
+
+>> Does SOS support DML?
+
+Yes. SOS respects the .prefer_dml option in the debugger. If this setting is
+turned on, then SOS will output DML by default. Alternatively, you may leave
+it off and add /D to the beginning of a command to get DML based output for it.
+Not all SOS commands support DML output.
+
+\\
+
+COMMAND: stoponexception.
+!StopOnException [-derived]
+ [-create | -create2]
+ <Exception>
+ [<Pseudo-register number>]
+
+!StopOnException helps when you want the Windows Debugger to stop on a
+particular managed exception, say a System.OutOfMemoryException, but continue
+running if other exceptions are thrown. The command can be used in two ways:
+
+1) When you just want to stop on one particular CLR exception
+
+ At the debugger prompt, anytime after loading SOS, type:
+
+ !StopOnException -create System.OutOfMemoryException 1
+
+ The pseudo-register number (1) indicates that SOS can use register $t1 for
+ maintaining the breakpoint. The -create parameter allows SOS to go ahead
+ and set up the breakpoint as a first-chance exception. -create2 would set
+ it up as a 2nd-chance exception.
+
+2) When you need more complex logic for stopping on a CLR exception
+
+ !StopOnException can be used purely as a predicate in a larger expression.
+ If you type:
+
+ !StopOnException System.OutOfMemoryException 3
+
+ then register $t3 will be set to 1 if the last thrown exception on the
+ current thread is a System.OutOfMemoryException. Otherwise, $t3 will be set
+ to 0. Using the Windows Debugger scripting language, you could chain
+ such calls together to stop on various exception types. You'll have to
+ manually create such predicates, for example:
+
+ sxe -c "!soe System.OutOfMemoryException 3;
+ !soe -derived System.IOException 4;
+ .if(@$t3==1 || @$t4==1) { .echo 'stop' } .else {g}"
+
+The -derived option will cause StopOnException to set the pseudo-register to
+1 even if the thrown exception type doesn't exactly match the exception type
+given, but merely derives from it. So, "-derived System.Exception" would catch
+every exception in the System.Exception heirarchy.
+
+The pseudo-register number is optional. If you don't pass a number, SOS will
+use pseudo-register $t1.
+
+Note that !PrintException with no parameters will print out the last thrown
+exception on the current thread (if any). You can use !soe as a shortcut for
+!StopOnException.
+\\
+
+COMMAND: minidumpmode.
+!MinidumpMode <0 or 1>
+
+Minidumps created with ".dump /m" or ".dump" have a very small set of
+CLR-specific data, just enough to run a subset of SOS commands correctly. You
+are able to run other SOS commands, but they may fail with unexpected errors
+because required areas of memory are not mapped in or only partially mapped
+in. At this time, SOS cannot reliably detect if a dump file is of this type
+(for one thing, custom dump commands can map in additional memory, but there
+is no facility to read meta-information about this memory). You can turn this
+option on to protect against running unsafe commands against small minidumps.
+
+By default, MinidumpMode is 0, so there is no restriction on commands that will
+run against a minidump.
+\\
+
+COMMAND: dumpobj.
+!DumpObj [-nofields] <object address>
+
+This command allows you to examine the fields of an object, as well as learn
+important properties of the object such as the EEClass, the MethodTable, and
+the size.
+
+You might find an object pointer by running !DumpStackObjects and choosing
+from the resultant list. Here is a simple object:
+
+ 0:000> !DumpObj a79d40
+ Name: Customer
+ MethodTable: 009038ec
+ EEClass: 03ee1b84
+ Size: 20(0x14) bytes
+ (C:\pub\unittest.exe)
+ Fields:
+ MT Field Offset Type VT Attr Value Name
+ 009038ec 4000008 4 Customer 0 instance 00a79ce4 name
+ 009038ec 4000009 8 Bank 0 instance 00a79d2c bank
+
+Note that fields of type Customer and Bank are themselves objects, and you can
+run !DumpObj on them too. You could look at the field directly in memory using
+the offset given. "dd a79d40+8 l1" would allow you to look at the bank field
+directly. Be careful about using this to set memory breakpoints, since objects
+can move around in the garbage collected heap.
+
+What else can you do with an object? You might run !GCRoot, to determine what
+roots are keeping it alive. Or you can find all objects of that type with
+"!DumpHeap -type Customer".
+
+The column VT contains the value 1 if the field is a valuetype structure, and
+0 if the field contains a pointer to another object. For valuetypes, you can
+take the MethodTable pointer in the MT column, and the Value and pass them to
+the command !DumpVC.
+
+The abbreviation !do can be used for brevity.
+
+The arguments in detail:
+-nofields: do not print fields of the object, useful for objects like
+ String
+\\
+
+COMMAND: dumparray.
+!DumpArray
+ [-start <startIndex>]
+ [-length <length>]
+ [-details]
+ [-nofields]
+ <array object address>
+
+This command allows you to examine elements of an array object.
+The arguments in detail:
+ -start <startIndex>: optional, only supported for single dimension array.
+ Specify from which index the command shows the elements.
+ -length <length>: optional, only supported for single dimension array.
+ Specify how many elements to show.
+ -details: optional. Ask the command to print out details
+ of the element using !DumpObj and !DumpVC format.
+ -nofields: optional, only takes effect when -details is used. Do
+ not print fields of the elements. Useful for arrays of
+ objects like String
+
+ Example output:
+
+ 0:000> !dumparray -start 2 -length 3 -details 00ad28d0
+ Name: Value[]
+ MethodTable: 03e41044
+ EEClass: 03e40fc0
+ Size: 132(0x84) bytes
+ Array: Rank 1, Number of elements 10, Type VALUETYPE
+ Element Type: Value
+ [2] 00ad28f0
+ Name: Value
+ MethodTable 03e40f4c
+ EEClass: 03ef1698
+ Size: 20(0x14) bytes
+ (C:\bugs\225271\arraytest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 5b9a628c 4000001 0 System.Int32 instance 2 x
+ 5b9a628c 4000002 4 System.Int32 instance 4 y
+ 5b9a628c 4000003 8 System.Int32 instance 6 z
+ [3] 00ad28fc
+ Name: Value
+ MethodTable 03e40f4c
+ EEClass: 03ef1698
+ Size: 20(0x14) bytes
+ (C:\bugs\225271\arraytest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 5b9a628c 4000001 0 System.Int32 instance 3 x
+ 5b9a628c 4000002 4 System.Int32 instance 6 y
+ 5b9a628c 4000003 8 System.Int32 instance 9 z
+ [4] 00ad2908
+ Name: Value
+ MethodTable 03e40f4c
+ EEClass: 03ef1698
+ Size: 20(0x14) bytes
+ (C:\bugs\225271\arraytest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 5b9a628c 4000001 0 System.Int32 instance 4 x
+ 5b9a628c 4000002 4 System.Int32 instance 8 y
+ 5b9a628c 4000003 8 System.Int32 instance 12 z
+
+
+\\
+
+COMMAND: dumpstackobjects.
+!DumpStackObjects [-verify] [top stack [bottom stack]]
+
+This command will display any managed objects it finds within the bounds of
+the current stack. Combined with the stack tracing commands like K and
+!CLRStack, it is a good aid to determining the values of locals and
+parameters.
+
+If you use the -verify option, each non-static CLASS field of an object
+candidate is validated. This helps to eliminate false positives. It is not
+on by default because very often in a debugging scenario, you are
+interested in objects with invalid fields.
+
+The abbreviation !dso can be used for brevity.
+\\
+
+COMMAND: dumpheap.
+!DumpHeap [-stat]
+ [-strings]
+ [-short]
+ [-min <size>]
+ [-max <size>]
+ [-live]
+ [-dead]
+ [-thinlock]
+ [-startAtLowerBound]
+ [-mt <MethodTable address>]
+ [-type <partial type name>]
+ [start [end]]
+
+!DumpHeap is a powerful command that traverses the garbage collected heap,
+collection statistics about objects. With it's various options, it can look for
+particular types, restrict to a range, or look for ThinLocks (see !SyncBlk
+documentation). Finally, it will provide a warning if it detects excessive
+fragmentation in the GC heap.
+
+When called without options, the output is first a list of objects in the heap,
+followed by a report listing all the types found, their size and number:
+
+ 0:000> !dumpheap
+ Address MT Size
+ 00a71000 0015cde8 12 Free
+ 00a7100c 0015cde8 12 Free
+ 00a71018 0015cde8 12 Free
+ 00a71024 5ba58328 68
+ 00a71068 5ba58380 68
+ 00a710ac 5ba58430 68
+ 00a710f0 5ba5dba4 68
+ ...
+ total 619 objects
+ Statistics:
+ MT Count TotalSize Class Name
+ 5ba7607c 1 12 System.Security.Permissions.HostProtectionResource
+ 5ba75d54 1 12 System.Security.Permissions.SecurityPermissionFlag
+ 5ba61f18 1 12 System.Collections.CaseInsensitiveComparer
+ ...
+ 0015cde8 6 10260 Free
+ 5ba57bf8 318 18136 System.String
+ ...
+
+"Free" objects are simply regions of space the garbage collector can use later.
+If 30% or more of the heap contains "Free" objects, the process may suffer from
+heap fragmentation. This is usually caused by pinning objects for a long time
+combined with a high rate of allocation. Here is example output where !DumpHeap
+provides a warning about fragmentation:
+
+ <After the Statistics section>
+ Fragmented blocks larger than 1MB:
+ Addr Size Followed by
+ 00a780c0 1.5MB 00bec800 System.Byte[]
+ 00da4e38 1.2MB 00ed2c00 System.Byte[]
+ 00f16df0 1.2MB 01044338 System.Byte[]
+
+The arguments in detail:
+
+-stat Restrict the output to the statistical type summary
+-strings Restrict the output to a statistical string value summary
+-short Limits output to just the address of each object. This allows you
+ to easily pipe output from the command to another debugger
+ command for automation.
+-min Ignore objects less than the size given in bytes
+-max Ignore objects larger than the size given in bytes
+-live Only print live objects
+-dead Only print dead objects (objects which will be collected in the
+ next full GC)
+-thinlock Report on any ThinLocks (an efficient locking scheme, see !SyncBlk
+ documentation for more info)
+-startAtLowerBound
+ Force heap walk to begin at lower bound of a supplied address range.
+ (During plan phase, the heap is often not walkable because objects
+ are being moved. In this case, DumpHeap may report spurious errors,
+ in particular bad objects. It may be possible to traverse more of
+ the heap after the reported bad object. Even if you specify an
+ address range, !DumpHeap will start its walk from the beginning of
+ the heap by default. If it finds a bad object before the specified
+ range, it will stop before displaying the part of the heap in which
+ you are interested. This switch will force !DumpHeap to begin its
+ walk at the specified lower bound. You must supply the address of a
+ good object as the lower bound for this to work. Display memory at
+ the address of the bad object to manually find the next method
+ table (use !dumpmt to verify). If the GC is currently in a call to
+ memcopy, You may also be able to find the next object's address by
+ adding the size to the start address given as parameters.)
+-mt List only those objects with the MethodTable given
+-type List only those objects whose type name is a substring match of the
+ string provided.
+start Begin listing from this address
+end Stop listing at this address
+
+A special note about -type: Often, you'd like to find not only Strings, but
+System.Object arrays that are constrained to contain Strings. ("new
+String[100]" actually creates a System.Object array, but it can only hold
+System.String object pointers). You can use -type in a special way to find
+these arrays. Just pass "-type System.String[]" and those Object arrays will
+be returned. More generally, "-type <Substring of interesting type>[]".
+
+The start/end parameters can be obtained from the output of !EEHeap -gc. For
+example, if you only want to list objects in the large heap segment:
+
+ 0:000> !eeheap -gc
+ Number of GC Heaps: 1
+ generation 0 starts at 0x00c32754
+ generation 1 starts at 0x00c32748
+ generation 2 starts at 0x00a71000
+ segment begin allocated size
+ 00a70000 00a71000 010443a8 005d33a8(6108072)
+ Large object heap starts at 0x01a71000
+ segment begin allocated size
+ 01a70000 01a71000 01a75000 0x00004000(16384)
+ Total Size 0x5d73a8(6124456)
+ ------------------------------
+ GC Heap Size 0x5d73a8(6124456)
+
+ 0:000> !dumpheap 1a71000 1a75000
+ Address MT Size
+ 01a71000 5ba88bd8 2064
+ 01a71810 0019fe48 2032 Free
+ 01a72000 5ba88bd8 4096
+ 01a73000 0019fe48 4096 Free
+ 01a74000 5ba88bd8 4096
+ total 5 objects
+ Statistics:
+ MT Count TotalSize Class Name
+ 0019fe48 2 6128 Free
+ 5ba88bd8 3 10256 System.Object[]
+ Total 5 objects
+
+Finally, if GC heap corruption is present, you may see an error like this:
+
+ 0:000> !dumpheap -stat
+ object 00a73d24: does not have valid MT
+ curr_object : 00a73d24
+ Last good object: 00a73d14
+ ----------------
+
+That indicates a serious problem. See the help for !VerifyHeap for more
+information on diagnosing the cause.
+\\
+
+COMMAND: dumpvc.
+!DumpVC <MethodTable address> <Address>
+
+!DumpVC allows you to examine the fields of a value class. In C#, this is a
+struct, and lives on the stack or within an Object on the GC heap. You need
+to know the MethodTable address to tell SOS how to interpret the fields, as
+a value class is not a first-class object with it's own MethodTable as the
+first field. For example:
+
+ 0:000> !DumpObj a79d98
+ Name: Mainy
+ MethodTable: 009032d8
+ EEClass: 03ee1424
+ Size: 28(0x1c) bytes
+ (C:\pub\unittest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 0090320c 4000010 4 VALUETYPE instance 00a79d9c m_valuetype
+ 009032d8 400000f 4 CLASS static 00a79d54 m_sExcep
+
+m_valuetype is a value type. The value in the MT column (0090320c) is the
+MethodTable for it, and the Value column provides the start address:
+
+ 0:000> !DumpVC 0090320c 00a79d9c
+ Name: Funny
+ MethodTable 0090320c
+ EEClass: 03ee14b8
+ Size: 28(0x1c) bytes
+ (C:\pub\unittest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 0090320c 4000001 0 CLASS instance 00a743d8 signature
+ 0090320c 4000002 8 System.Int32 instance 2345 m1
+ 0090320c 4000003 10 System.Boolean instance 1 b1
+ 0090320c 4000004 c System.Int32 instance 1234 m2
+ 0090320c 4000005 4 CLASS instance 00a79d98 backpointer
+
+!DumpVC is quite a specialized function. Some managed programs make heavy use
+of value classes, while others do not.
+\\
+
+COMMAND: gcroot.
+!GCRoot [-nostacks] <Object address>
+
+!GCRoot looks for references (or roots) to an object. These can exist in four
+places:
+
+ 1. On the stack
+ 2. Within a GC Handle
+ 3. In an object ready for finalization
+ 4. As a member of an object found in 1, 2 or 3 above.
+
+First, all stacks will be searched for roots, then handle tables, and finally
+the freachable queue of the finalizer. Some caution about the stack roots:
+!GCRoot doesn't attempt to determine if a stack root it encountered is valid
+or is old (discarded) data. You would have to use !CLRStack and !U to
+disassemble the frame that the local or argument value belongs to in order to
+determine if it is still in use.
+
+Because people often want to restrict the search to gc handles and freachable
+objects, there is a -nostacks option.
+\\
+
+COMMAND: objsize.
+!ObjSize [<Object address>]
+
+With no parameters, !ObjSize lists the size of all objects found on managed
+threads. It also enumerates all GCHandles in the process, and totals the size
+of any objects pointed to by those handles. In calculating object size,
+!ObjSize includes the size of all child objects in addition to the parent.
+
+For example, !DumpObj lists a size of 20 bytes for this Customer object:
+
+ 0:000> !do a79d40
+ Name: Customer
+ MethodTable: 009038ec
+ EEClass: 03ee1b84
+ Size: 20(0x14) bytes
+ (C:\pub\unittest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 009038ec 4000008 4 CLASS instance 00a79ce4 name
+ 009038ec 4000009 8 CLASS instance 00a79d2c bank
+ 009038ec 400000a c System.Boolean instance 1 valid
+
+but !ObjSize lists 152 bytes:
+
+ 0:000> !ObjSize a79d40
+ sizeof(00a79d40) = 152 ( 0x98) bytes (Customer)
+
+This is because a Customer points to a Bank, has a name, and the Bank points to
+an Address string. You can use !ObjSize to identify any particularly large
+objects, such as a managed cache in a web server.
+
+While running ObjSize with no arguments may point to specific roots that hold
+onto large amounts of memory it does not provide information regarding the
+amount of managed memory that is still alive. This is due to the fact that a
+number of roots can share a common subgraph, and that part will be reported in
+the size of all the roots that reference the subgraph.
+
+Please note the -aggregate parameter to !ObjSize has been removed. Please see
+'!DumpHeap -live' and '!DumpHeap -dead' for that functionality.
+
+\\
+
+COMMAND: finalizequeue.
+!FinalizeQueue [-detail] | [-allReady] [-short]
+
+This command lists the objects registered for finalization. Here is output from
+a simple program:
+
+ 0:000> !finalizequeue
+ SyncBlocks to be cleaned up: 0
+ MTA Interfaces to be released: 0
+ STA Interfaces to be released: 1
+ generation 0 has 4 finalizable objects (0015bc90->0015bca0)
+ generation 1 has 0 finalizable objects (0015bc90->0015bc90)
+ generation 2 has 0 finalizable objects (0015bc90->0015bc90)
+ Ready for finalization 0 objects (0015bca0->0015bca0)
+ Statistics:
+ MT Count TotalSize Class Name
+ 5ba6cf78 1 24 Microsoft.Win32.SafeHandles.SafeFileHandle
+ 5ba5db04 1 68 System.Threading.Thread
+ 5ba73e28 2 112 System.IO.StreamWriter
+ Total 4 objects
+
+The GC heap is divided into generations, and objects are listed accordingly. We
+see that only generation 0 (the youngest generation) has any objects registered
+for finalization. The notation "(0015bc90->0015bca0)" means that if you look at
+memory in that range, you'll see the object pointers that are registered:
+
+0:000> dd 15bc90 15bca0-4
+0015bc90 00a743f4 00a79f00 00a7b3d8 00a7b47c
+
+You could run !DumpObj on any of those pointers to learn more. In this example,
+there are no objects ready for finalization, presumably because they still have
+roots (You can use !GCRoot to find out). The statistics section provides a
+higher-level summary of the objects registered for finalization. Note that
+objects ready for finalization are also included in the statistics (if any).
+
+Specifying -short will inhibit any display related to SyncBlocks or RCWs.
+
+The arguments in detail:
+
+-allReady Specifying this argument will allow for the display of all objects
+ that are ready for finalization, whether they are already marked by
+ the GC as such, or whether the next GC will. The objects that are
+ not in the "Ready for finalization" list are finalizable objects that
+ are no longer rooted. This option can be very expensive, as it
+ verifies whether all the objects in the finalizable queues are still
+ rooted or not.
+-short Limits the output to just the address of each object. If used in
+ conjunction with -allReady it enumerates all objects that have a
+ finalizer that are no longer rooted. If used independently it lists
+ all objects in the finalizable and "ready for finalization" queues.
+-detail Will display extra information on any SyncBlocks that need to be
+ cleaned up, and on any RuntimeCallableWrappers (RCWs) that await
+ cleanup. Both of these data structures are cached and cleaned up by
+ the finalizer thread when it gets a chance to run.
+\\
+
+COMMAND: printexception.
+!PrintException [-nested] [-lines] [-ccw] [<Exception object address>] [<CCW pointer>]
+
+This will format fields of any object derived from System.Exception. One of the
+more useful aspects is that it will format the _stackTrace field, which is a
+binary array. If _stackTraceString field is not filled in, that can be helpful
+for debugging. You can of course use !DumpObj on the same exception object to
+explore more fields.
+
+If called with no parameters, PrintException will look for the last outstanding
+exception on the current thread and print it. This will be the same exception
+that shows up in a run of !Threads.
+
+!PrintException will notify you if there are any nested exceptions on the
+current managed thread. (A nested exception occurs when you throw another
+exception within a catch handler already being called for another exception).
+If there are nested exceptions, you can re-run !PrintException with the
+"-nested" option to get full details on the nested exception objects. The
+!Threads command will also tell you which threads have nested exceptions.
+
+!PrintException can display source information if available, by specifying the
+-lines command line argument.
+
+!PrintException prints the exception object corresponding to a given CCW pointer,
+which can be specified using the -ccw option.
+
+The abbreviation !pe can be used for brevity.
+\\
+
+COMMAND: traverseheap.
+!TraverseHeap [-xml] [-verify] <filename>
+
+!TraverseHeap writes out a file in a format understood by the CLR Profiler.
+You can download the CLR Profiler from this link:
+
+http://www.microsoft.com/downloads/details.aspx?FamilyId=86CE6052-D7F4-4AEB-
+9B7A-94635BEEBDDA&displaylang=en
+
+It creates a graphical display of the GC heap to help you analyze the state of
+your application.
+
+If you pass the -verify option it will do more sanity checking of the heap
+as it dumps it. Use this option if heap corruption is suspected.
+
+If you pass the "-xml" flag, the file is instead written out in an easy to
+understand xml format:
+
+ <gcheap>
+ <types>
+ <type id="1" name="System.String">
+ ...
+ </types>
+ <roots>
+ <root kind="handle" address="0x00a73ff0"/>
+ <root kind="stack" address="0x0069f0e0"/>
+ ...
+ </roots>
+ <objects>
+ <object address="0x00b73030" typeid="1" size="300"/>
+ <object address="0x00b75054" typeid="5" size="20">
+ <member address="0x00b75088" />
+ ...
+ </object>
+ ...
+ </objects>
+ </gcheap>
+
+You can break into your process, load SOS, take a snapshot of your heap with
+this function, then continue.
+\\
+COMMAND: threadstate.
+!ThreadState value
+
+The !Threads command outputs, among other things, the state of the thread.
+This is a bit field which corresponds to various states the thread is in.
+To check the state of the thread, simply pass that bit field from the
+output of !Threads into !ThreadState.
+
+Example:
+ 0:003> !Threads
+ ThreadCount: 2
+ UnstartedThread: 0
+ BackgroundThread: 1
+ PendingThread: 0
+ DeadThread: 0
+ Hosted Runtime: no
+ PreEmptive GC Alloc Lock
+ ID OSID ThreadOBJ State GC Context Domain Count APT Exception
+ 0 1 250 0019b068 a020 Disabled 02349668:02349fe8 0015def0 0 MTA
+ 2 2 944 001a6020 b220 Enabled 00000000:00000000 0015def0 0 MTA (Finalizer)
+ 0:003> !ThreadState b220
+ Legal to Join
+ Background
+ CLR Owns
+ CoInitialized
+ In Multi Threaded Apartment
+
+Possible thread states:
+ Thread Abort Requested
+ GC Suspend Pending
+ User Suspend Pending
+ Debug Suspend Pending
+ GC On Transitions
+ Legal to Join
+ Yield Requested
+ Hijacked by the GC
+ Blocking GC for Stack Overflow
+ Background
+ Unstarted
+ Dead
+ CLR Owns
+ CoInitialized
+ In Single Threaded Apartment
+ In Multi Threaded Apartment
+ Reported Dead
+ Fully initialized
+ Task Reset
+ Sync Suspended
+ Debug Will Sync
+ Stack Crawl Needed
+ Suspend Unstarted
+ Aborted
+ Thread Pool Worker Thread
+ Interruptible
+ Interrupted
+ Completion Port Thread
+ Abort Initiated
+ Finalized
+ Failed to Start
+ Detached
+\\
+COMMAND: threads.
+!Threads [-live] [-special]
+
+!Threads lists all the mananaged threads in the process.
+
+-live: optional. Only print threads associated with a live thread.
+-special: optional. With this switch, the command will display all the special
+ threads created by CLR. Those threads might not be managed threads
+ so they might not be shown in the first part of the command's
+ output. Example of special threads include: GC threads (in
+ concurrent GC and server GC), Debugger helper threads, Finalizer
+ threads, AppDomain Unload threads, and Threadpool timer threads.
+
+Each thread has many attributes, many of which can be ignored. The important
+ones are discussed below:
+
+There are three ID columns:
+
+1) The debugger shorthand ID (When the runtime is hosted this column might
+ display the special string "<<<<" when this internal thread object is not
+ associated with any physical thread - this may happen when the host reuses
+ the runtime internal thread object)
+2) The CLR Thread ID
+3) The OS thread ID.
+
+If PreEmptiveGC is enabled for a thread, then a garbage collection
+can occur while that thread is running. For example, if you break in while
+a managed thread is making a PInvoke call to a Win32 function, that thread
+will be in PreEmptive GC mode.
+
+The Domain column indicates what AppDomain the thread is currently executing
+in. You can pass this value to !DumpDomain to find out more.
+
+The APT column gives the COM apartment mode.
+
+Exception will list the last thrown exception (if any) for the thread. More
+details can be obtained by passing the pointer value to !PrintException. If
+you get the notation "(nested exceptions)", you can get details on those
+exceptions by switching to the thread in question, and running
+"!PrintException -nested".
+\\
+
+COMMAND: clrstack.
+!CLRStack [-a] [-l] [-p] [-n] [-f]
+!CLRStack [-a] [-l] [-p] [-i] [variable name] [frame]
+
+CLRStack attempts to provide a true stack trace for managed code only. It is
+handy for clean, simple traces when debugging straightforward managed
+programs. The -p parameter will show arguments to the managed function. The
+-l parameter can be used to show information on local variables in a frame.
+SOS can't retrieve local names at this time, so the output for locals is in
+the format <local address> = <value>. The -a (all) parameter is a short-cut
+for -l and -p combined.
+
+The -f option (full mode) displays the native frames intermixing them with
+the managed frames and the assembly name and function offset for the managed
+frames.
+
+If the debugger has the option SYMOPT_LOAD_LINES specified (either by the
+.lines or .symopt commands), SOS will look up the symbols for every managed
+frame and if successful will display the corresponding source file name and
+line number. The -n (No line numbers) parameter can be specified to disable
+this behavior.
+
+When you see methods with the name "[Frame:...", that indicates a transition
+between managed and unmanaged code. You could run !IP2MD on the return
+addresses in the call stack to get more information on each managed method.
+
+On x64 platforms, Transition Frames are not displayed at this time. To avoid
+heavy optimization of parameters and locals one can request the JIT compiler
+to not optimize functions in the managed app by creating a file myapp.ini
+(if your program is myapp.exe) in the same directory. Put the following lines
+in myapp.ini and re-run:
+
+[.NET Framework Debugging Control]
+GenerateTrackingInfo=1
+AllowOptimize=0
+
+The -i option is a new EXPERIMENTAL addition to CLRStack and will use the ICorDebug
+interfaces to display the managed stack and variables. With this option you can also
+view and expand arrays and fields for managed variables. If a stack frame number is
+specified in the command line, CLRStack will show you the parameters and/or locals
+only for that frame (provided you specify -l or -p or -a of course). If a variable
+name and a stack frame number are specified in the command line, CLRStack will show
+you the parameters and/or locals for that frame, and will also show you the fields
+for that variable name you specified. Here are some examples:
+ !CLRStack -i -a : This will show you all parameters and locals for all frames
+ !CLRStack -i -a 3 : This will show you all parameters and locals, for frame 3
+ !CLRStack -i var1 0 : This will show you the fields of 'var1' for frame 0
+ !CLRStack -i var1.abc 2 : This will show you the fields of 'var1', and expand
+ 'var1.abc' to show you the fields of the 'abc' field,
+ for frame 2.
+ !CLRStack -i var1.[basetype] 0 : This will show you the fields of 'var1', and
+ expand the base type of 'var1' to show you its
+ fields.
+ !CLRStack -i var1.[6] 0 : If 'var1' is an array, this will show you the element
+ at index 6 in the array, along with its fields
+The -i options uses DML output for a better debugging experience, so typically you
+should only need to execute "!CLRStack -i", and from there, click on the DML
+hyperlinks to inspect the different managed stack frames and managed variables.
+\\
+
+COMMAND: ip2md.
+!IP2MD <Code address>
+
+Given an address in managed JITTED code, IP2MD attempts to find the MethodDesc
+associated with it. For example, this output from K:
+
+ 0:000> K
+ ChildEBP RetAddr
+ 00a79c78 03ef02ab image00400000!Mainy.Top()+0xb
+ 00a79c78 03ef01a6 image00400000!Mainy.Level(Int32)+0xb
+ 00a79c78 5d3725a1 image00400000!Mainy.Main()+0xee
+ 0012ea04 5d512f59 clr!CallDescrWorkerInternal+0x30
+ 0012ee34 5d7946aa clr!CallDescrWorker+0x109
+
+ 0:000> !IP2MD 03ef01a6
+ MethodDesc: 00902f40
+ Method Name: Mainy.Main()
+ Class: 03ee1424
+ MethodTable: 009032d8
+ mdToken: 0600000d
+ Module: 001caa38
+ IsJitted: yes
+ CodeAddr: 03ef00b8
+ Transparency: Critical
+ Source file: c:\Code\prj.mini\exc.cs @ 39
+
+We have taken a return address into Mainy.Main, and discovered information
+about that method. You could run !U, !DumpMT, !DumpClass, !DumpMD, or
+!DumpModule on the fields listed to learn more.
+
+The "Source line" output will only be present if the debugger can find the
+symbols for the managed module containing the given <code address>, and if the
+debugger is configured to load line number information.
+\\
+
+COMMAND: u.
+!U [-gcinfo] [-ehinfo] [-n] [-o] <MethodDesc address> | <Code address>
+
+Presents an annotated disassembly of a managed method when given a MethodDesc
+pointer for the method, or a code address within the method body. Unlike the
+debugger "U" function, the entire method from start to finish is printed,
+with annotations that convert metadata tokens to names.
+
+ <example output>
+ ...
+ 03ef015d b901000000 mov ecx,0x1
+ 03ef0162 ff156477a25b call dword ptr [mscorlib_dll+0x3c7764 (5ba27764)] (System.Console.InitializeStdOutError(Boolean), mdToken: 06000713)
+ 03ef0168 a17c20a701 mov eax,[01a7207c] (Object: SyncTextWriter)
+ 03ef016d 89442414 mov [esp+0x14],eax
+
+If you pass the -gcinfo flag, you'll get inline display of the GCInfo for
+the method. You can also obtain this information with the !GCInfo command.
+
+If you pass the -ehinfo flag, you'll get inline display of exception info
+for the method. (Beginning and end of try/finally/catch handlers, etc.).
+You can also obtain this information with the !EHInfo command.
+
+If you pass the -o flag, the byte offset of each instruction from the
+beginning of the method will be printed in addition to the absolute address of
+the instruction.
+
+If the debugger has the option SYMOPT_LOAD_LINES specified (either by the
+.lines or .symopt commands), and if symbols are available for the managed
+module containing the method being examined, the output of the command will
+include the source file name and line number corresponding to the
+disassembly. The -n (No line numbers) flag can be specified to disable this
+behavior.
+
+ <example output>
+ ...
+ c:\Code\prj.mini\exc.cs @ 38:
+ 001b00b0 8b0d3020ab03 mov ecx,dword ptr ds:[3AB2030h] ("Break in debugger. When done type <Enter> to continue: ")
+ 001b00b6 e8d5355951 call mscorlib_ni+0x8b3690 (51743690) (System.Console.Write(System.String), mdToken: 0600091b)
+ 001b00bb 90 nop
+
+ c:\Code\prj.mini\exc.cs @ 39:
+ 001b00bc e863cdc651 call mscorlib_ni+0xf8ce24 (51e1ce24) (System.Console.ReadLine(), mdToken: 060008f6)
+ >>> 001b00c1 90 nop
+ ...
+\\
+
+COMMAND: dumpstack.
+!DumpStack [-EE] [-n] [top stack [bottom stack]]
+
+[x86 and x64 documentation]
+
+This command provides a verbose stack trace obtained by "scraping." Therefore
+the output is very noisy and potentially confusing. The command is good for
+viewing the complete call stack when "kb" gets confused. For best results,
+make sure you have valid symbols.
+
+-EE will only show managed functions.
+
+If the debugger has the option SYMOPT_LOAD_LINES specified (either by the
+.lines or .symopt commands), SOS will look up the symbols for every managed
+frame and if successful will display the corresponding source file name and
+line number. The -n (No line numbers) parameter can be specified to disable
+this behavior.
+
+You can also pass a stack range to limit the output. Use the debugger
+extension !teb to get the top and bottom stack values.
+
+\\
+
+COMMAND: eestack.
+!EEStack [-short] [-EE]
+
+This command runs !DumpStack on all threads in the process. The -EE option is
+passed directly to !DumpStack. The -short option tries to narrow down the
+output to "interesting" threads only, which is defined by
+
+1) The thread has taken a lock.
+2) The thread has been "hijacked" in order to allow a garbage collection.
+3) The thread is currently in managed code.
+
+See the documentation for !DumpStack for more info.
+\\
+
+COMMAND: ehinfo.
+!EHInfo (<MethodDesc address> | <Code address>)
+
+!EHInfo shows the exception handling blocks in a jitted method. For each
+handler, it shows the type, including code addresses and offsets for the clause
+block and the handler block. For a TYPED handler, this would be the "try" and
+"catch" blocks respectively.
+
+Sample output:
+
+ 0:000> !ehinfo 33bbd3a
+ MethodDesc: 03310f68
+ Method Name: MainClass.Main()
+ Class: 03571358
+ MethodTable: 0331121c
+ mdToken: 0600000b
+ Module: 001e2fd8
+ IsJitted: yes
+ CodeAddr: 033bbca0
+ Transparency: Critical
+
+ EHHandler 0: TYPED catch(System.IO.FileNotFoundException)
+ Clause: [033bbd2b, 033bbd3c] [8b, 9c]
+ Handler: [033bbd3c, 033bbd50] [9c, b0]
+
+ EHHandler 1: FINALLY
+ Clause: [033bbd83, 033bbda3] [e3, 103]
+ Handler: [033bbda3, 033bbdc5] [103, 125]
+
+ EHHandler 2: TYPED catch(System.Exception)
+ Clause: [033bbd7a, 033bbdc5] [da, 125]
+ Handler: [033bbdc5, 033bbdd6] [125, 136]
+
+\\
+
+COMMAND: gcinfo.
+!GCInfo (<MethodDesc address> | <Code address>)
+
+!GCInfo is especially useful for CLR Devs who are trying to determine if there
+is a bug in the JIT Compiler. It parses the GCEncoding for a method, which is a
+compressed stream of data indicating when registers or stack locations contain
+managed objects. It is important to keep track of this information, because if
+a garbage collection occurs, the collector needs to know where roots are so it
+can update them with new object pointer values.
+
+Here is sample output where you can see the change in register state. Normally
+you would print this output out and read it alongside a disassembly of the
+method. For example, the notation "reg EDI becoming live" at offset 0x11 of the
+method might correspond to a "mov edi,ecx" statement.
+
+ 0:000> !gcinfo 5b68dbb8 (5b68dbb8 is the start of a JITTED method)
+ entry point 5b68dbb8
+ preJIT generated code
+ GC info 5b9f2f09
+ Method info block:
+ method size = 0036
+ prolog size = 19
+ epilog size = 8
+ epilog count = 1
+ epilog end = yes
+ saved reg. mask = 000B
+ ebp frame = yes
+ fully interruptible=yes
+ double align = no
+ security check = no
+ exception handlers = no
+ local alloc = no
+ edit & continue = no
+ varargs = no
+ argument count = 4
+ stack frame size = 1
+ untracked count = 5
+ var ptr tab count = 0
+ epilog at 002E
+ 36 D4 8C C7 AA |
+ 93 F3 40 05 |
+
+ Pointer table:
+ 14 | [EBP+14H] an untracked local
+ 10 | [EBP+10H] an untracked local
+ 0C | [EBP+0CH] an untracked local
+ 08 | [EBP+08H] an untracked local
+ 44 | [EBP-04H] an untracked local
+ F1 79 | 0011 reg EDI becoming live
+ 72 | 0013 reg ESI becoming live
+ 83 | 0016 push ptr 0
+ 8B | 0019 push ptr 1
+ 93 | 001C push ptr 2
+ 9B | 001F push ptr 3
+ 56 | 0025 reg EDX becoming live
+ 4A | 0027 reg ECX becoming live
+ 0E | 002D reg ECX becoming dead
+ 10 | 002D reg EDX becoming dead
+ E0 | 002D pop 4 ptrs
+ F0 31 | 0036 reg ESI becoming dead
+ 38 | 0036 reg EDI becoming dead
+ FF |
+
+This function is important for CLR Devs, but very difficult for anyone else to
+make sense of it. You would usually come to use it if you suspect a gc heap
+corruption bug caused by invalid GCEncoding for a particular method.
+\\
+
+COMMAND: comstate.
+!COMState
+
+!COMState lists the com apartment model for each thread, as well as a Context
+pointer if provided.
+\\
+
+COMMAND: bpmd.
+!BPMD [-nofuturemodule] <module name> <method name> [<il offset>]
+!BPMD <source file name>:<line number>
+!BPMD -md <MethodDesc>
+!BPMD -list
+!BPMD -clear <pending breakpoint number>
+!BPMD -clearall
+
+!BPMD provides managed breakpoint support. If it can resolve the method name
+to a loaded, jitted or ngen'd function it will create a breakpoint with "bp".
+If not then either the module that contains the method hasn't been loaded yet
+or the module is loaded, but the function is not jitted yet. In these cases,
+!bpmd asks the Windows Debugger to receive CLR Notifications, and waits to
+receive news of module loads and JITs, at which time it will try to resolve
+the function to a breakpoint. -nofuturemodule can be used to suppress
+creating a breakpoint against a module that has not yet been loaded.
+
+Management of the list of pending breakpoints can be done via !BPMD -list,
+!BPMD -clear, and !BPMD -clearall commands. !BPMD -list generates a list of
+all of the pending breakpoints. If the pending breakpoint has a non-zero
+module id, then that pending breakpoint is specific to function in that
+particular loaded module. If the pending breakpoint has a zero module id, then
+the breakpoint applies to modules that have not yet been loaded. Use
+!BPMD -clear or !BPMD -clearall to remove pending breakpoints from the list.
+
+This brings up a good question: "I want to set a breakpoint on the main
+method of my application. How can I do this?"
+
+ 1) If you know the full path to SOS, use this command and skip to step 6
+ .load <the full path to sos.dll>
+
+ 2) If you don't know the full path to sos, its usually next to clr.dll
+ You can wait for clr to load and then find it.
+ Start the debugger and type:
+ sxe -c "" clrn
+ 3) g
+ 4) You'll get the following notification from the debugger:
+ "CLR notification: module 'mscorlib' loaded"
+ 5) Now you can load SOS. Type
+ .loadby sos clr
+
+ 6) Add the breakpoint with command such as:
+ !bpmd myapp.exe MyApp.Main
+ 7) g
+ 8) You will stop at the start of MyApp.Main. If you type "bl" you will
+ see the breakpoint listed.
+
+You can specify breakpoints by file and line number if:
+ a) You have some version of .Net Framework installed on your machine. Any OS from
+ Vista onwards should have .Net Framework installed by default.
+ b) You have PDBs for the managed modules that need breakpoints, and your symbol
+ path points to those PDBs.
+This is often easier than module and method name syntax. For example:
+ !bpmd Demo.cs:15
+
+
+To correctly specify explicitly implemented methods make sure to retrieve the
+method name from the metadata, or from the output of the "!dumpmt -md" command.
+For example:
+
+ public interface I1
+ {
+ void M1();
+ }
+ public class ExplicitItfImpl : I1
+ {
+ ...
+ void I1.M1() // this method's name is 'I1.M1'
+ { ... }
+ }
+
+ !bpmd myapp.exe ExplicitItfImpl.I1.M1
+
+
+!BPMD works equally well with generic types. Adding a breakpoint on a generic
+type sets breakpoints on all already JIT-ted generic methods and sets a pending
+breakpoint for any instantiation that will be JIT-ted in the future.
+
+Example for generics:
+ Given the following two classes:
+
+ class G3<T1, T2, T3>
+ {
+ ...
+ public void F(T1 p1, T2 p2, T3 p3)
+ { ... }
+ }
+
+ public class G1<T> {
+ // static method
+ static public void G<W>(W w)
+ { ... }
+ }
+
+ One would issue the following commands to set breapoints on G3.F() and
+ G1.G():
+
+ !bpmd myapp.exe G3`3.F
+ !bpmd myapp.exe G1`1.G
+
+And for explicitly implemented methods on generic interfaces:
+ public interface IT1<T>
+ {
+ void M1(T t);
+ }
+
+ public class ExplicitItfImpl<U> : IT1<U>
+ {
+ ...
+ void IT1<U>.M1(U u) // this method's name is 'IT1<U>.M1'
+ { ... }
+ }
+
+ !bpmd bpmd.exe ExplicitItfImpl`1.IT1<U>.M1
+
+Additional examples:
+ If IT1 and ExplicitItfImpl are types declared inside another class,
+ Outer, the bpmd command would become:
+
+ !bpmd bpmd.exe Outer+ExplicitItfImpl`1.Outer.IT1<U>.M1
+
+ (note that the fully qualified type name for ExplicitItfImpl became
+ Outer+ExplicitItfImpl, using the '+' separator, while the method name
+ is Outer.IT1<U>.M1, using a '.' as the separator)
+
+ Furthermore, if the Outer class resides in a namespace, NS, the bpmd
+ command to use becomes:
+
+ !bpmd bpmd.exe NS.Outer+ExplicitItfImpl`1.NS.Outer.IT1<U>.M1
+
+!BPMD does not accept offsets nor parameters in the method name. You can add
+an IL offset as an optional parameter seperate from the name. If there are overloaded
+methods, !bpmd will set a breakpoint for all of them.
+
+In the case of hosted environments such as SQL, the module name may be
+complex, like 'price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
+For this case, just be sure to surround the module name with single quotes,
+like:
+
+!bpmd 'price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' Price.M2
+
+\\
+
+COMMAND: dumpdomain.
+!DumpDomain [<Domain address>]
+
+When called with no parameters, !DumpDomain will list all the AppDomains in the
+process. It enumerates each Assembly loaded into those AppDomains as well.
+In addition to your application domain, and any domains it might create, there
+are two special domains: the Shared Domain and the System Domain.
+
+Any Assembly pointer in the output can be passed to !DumpAssembly. Any Module
+pointer in the output can be passed to !DumpModule. Any AppDomain pointer can
+be passed to !DumpDomain to limit output only to that AppDomain. Other
+functions provide an AppDomain pointer as well, such as !Threads where it lists
+the current AppDomain for each thread.
+\\
+
+COMMAND: eeheap.
+!EEHeap [-gc] [-loader]
+
+!EEHeap enumerates process memory consumed by internal CLR data structures. You
+can limit the output by passing "-gc" or "-loader". All information will be
+displayed otherwise.
+
+The information for the Garbage Collector lists the ranges of each Segment in
+the managed heap. This can be useful if you believe you have an object pointer.
+If the pointer falls within a segment range given by "!EEHeap -gc", then you do
+have an object pointer, and can attempt to run "!DumpObj" on it.
+
+Here is output for a simple program:
+
+ 0:000> !eeheap -gc
+ Number of GC Heaps: 1
+ generation 0 starts at 0x00a71018
+ generation 1 starts at 0x00a7100c
+ generation 2 starts at 0x00a71000
+ segment begin allocated size
+ 00a70000 00a71000 00a7e01c 0000d01c(53276)
+ Large object heap starts at 0x01a71000
+ segment begin allocated size
+ 01a70000 01a71000 01a76000 0x00005000(20480)
+ Total Size 0x1201c(73756)
+ ------------------------------
+ GC Heap Size 0x1201c(73756)
+
+So the total size of the GC Heap is only 72K. On a large web server, with
+multiple processors, you can expect to see a GC Heap of 400MB or more. The
+Garbage Collector attempts to collect and reclaim memory only when required to
+by memory pressure for better performance. You can also see the notion of
+"generations," wherein the youngest objects live in generation 0, and
+long-lived objects eventually get "promoted" to generation 2.
+
+The loader output lists various private heaps associated with AppDomains. It
+also lists heaps associated with the JIT compiler, and heaps associated with
+Modules. For example:
+
+ 0:000> !EEHeap -loader
+ Loader Heap:
+ --------------------------------------
+ System Domain: 5e0662a0
+ LowFrequencyHeap:008f0000(00002000:00001000) Size: 0x00001000 bytes.
+ HighFrequencyHeap:008f2000(00008000:00001000) Size: 0x00001000 bytes.
+ StubHeap:008fa000(00002000:00001000) Size: 0x00001000 bytes.
+ Total size: 0x3000(12288)bytes
+ --------------------------------------
+ Shared Domain: 5e066970
+ LowFrequencyHeap:00920000(00002000:00001000) 03e30000(00010000:00003000) Size: 0x00004000 bytes.
+ Wasted: 0x00001000 bytes.
+ HighFrequencyHeap:00922000(00008000:00001000) Size: 0x00001000 bytes.
+ StubHeap:0092a000(00002000:00001000) Size: 0x00001000 bytes.
+ Total size: 0x6000(24576)bytes
+ --------------------------------------
+ Domain 1: 14f000
+ LowFrequencyHeap:00900000(00002000:00001000) 03ee0000(00010000:00003000) Size: 0x00004000 bytes.
+ Wasted: 0x00001000 bytes.
+ HighFrequencyHeap:00902000(00008000:00003000) Size: 0x00003000 bytes.
+ StubHeap:0090a000(00002000:00001000) Size: 0x00001000 bytes.
+ Total size: 0x8000(32768)bytes
+ --------------------------------------
+ Jit code heap:
+ Normal JIT:03ef0000(00010000:00002000) Size: 0x00002000 bytes.
+ Total size: 0x2000(8192)bytes
+ --------------------------------------
+ Module Thunk heaps:
+ Module 5ba22410: Size: 0x00000000 bytes.
+ Module 001c1320: Size: 0x00000000 bytes.
+ Module 001c03f0: Size: 0x00000000 bytes.
+ Module 001caa38: Size: 0x00000000 bytes.
+ Total size: 0x0(0)bytes
+ --------------------------------------
+ Module Lookup Table heaps:
+ Module 5ba22410:Size: 0x00000000 bytes.
+ Module 001c1320:Size: 0x00000000 bytes.
+ Module 001c03f0:Size: 0x00000000 bytes.
+ Module 001caa38:03ec0000(00010000:00002000) Size: 0x00002000 bytes.
+ Total size: 0x2000(8192)bytes
+ --------------------------------------
+ Total LoaderHeap size: 0x15000(86016)bytes
+ =======================================
+
+By using !EEHeap to keep track of the growth of these private heaps, we are
+able to rule out or include them as a source of a memory leak.
+\\
+
+COMMAND: name2ee.
+!Name2EE <module name> <type or method name>
+!Name2EE <module name>!<type or method name>
+
+This function allows you to turn a class name into a MethodTable and EEClass.
+It turns a method name into a MethodDesc. Here is an example for a method:
+
+ 0:000> !name2ee unittest.exe MainClass.Main
+ Module: 001caa38
+ Token: 0x0600000d
+ MethodDesc: 00902f40
+ Name: MainClass.Main()
+ JITTED Code Address: 03ef00b8
+
+and for a class:
+
+ 0:000> !name2ee unittest!MainClass
+ Module: 001caa38
+ Token: 0x02000005
+ MethodTable: 009032d8
+ EEClass: 03ee1424
+ Name: MainClass
+
+The module you are "browsing" with Name2EE needs to be loaded in the process.
+To get a type name exactly right, first browse the module with ILDASM. You
+can also pass * as the <module name> to search all loaded managed modules.
+<module name> can also be the debugger's name for a module, such as
+mscorlib or image00400000.
+
+The Windows Debugger syntax of <module>!<type> is also supported. You can
+use an asterisk on the left of the !, but the type on the right side needs
+to be fully qualified.
+
+If you are looking for a way to display a static field of a class (and you
+don't have an instance of the class, so !dumpobj won't help you), note that
+once you have the EEClass, you can run !DumpClass, which will display the
+value of all static fields.
+
+There is yet one more way to specify a module name. In the case of modules
+loaded from an assembly store (such as a SQL db) rather than disk, the
+module name will look like this:
+
+price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
+
+For this kind of module, simply use price as the module name:
+
+ 0:044> !name2ee price Price
+ Module: 10f028b0 (price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null)
+ Token: 0x02000002
+ MethodTable: 11a47ae0
+ EEClass: 11a538c8
+ Name: Price
+
+Where are we getting these module names from? Run !DumpDomain to see a list of
+all loaded modules in all domains. And remember that you can browse all the
+types in a module with !DumpModule -mt <module pointer>.
+\\
+
+COMMAND: syncblk.
+!SyncBlk [-all | <syncblk number>]
+
+A SyncBlock is a holder for extra information that doesn't need to be created
+for every object. It can hold COM Interop data, HashCodes, and locking
+information for thread-safe operations.
+
+When called without arguments, !SyncBlk will print the list of SyncBlocks
+corresponding to objects that are owned by a thread. For example, a
+
+ lock(MyObject)
+ {
+ ....
+ }
+
+statement will set MyObject to be owned by the current thread. A SyncBlock will
+be created for MyObject, and the thread ownership information stored there
+(this is an oversimplification, see NOTE below). If another thread tries to
+execute the same code, they won't be able to enter the block until the first
+thread exits.
+
+This makes !SyncBlk useful for detecting managed deadlocks. Consider that the
+following code is executed by Threads A & B:
+
+ Resource r1 = new Resource();
+ Resource r2 = new Resource();
+
+ ...
+
+ lock(r1) lock(r2)
+ { {
+ lock(r2) lock(r1)
+ { {
+ ... ...
+ } }
+ } }
+
+This is a deadlock situation, as Thread A could take r1, and Thread B r2,
+leaving both threads with no option but to wait forever in the second lock
+statement. !SyncBlk will detect this with the following output:
+
+ 0:003> !syncblk
+ Index SyncBlock MonitorHeld Recursion Owning Thread Info SyncBlock Owner
+ 238 001e40ec 3 1 001e4e60 e04 3 00a7a194 Resource
+ 239 001e4124 3 1 001e5980 ab8 4 00a7a1a4 Resource
+
+It means that Thread e04 owns object 00a7a194, and Thread ab8 owns object
+00a7a1a4. Combine that information with the call stacks of the deadlock:
+
+(threads 3 and 4 have similar output)
+ 0:003> k
+ ChildEBP RetAddr
+ 0404ea04 77f5c524 SharedUserData!SystemCallStub+0x4
+ 0404ea08 77e75ee0 ntdll!NtWaitForMultipleObjects+0xc
+ 0404eaa4 5d9de9d6 KERNEL32!WaitForMultipleObjectsEx+0x12c
+ 0404eb38 5d9def80 clr!Thread::DoAppropriateAptStateWait+0x156
+ 0404ecc4 5d9dd8bb clr!Thread::DoAppropriateWaitWorker+0x360
+ 0404ed20 5da628dd clr!Thread::DoAppropriateWait+0xbb
+ 0404ede4 5da4e2e2 clr!CLREvent::Wait+0x29d
+ 0404ee70 5da4dd41 clr!AwareLock::EnterEpilog+0x132
+ 0404ef34 5da4efa3 clr!AwareLock::Enter+0x2c1
+ 0404f09c 5d767880 clr!AwareLock::Contention+0x483
+ 0404f1c4 03f00229 clr!JITutil_MonContention+0x2c0
+ 0404f1f4 5b6ef077 image00400000!Worker.Work()+0x79
+ ...
+
+By looking at the code corresponding to Worker.Work()+0x79 (run "!u 03f00229"),
+you can see that thread 3 is attempting to acquire the Resource 00a7a1a4, which
+is owned by thread 4.
+
+NOTE:
+It is not always the case that a SyncBlock will be created for every object
+that is locked by a thread. In version 2.0 of the CLR and above, a mechanism
+called a ThinLock will be used if there is not already a SyncBlock for the
+object in question. ThinLocks will not be reported by the !SyncBlk command.
+You can use "!DumpHeap -thinlock" to list objects locked in this way.
+\\
+
+COMMAND: dumpmt.
+!DumpMT [-MD] <MethodTable address>
+
+Examine a MethodTable. Each managed object has a MethodTable pointer at the
+start. If you pass the "-MD" flag, you'll also see a list of all the methods
+defined on the object.
+\\
+
+COMMAND: dumpclass.
+!DumpClass <EEClass address>
+
+The EEClass is a data structure associated with an object type. !DumpClass
+will show attributes, as well as list the fields of the type. The output is
+similar to !DumpObj. Although static field values will be displayed,
+non-static values won't because you need an instance of an object for that.
+
+You can get an EEClass to look at from !DumpMT, !DumpObj, !Name2EE, and
+!Token2EE among others.
+\\
+
+COMMAND: dumpmd.
+!DumpMD <MethodDesc address>
+
+This command lists information about a MethodDesc. You can use !IP2MD to turn
+a code address in a managed function into a MethodDesc:
+
+ 0:000> !dumpmd 902f40
+ Method Name: Mainy.Main()
+ Class: 03ee1424
+ MethodTable: 009032d8
+ mdToken: 0600000d
+ Module: 001caa78
+ IsJitted: yes
+ CodeAddr: 03ef00b8
+
+If IsJitted is "yes," you can run !U on the CodeAddr pointer to see a
+disassembly of the JITTED code. You can also call !DumpClass, !DumpMT,
+!DumpModule on the Class, MethodTable and Module fields above.
+\\
+
+COMMAND: token2ee.
+!Token2EE <module name> <token>
+
+This function allows you to turn a metadata token into a MethodTable or
+MethodDesc. Here is an example showing class tokens being resolved:
+
+ 0:000> !token2ee unittest.exe 02000003
+ Module: 001caa38
+ Token: 0x02000003
+ MethodTable: 0090375c
+ EEClass: 03ee1ae0
+ Name: Bank
+ 0:000> !token2ee image00400000 02000004
+ Module: 001caa38
+ Token: 0x02000004
+ MethodTable: 009038ec
+ EEClass: 03ee1b84
+ Name: Customer
+
+The module you are "browsing" with Token2EE needs to be loaded in the process.
+This function doesn't see much use, especially since a tool like ILDASM can
+show the mapping between metadata tokens and types/methods in a friendlier way.
+But it could be handy sometimes.
+
+You can pass "*" for <module name> to find what that token maps to in every
+loaded managed module. <module name> can also be the debugger's name for a
+module, such as mscorlib or image00400000.
+\\
+
+COMMAND: eeversion.
+!EEVersion
+
+This prints the Common Language Runtime version. It also tells you if the code
+is running in "Workstation" or "Server" mode, a distinction which affects the
+garbage collector. The most apparent difference in the debugger is that in
+"Server" mode there is one dedicated garbage collector thread per CPU.
+
+A handy supplement to this function is to also run "lm v m clr". That
+will provide more details about the CLR, including where clr.dll is
+loaded from.
+\\
+
+COMMAND: dumpmodule.
+!DumpModule [-mt] <Module address>
+
+You can get a Module address from !DumpDomain, !DumpAssembly and other
+functions. Here is sample output:
+
+ 0:000> !DumpModule 1caa50
+ Name: C:\pub\unittest.exe
+ Attributes: PEFile
+ Assembly: 001ca248
+ LoaderHeap: 001cab3c
+ TypeDefToMethodTableMap: 03ec0010
+ TypeRefToMethodTableMap: 03ec0024
+ MethodDefToDescMap: 03ec0064
+ FieldDefToDescMap: 03ec00a4
+ MemberRefToDescMap: 03ec00e8
+ FileReferencesMap: 03ec0128
+ AssemblyReferencesMap: 03ec012c
+ MetaData start address: 00402230 (1888 bytes)
+
+The Maps listed map metadata tokens to CLR data structures. Without going into
+too much detail, you can examine memory at those addresses to find the
+appropriate structures. For example, the TypeDefToMethodTableMap above can be
+examined:
+
+ 0:000> dd 3ec0010
+ 03ec0010 00000000 00000000 0090320c 0090375c
+ 03ec0020 009038ec ...
+
+This means TypeDef token 2 maps to a MethodTable with the value 0090320c. You
+can run !DumpMT to verify that. The MethodDefToDescMap takes a MethodDef token
+and maps it to a MethodDesc, which can be passed to !DumpMD.
+
+There is a new option "-mt", which will display the types defined in a module,
+and the types referenced by the module. For example:
+
+ 0:000> !dumpmodule -mt 1aa580
+ Name: C:\pub\unittest.exe
+ ...<etc>...
+ MetaData start address: 0040220c (1696 bytes)
+
+ Types defined in this module
+
+ MT TypeDef Name
+ --------------------------------------------------------------------------
+ 030d115c 0x02000002 Funny
+ 030d1228 0x02000003 Mainy
+
+ Types referenced in this module
+
+ MT TypeRef Name
+ --------------------------------------------------------------------------
+ 030b6420 0x01000001 System.ValueType
+ 030b5cb0 0x01000002 System.Object
+ 030fceb4 0x01000003 System.Exception
+ 0334e374 0x0100000c System.Console
+ 03167a50 0x0100000e System.Runtime.InteropServices.GCHandle
+ 0336a048 0x0100000f System.GC
+
+\\
+
+COMMAND: threadpool.
+!ThreadPool
+
+This command lists basic information about the ThreadPool, including the number
+of work requests in the queue, number of completion port threads, and number of
+timers.
+\\
+
+COMMAND: dumpassembly.
+!DumpAssembly <Assembly address>
+
+Example output:
+
+ 0:000> !dumpassembly 1ca248
+ Parent Domain: 0014f000
+ Name: C:\pub\unittest.exe
+ ClassLoader: 001ca060
+ Module Name
+ 001caa50 C:\pub\unittest.exe
+
+An assembly can consist of multiple modules, and those will be listed. You can
+get an Assembly address from the output of !DumpDomain.
+\\
+
+COMMAND: dumpruntimetypes.
+!DumpRuntimeTypes
+
+!DumpRuntimeTypes finds all System.RuntimeType objects in the gc heap and
+prints the type name and MethodTable they refer too. Sample output:
+
+ Address Domain MT Type Name
+ ------------------------------------------------------------------------------
+ a515f4 14a740 5baf8d28 System.TypedReference
+ a51608 14a740 5bb05764 System.Globalization.BaseInfoTable
+ a51958 14a740 5bb05b24 System.Globalization.CultureInfo
+ a51a44 14a740 5bb06298 System.Globalization.GlobalizationAssembly
+ a51de0 14a740 5bb069c8 System.Globalization.TextInfo
+ a56b98 14a740 5bb12d28 System.Security.Permissions.HostProtectionResource
+ a56bbc 14a740 5baf7248 System.Int32
+ a56bd0 14a740 5baf3fdc System.String
+ a56cfc 14a740 5baf36a4 System.ValueType
+ ...
+
+This command will print a "?" in the domain column if the type is loaded into multiple
+AppDomains. For example:
+
+ 0:000> !DumpRuntimeTypes
+ Address Domain MT Type Name
+ ------------------------------------------------------------------------------
+ 28435a0 ? 3f6a8c System.TypedReference
+ 28435b4 ? 214d6c System.ValueType
+ 28435c8 ? 216314 System.Enum
+ 28435dc ? 2147cc System.Object
+ 284365c ? 3cd57c System.IntPtr
+ 2843670 ? 3feaac System.Byte
+ 2843684 ? 23a544c System.IEquatable`1[[System.IntPtr, mscorlib]]
+ 2843784 ? 3c999c System.Int32
+ 2843798 ? 3caa04 System.IEquatable`1[[System.Int32, mscorlib]]
+
+\\
+
+COMMAND: dumpsig.
+!DumpSig <sigaddr> <moduleaddr>
+
+This command dumps the signature of a method or field given by <sigaddr>. This is
+useful when you are debugging parts of the runtime which returns a raw PCCOR_SIGNATURE
+structure and need to know what its contents are.
+
+Sample output for a method:
+ 0:000> !dumpsig 0x000007fe`ec20879d 0x000007fe`eabd1000
+ [DEFAULT] [hasThis] Void (Boolean,String,String)
+
+The first section of the output is the calling convention. This includes, but is not
+limited to, "[DEFAULT]", "[C]", "[STDCALL]", "[THISCALL]", and so on. The second
+portion of the output is either "[hasThis]" or "[explicit]" for whether the method
+is an instance method or a static method respectively. The third portion of the
+output is the return value (in this case a "void"). Finally, the method's arguments
+are printed as the final portion of the output.
+
+Sample output for a field:
+ 0:000> !dumpsig 0x000007fe`eb7fd8cd 0x000007fe`eabd1000
+ [FIELD] ValueClass System.RuntimeTypeHandle
+
+!DumpSig will also work with generics. Here is the output for the following
+function:
+ public A Test(IEnumerable<B> n)
+
+ 0:000> !dumpsig 00000000`00bc2437 000007ff00043178
+ [DEFAULT] [hasThis] __Canon (Class System.Collections.Generic.IEnumerable`1<__Canon>)
+
+\\
+
+COMMAND: dumpsigelem.
+!DumpSigElem <sigaddr> <moduleaddr>
+
+This command dumps a single element of a signature object. For most circumstances,
+you should use !DumpSig to look at individual signature objects, but if you find a
+signature that has been corrupted in some manner you can use !DumpSigElem to read out
+the valid portions of it.
+
+If we look at a valid signature object for a method we see the following:
+ 0:000> !dumpsig 0x000007fe`ec20879d 0x000007fe`eabd1000
+ [DEFAULT] [hasThis] Void (Boolean,String,String)
+
+We can look at the individual elements of this object by adding the offsets into the
+object which correspond to the return value and parameters:
+ 0:000> !dumpsigelem 0x000007fe`ec20879d+2 0x000007fe`eabd1000
+ Void
+ 0:000> !dumpsigelem 0x000007fe`ec20879d+3 0x000007fe`eabd1000
+ Boolean
+ 0:000> !dumpsigelem 0x000007fe`ec20879d+4 0x000007fe`eabd1000
+ String
+ 0:000> !dumpsigelem 0x000007fe`ec20879d+5 0x000007fe`eabd1000
+ String
+
+We can do something similar for fields. Here is the full signature of a field:
+ 0:000> !dumpsig 0x000007fe`eb7fd8cd 0x000007fe`eabd1000
+ [FIELD] ValueClass System.RuntimeTypeHandle
+
+Using !DumpSigElem we can find the type of the field by adding the offset of it (1) to
+the address of the signature:
+ 0:000> !dumpsigelem 0x000007fe`eb7fd8cd+1 0x000007fe`eabd1000
+ ValueClass System.RuntimeTypeHandle
+
+!DumpSigElem will also work with generics. Let a function be defined as follows:
+ public A Test(IEnumerable<B> n)
+
+The elements of this signature can be obtained by adding offsets into the signature
+when calling !DumpSigElem:
+
+ 0:000> !dumpsigelem 00000000`00bc2437+2 000007ff00043178
+ __Canon
+ 0:000> !dumpsigelem 00000000`00bc2437+4 000007ff00043178
+ Class System.Collections.Generic.IEnumerable`1<__Canon>
+
+The actual offsets that you should add are determined by the contents of the
+signature itself. By trial and error you should be able to find various elements
+of the signature.
+
+\\
+
+COMMAND: rcwcleanuplist.
+!RCWCleanupList [address]
+
+A RuntimeCallableWrapper is an internal CLR structure used to host COM objects
+which are exposed to managed code. This is exposed to managed code through the
+System.__ComObject class, and when objects of this type are collected, and a
+reference to the underlying COM object is no longer needed, the corresponding
+RCW is cleaned up. If you are trying to debug an issue related to one of these
+RCWs, then you can use the !RCWCleanupList function to display which COM objects
+will be released the next time a cleanup occurs.
+
+If given an address, this function will display the RCWCleanupList at that address.
+If no address is specified, it displays the default cleanup list, printing the
+wrapper, the context, and the thread of the object.
+
+Example:
+ 0:002> !rcwcleanuplist 001c04d0
+ RuntimeCallableWrappers (RCW) to be cleaned:
+ RCW CONTEXT THREAD Apartment
+ 1d54e0 192008 181180 STA
+ 1d4140 192178 0 MTA
+ 1dff50 192178 0 MTA
+ MTA Interfaces to be released: 2
+ STA Interfaces to be released: 1
+
+Note that CLR keeps track of which RCWs are bound to which managed objects through
+the SyncBlock of the object. As such, you can see more information about RCW
+objects through the !SyncBlk command. You can find more information about RCW
+cleanup through the !FinalizeQueue command.
+
+\\
+
+COMMAND: dumpil.
+!DumpIL <Managed DynamicMethod object> |
+ <DynamicMethodDesc pointer> |
+ <MethodDesc pointer> |
+ /i <IL pointer>
+
+!DumpIL prints the IL code associated with a managed method. We added this
+function specifically to debug DynamicMethod code which was constructed on
+the fly. Happily it works for non-dynamic code as well.
+
+You can use it in four ways:
+
+ 1) If you have a System.Reflection.Emit.DynamicMethod object, just pass
+ the pointer as the first argument.
+ 2) If you have a DynamicMethodDesc pointer you can use that to print the
+ IL associated with the dynamic method.
+ 3) If you have an ordinary MethodDesc, you can see the IL for that as well,
+ just pass it as the first argument.
+ 4) If you have a pointer directly to the IL, specify /i followed by the
+ the IL address. This is useful for writers of profilers that instrument
+ IL.
+
+
+Note that dynamic IL is constructed a bit differently. Rather than referring
+to metadata tokens, the IL points to objects in a managed object array. Here
+is a simple example of the output for a dynamic method:
+
+ 0:000> !dumpil b741dc
+ This is dynamic IL. Exception info is not reported at this time.
+ If a token is unresolved, run "!do <addr>" on the addr given
+ in parenthesis. You can also look at the token table yourself, by
+ running "!DumpArray 00b77388".
+
+ IL_0000: ldstr 70000002 "Inside invoked method "
+ IL_0005: call 6000003 System.Console.WriteLine(System.String)
+ IL_000a: ldc.i4.1
+ IL_000b: newarr 2000004 "System.Int32"
+ IL_0010: stloc.0
+ IL_0011: ldloc.0
+ IL_0012: ret
+
+\\
+
+COMMAND: verifyheap.
+!VerifyHeap
+
+!VerifyHeap is a diagnostic tool that checks the garbage collected heap for
+signs of corruption. It walks objects one by one in a pattern like this:
+
+ o = firstobject;
+ while(o != endobject)
+ {
+ o.ValidateAllFields();
+ o = (Object *) o + o.Size();
+ }
+
+If an error is found, !VerifyHeap will report it. I'll take a perfectly good
+object and corrupt it:
+
+ 0:000> !DumpObj a79d40
+ Name: Customer
+ MethodTable: 009038ec
+ EEClass: 03ee1b84
+ Size: 20(0x14) bytes
+ (C:\pub\unittest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 009038ec 4000008 4 CLASS instance 00a79ce4 name
+ 009038ec 4000009 8 CLASS instance 00a79d2c bank
+ 009038ec 400000a c System.Boolean instance 1 valid
+
+ 0:000> ed a79d40+4 01 (change the name field to the bogus pointer value 1)
+ 0:000> !VerifyHeap
+ object 01ee60dc: bad member 00000003 at 01EE6168
+ Last good object: 01EE60C4.
+
+If this gc heap corruption exists, there is a serious bug in your own code or
+in the CLR. In user code, an error in constructing PInvoke calls can cause
+this problem, and running with Managed Debugging Assistants is advised. If that
+possibility is eliminated, consider contacting Microsoft Product Support for
+help.
+
+\\
+
+COMMAND: verifyobj.
+!VerifyObj <object address>
+
+!VerifyObj is a diagnostic tool that checks the object that is passed as an
+argument for signs of corruption.
+
+ 0:002> !verifyobj 028000ec
+ object 0x28000ec does not have valid method table
+
+ 0:002> !verifyobj 0680017c
+ object 0x680017c: bad member 00000001 at 06800184
+
+\\
+
+COMMAND: findroots.
+!FindRoots -gen <N> | -gen any | <object address>
+
+The "-gen" form causes the debugger to break in the debuggee on the next
+collection of the specified generation. The effect is reset as soon as the
+break occurs, in other words, if you need to break on the next collection you
+would need to reissue the command.
+
+The last form of this command is meant to be used after the break caused by the
+other forms has occurred. Now the debuggee is in the right state for
+!FindRoots to be able to identify roots for objects from the current condemned
+generations.
+
+!FindRoots is a diagnostic command that is meant to answer the following
+question:
+
+"I see that GCs are happening, however my objects have still not been
+collected. Why? Who is holding onto them?"
+
+The process of answering the question would go something like this:
+
+1. Find out the generation of the object of interest using the !GCWhere
+command, say it is gen 1:
+ !GCWhere <object address>
+
+2. Instruct the runtime to stop the next time it collects that generation using
+the !FindRoots command:
+ !FindRoots -gen 1
+ g
+
+3. When the next GC starts, and has proceeded past the mark phase a CLR
+notification will cause a break in the debugger:
+ (fd0.ec4): CLR notification exception - code e0444143 (first chance)
+ CLR notification: GC - end of mark phase.
+ Condemned generation: 1.
+
+4. Now we can use the !FindRoots <object address> to find out the cross
+generational references to the object of interest. In other words, even if the
+object is not referenced by any "proper" root it may still be referenced by an
+older object (from an older generation), from a generation that has not yet been
+scheduled for collection. At this point !FindRoots will search those older
+generations too, and report those roots.
+ 0:002> !findroots 06808094
+ older generations::Root: 068012f8(AAA.Test+a)->
+ 06808094(AAA.Test+b)
+
+
+\\
+
+COMMAND: heapstat.
+!HeapStat [-inclUnrooted | -iu]
+
+This command shows the generation sizes for each heap and the total, how much free
+space there is in each generation on each heap. If the -inclUnrooted option is
+specified the report will include information about the managed objects from the
+GC heap that are not rooted anymore.
+
+Sample output:
+
+ 0:002> !heapstat
+ Heap Gen0 Gen1 Gen2 LOH
+ Heap0 177904 12 306956 8784
+ Heap1 159652 12 12 16
+ Total 337556 24 306968 8800
+
+ Free space: Percentage
+ Heap0 28 12 12 64 SOH: 0% LOH: 0%
+ Heap1 104 12 12 16 SOH: 0% LOH:100%
+ Total 132 24 24 80
+
+ 0:002> !heapstat -inclUnrooted
+ Heap Gen0 Gen1 Gen2 LOH
+ Heap0 177904 12 306956 8784
+ Heap1 159652 12 12 16
+ Total 337556 24 306968 8800
+
+ Free space: Percentage
+ Heap0 28 12 12 64 SOH: 0% LOH: 0%
+ Heap1 104 12 12 16 SOH: 0% LOH:100%
+ Total 132 24 24 80
+
+ Unrooted objects: Percentage
+ Heap0 152212 0 306196 0 SOH: 94% LOH: 0%
+ Heap1 155704 0 0 0 SOH: 97% LOH: 0%
+ Total 307916 0 306196 0
+
+The percentage column contains a breakout of free or unrooted bytes to total bytes.
+
+\\
+
+COMMAND: analyzeoom.
+!AnalyzeOOM
+
+!AnalyzeOOM displays the info of the last OOM occurred on an allocation request to
+the GC heap (in Server GC it displays OOM, if any, on each GC heap).
+
+To see the managed exception(s) use the !Threads command which will show you
+managed exception(s), if any, on each managed thread. If you do see an
+OutOfMemoryException exception you can use the !PrintException command on it.
+To get the full callstack use the "kb" command in the debugger for that thread.
+For example, to display thread 3's stack use ~3kb.
+
+OOM exceptions could be because of the following reasons:
+
+1) allocation request to GC heap
+ in which case you will see JIT_New* on the call stack because managed code called new.
+2) other runtime allocation failure
+ for example, failure to expand the finalize queue when GC.ReRegisterForFinalize is
+ called.
+3) some other code you use throws a managed OOM exception
+ for example, some .NET framework code converts a native OOM exception to managed
+ and throws it.
+
+The !AnalyzeOOM command aims to help you with investigating 1) which is the most
+difficult because it requires some internal info from GC. The only exception is
+we don't support allocating objects larger than 2GB on CLR v2.0 or prior. And this
+command will not display any managed OOM because we will throw OOM right away
+instead of even trying to allocate it on the GC heap.
+
+There are 2 legitimate scenarios where GC would return OOM to allocation requests -
+one is if the process is running out of VM space to reserve a segment; the other
+is if the system is running out physical memory (+ page file if you have one) so
+GC can not commit memory it needs. You can look at these scenarios by using performance
+counters or debugger commands. For example for the former scenario the "!address
+-summary" debugger command will show you the largest free region in the VM. For
+the latter scenario you can look at the "Memory\% Committed Bytes In Use" see
+if you are running low on commit space. One important thing to keep in mind is
+when you do this kind of memory analysis it could an aftereffect and doesn't
+completely agree with what this command tells you, in which case the command should
+be respected because it truly reflects what happened during GC.
+
+The other cases should be fairly obvious from the callstack.
+
+Sample output:
+
+0:011> !ao
+---------Heap 2 ---------
+Managed OOM occurred after GC #28 (Requested to allocate 1234 bytes)
+Reason: Didn't have enough memory to commit
+Detail: SOH: Didn't have enough memory to grow the internal GC datastructures (800000 bytes) -
+ on GC entry available commit space was 500 MB
+---------Heap 4 ---------
+Managed OOM occurred after GC #12 (Requested to allocate 100000 bytes)
+Reason: Didn't have enough memory to allocate an LOH segment
+Detail: LOH: Failed to reserve memory (16777216 bytes)
+
+\\
+
+COMMAND: gcwhere.
+!GCWhere <object address>
+
+!GCWhere displays the location in the GC heap of the argument passed in.
+
+ 0:002> !GCWhere 02800038
+ Address Gen Heap segment begin allocated size
+ 02800038 2 0 02800000 02800038 0282b740 12
+
+When the argument lies in the managed heap, but is not a valid *object* address
+the "size" is displayed as 0:
+
+ 0:002> !GCWhere 0280003c
+ Address Gen Heap segment begin allocated size
+ 0280003c 2 0 02800000 02800038 0282b740 0
+
+\\
+
+COMMAND: listnearobj.
+!ListNearObj <object address>
+
+!ListNearObj is a diagnostic tool that displays the object preceeding and
+succeeding the address passed in:
+
+The command looks for the address in the GC heap that looks like a valid
+beginning of a managed object (based on a valid method table) and the object
+following the argument address.
+
+ 0:002> !ListNearObj 028000ec
+ Before: 0x28000a4 72 (0x48 ) System.StackOverflowException
+ After: 0x2800134 72 (0x48 ) System.Threading.ThreadAbortException
+ Heap local consistency confirmed.
+
+ 0:002> !ListNearObj 028000f0
+ Before: 0x28000ec 72 (0x48 ) System.ExecutionEngineException
+ After: 0x2800134 72 (0x48 ) System.Threading.ThreadAbortException
+ Heap local consistency confirmed.
+
+The command considers the heap as "locally consistent" if:
+ prev_obj_addr + prev_obj_size = arg_addr && arg_obj + arg_size = next_obj_addr
+OR
+ prev_obj_addr + prev_obj_size = next_obj_addr
+
+When the condition is not satisfied:
+
+ 0:002> !lno 028000ec
+ Before: 0x28000a4 72 (0x48 ) System.StackOverflowException
+ After: 0x2800134 72 (0x48 ) System.Threading.ThreadAbortException
+ Heap local consistency not confirmed.
+
+\\
+
+COMMAND: dumplog.
+!DumpLog [-addr <addressOfStressLog>] [<Filename>]
+
+To aid in diagnosing hard-to-reproduce stress failures, the CLR team added an
+in-memory log capability. The idea was to avoid using locks or I/O which could
+disturb a fragile repro environment. The !DumpLog function allows you to write
+that log out to a file. If no Filename is specified, the file "Stresslog.txt"
+in the current directory is created.
+
+The optional argument addr allows one to specify a stress log other then the
+default one.
+
+ 0:000> !DumpLog
+ Attempting to dump Stress log to file 'StressLog.txt'
+ .................
+ SUCCESS: Stress log dumped
+
+To turn on the stress log, set the following registry keys under
+HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework:
+
+
+(DWORD) StressLog = 1
+(DWORD) LogFacility = 0xffffffbf (this is a bit mask, almost all logging is on.
+ This is also the default value if the key
+ isn't specified)
+(DWORD) StressLogSize = 65536 (this is the default value if the key isn't
+ specified)
+(DWORD) LogLevel = 6 (this is the default value if the key isn't
+ specified. The higher the number the more
+ detailed logs are generated. The maximum
+ value is decimal 10)
+
+StressLogSize is the size in bytes of the in-memory log allocated for each
+thread in the process. In the case above, each thread gets a 64K log. You
+could increase this to get more logging, but more memory will be required for
+this log in the process. For example, 20 threads with 524288 bytes per thread
+has a memory demand of 10 Megabytes. The stress log is circular so new entries
+will replace older ones on threads which have reached their buffer limit.
+
+The log facilities are defined as follows:
+ GC 0x00000001
+ GCINFO 0x00000002
+ STUBS 0x00000004
+ JIT 0x00000008
+ LOADER 0x00000010
+ METADATA 0x00000020
+ SYNC 0x00000040
+ EEMEM 0x00000080
+ GCALLOC 0x00000100
+ CORDB 0x00000200
+ CLASSLOADER 0x00000400
+ CORPROF 0x00000800
+ REMOTING 0x00001000
+ DBGALLOC 0x00002000
+ EH 0x00004000
+ ENC 0x00008000
+ ASSERT 0x00010000
+ VERIFIER 0x00020000
+ THREADPOOL 0x00040000
+ GCROOTS 0x00080000
+ INTEROP 0x00100000
+ MARSHALER 0x00200000
+ IJW 0x00400000
+ ZAP 0x00800000
+ STARTUP 0x01000000
+ APPDOMAIN 0x02000000
+ CODESHARING 0x04000000
+ STORE 0x08000000
+ SECURITY 0x10000000
+ LOCKS 0x20000000
+ BCL 0x40000000
+
+Here is some sample output:
+
+ 3560 9.981137099 : `SYNC` RareEnablePremptiveGC: entering.
+ Thread state = a030
+
+ 3560 9.981135033 : `GC`GCALLOC`GCROOTS` ========== ENDGC 4194 (gen = 2,
+ collect_classes = 0) ==========={
+
+ 3560 9.981125826 : `GC` Segment mem 00C61000 alloc
+ = 00D071F0 used 00D09254 committed 00D17000
+
+ 3560 9.981125726 : `GC` Generation 0 [00CED07C, 00000000
+ ] cur = 00000000
+
+ 3560 9.981125529 : `GC` Generation 1 [00CED070, 00000000
+ ] cur = 00000000
+
+ 3560 9.981125103 : `GC` Generation 2 [00C61000, 00000000
+ ] cur = 00000000
+
+ 3560 9.981124963 : `GC` GC Heap 00000000
+
+ 3560 9.980618994 : `GC`GCROOTS` GcScanHandles (Promotion Phase = 0)
+
+The first column is the OS thread ID for the thread appending to the log,
+the second column is the timestamp, the third is the facility category for the
+log entry, and the fourth contains the log message. The facility field is
+expressed as `facility1`facility2`facility3`. This facilitates the creation of
+filters for displaying only specific message categories. To make sense of this
+log, you would probably want the Shared Source CLI to find out exactly where
+the log comes from.
+\\
+
+COMMAND: findappdomain.
+!FindAppDomain <Object address>
+
+!FindAppDomain will attempt to resolve the AppDomain of an object. For example,
+using an Object Pointer from the output of !DumpStackObjects:
+
+ 0:000> !findappdomain 00a79d98
+ AppDomain: 0014f000
+ Name: unittest.exe
+ ID: 1
+
+You can find out more about the AppDomain with the !DumpDomain command. Not
+every object has enough clues about it's origin to determine the AppDomain.
+Objects with Finalizers are the easiest case, as the CLR needs to be able to
+call those when an AppDomain shuts down.
+\\
+
+COMMAND: savemodule.
+!SaveModule <Base address> <Filename>
+
+This command allows you to take a image loaded in memory and write it to a
+file. This is especially useful if you are debugging a full memory dump, and
+don't have the original DLLs or EXEs. This is most often used to save a managed
+binary to a file, so you can disassemble the code and browse types with ILDASM.
+
+The base address of an image can be found with the "LM" debugger command:
+
+ 0:000> lm
+ start end module name
+ 00400000 00408000 image00400000 (deferred)
+ 10200000 102ac000 MSVCR80D (deferred)
+ 5a000000 5a0b1000 mscoree (deferred)
+ 5a140000 5a29e000 clrjit (deferred)
+ 5b660000 5c440000 mscorlib_dll (deferred)
+ 5d1d0000 5e13c000 clr (deferred)
+ ...
+
+If I wanted to save a copy of clr.dll, I could run:
+
+ 0:000> !SaveModule 5d1d0000 c:\pub\out.tmp
+ 4 sections in file
+ section 0 - VA=1000, VASize=e82da9, FileAddr=400, FileSize=e82e00
+ section 1 - VA=e84000, VASize=24d24, FileAddr=e83200, FileSize=ec00
+ section 2 - VA=ea9000, VASize=5a8, FileAddr=e91e00, FileSize=600
+ section 3 - VA=eaa000, VASize=c183c, FileAddr=e92400, FileSize=c1a00
+
+The diagnostic output indicates that the operation was successful. If
+c:\pub\out.tmp already exists, it will be overwritten.
+\\
+
+COMMAND: gchandles.
+!GCHandles [-type handletype] [-stat] [-perdomain]
+
+!GCHandles provides statistics about GCHandles in the process.
+
+Paremeters:
+ stat - Only display the statistics and not the list of handles and
+ what they point to.
+ perdomain - Break down the statistics by the app domain in which
+ the handles reside.
+ type - A type of handle to filter it by. The handle types are:
+ Pinned
+ RefCounted
+ WeakShort
+ WeakLong
+ Strong
+ Variable
+ AsyncPinned
+ SizedRef
+
+Sometimes the source of a memory leak is a GCHandle leak. For example, code
+might keep a 50 Megabyte array alive because a strong GCHandle points to it,
+and the handle was discarded without freeing it.
+
+The most common handles are "Strong Handles," which keep the object they point
+to alive until the handle is explicitly freed. "Pinned Handles" are used to
+prevent the garbage collector from moving an object during collection. These
+should be used sparingly, and for short periods of time. If you don't follow
+that precept, the gc heap can become very fragmented.
+
+Here is sample output from a very simple program. Note that the "RefCount"
+field only applies to RefCount Handles, and this field will contain the
+reference count:
+
+ 0:000> !GCHandles
+ Handle Type Object Size RefCount Type
+ 001611c0 Strong 01d00b58 84 System.IndexOutOfRangeException
+ 001611c4 Strong 01d00b58 84 System.IndexOutOfRangeException
+ 001611c8 Strong 01d1b48c 40 System.Diagnostics.LogSwitch
+ 001611d0 Strong 01cfd2c0 36 System.Security.PermissionSet
+ 001611d4 Strong 01cf7484 56 System.Object[]
+ 001611d8 Strong 01cf1238 32 System.SharedStatics
+ 001611dc Strong 01cf11c8 84 System.Threading.ThreadAbortException
+ 001611e0 Strong 01cf1174 84 System.Threading.ThreadAbortException
+ 001611e4 Strong 01cf1120 84 System.ExecutionEngineException
+ 001611e8 Strong 01cf10cc 84 System.StackOverflowException
+ 001611ec Strong 01cf1078 84 System.OutOfMemoryException
+ 001611f0 Strong 01cf1024 84 System.Exception
+ 001611f8 Strong 01cf2068 48 System.Threading.Thread
+ 001611fc Strong 01cf1328 112 System.AppDomain
+ 001613ec Pinned 02cf3268 8176 System.Object[]
+ 001613f0 Pinned 02cf2258 4096 System.Object[]
+ 001613f4 Pinned 02cf2038 528 System.Object[]
+ 001613f8 Pinned 01cf121c 12 System.Object
+ 001613fc Pinned 02cf1010 4116 System.Object[]
+
+ Statistics:
+ MT Count TotalSize Class Name
+ 563266dc 1 12 System.Object
+ 56329708 1 32 System.SharedStatics
+ 5632bc38 1 36 System.Security.PermissionSet
+ 5635f934 1 40 System.Diagnostics.LogSwitch
+ 5632759c 1 48 System.Threading.Thread
+ 5632735c 1 84 System.ExecutionEngineException
+ 56327304 1 84 System.StackOverflowException
+ 563272ac 1 84 System.OutOfMemoryException
+ 563270c4 1 84 System.Exception
+ 56328914 1 112 System.AppDomain
+ 56335f78 2 168 System.IndexOutOfRangeException
+ 563273b4 2 168 System.Threading.ThreadAbortException
+ 563208d0 5 16972 System.Object[]
+ Total 19 objects
+
+ Handles:
+ Strong Handles: 14
+ Pinned Handles: 5
+\\
+
+COMMAND: gchandleleaks.
+!GCHandleLeaks
+
+This command is an aid in tracking down GCHandle leaks. It searches all of
+memory for any references to the Strong and Pinned GCHandles in the process,
+and reports what it found. If a handle is found, you'll see the address of the
+reference. This might be a stack address or a field within an object, for
+example. If a handle is not found in memory, you'll get notification of that
+too.
+
+The command has diagnostic output which doesn't need to be repeated here. One
+thing to keep in mind is that anytime you search all of memory for a value, you
+can get false positives because even though the value was found, it might be
+garbage in that no code knows about the address. You can also get false
+negatives because a user is free to pass that GCHandle to unmanaged code that
+might store the handle in a strange way (shifting bits, for example).
+
+For example, a GCHandle valuetype is stored on the stack with the low bit set
+if it points to a Pinned handle. So !GCHandleLeaks ignores the low bit in it's
+searches.
+
+That said, if a serious leak is going on, you'll get a ever-growing set of
+handle addresses that couldn't be found.
+\\
+
+COMMAND: vmmap.
+!VMMap
+
+!VMMap traverses the virtual address space and lists the type of protection
+applied to each region. Sample output:
+
+ 0:000> !VMMap
+ Start Stop Length AllocProtect Protect State Type
+ 00000000-0000ffff 00010000 NA Free
+ 00010000-00011fff 00002000 RdWr RdWr Commit Private
+ 00012000-0001ffff 0000e000 NA Free
+ 00020000-00020fff 00001000 RdWr RdWr Commit Private
+ 00021000-0002ffff 0000f000 NA Free
+ 00030000-00030fff 00001000 RdWr Reserve Private
+ ...
+\\
+
+COMMAND: vmstat.
+!VMStat
+
+Provides a summary view of the virtual address space, ordered by each type of
+protection applied to that memory (free, reserved, committed, private, mapped,
+image). The TOTAL column is (AVERAGE * BLK COUNT). Sample output below:
+
+ 0:000> !VMStat
+ ~~~~ ~~~~~~~ ~~~~~~~ ~~~~~~~ ~~~~~~~~~ ~~~~~
+ TYPE MINIMUM MAXIMUM AVERAGE BLK COUNT TOTAL
+ Free:
+ Small 4,096 65,536 48,393 27 1,306,611
+ Medium 139,264 528,384 337,920 4 1,351,680
+ Large 6,303,744 974,778,368 169,089,706 12 2,029,076,472
+ Summary 4,096 974,778,368 47,249,646 43 2,031,734,778
+
+ Reserve:
+ Small 4,096 65,536 43,957 41 1,802,237
+ Medium 249,856 1,019,904 521,557 6 3,129,342
+ Large 2,461,696 16,703,488 11,956,224 3 35,868,672
+ Summary 4,096 16,703,488 816,005 50 40,800,250
+
+\\
+
+COMMAND: procinfo.
+!ProcInfo [-env] [-time] [-mem]
+
+!ProcInfo lists the environment variables for the process, kernel CPU time, as
+well as memory usage statistics.
+\\
+
+COMMAND: histinit.
+!HistInit
+
+Before running any of the Hist - family commands you need to initialize the SOS
+structures from the stress log saved in the debuggee. This is achieved by the
+HistInit command.
+
+Sample output:
+
+ 0:001> !HistInit
+ Attempting to read Stress log
+ STRESS LOG:
+ facilitiesToLog = 0xffffffff
+ levelToLog = 6
+ MaxLogSizePerThread = 0x10000 (65536)
+ MaxTotalLogSize = 0x1000000 (16777216)
+ CurrentTotalLogChunk = 9
+ ThreadsWithLogs = 3
+ Clock frequency = 3.392 GHz
+ Start time 15:26:31
+ Last message time 15:26:56
+ Total elapsed time 25.077 sec
+ .....................................
+ ---------------------------- 2407 total entries -----------------------------
+
+
+ SUCCESS: GCHist structures initialized
+
+\\
+
+COMMAND: histobjfind.
+!HistObjFind <obj_address>
+
+To examine log entries related to an object whose present address is known one
+would use this command. The output of this command contains all entries that
+reference the object:
+
+ 0:003> !HistObjFind 028970d4
+ GCCount Object Message
+ ---------------------------------------------------------
+ 2296 028970d4 Promotion for root 01e411b8 (MT = 5b6c5cd8)
+ 2296 028970d4 Relocation NEWVALUE for root 00223fc4
+ 2296 028970d4 Relocation NEWVALUE for root 01e411b8
+ ...
+ 2295 028970d4 Promotion for root 01e411b8 (MT = 5b6c5cd8)
+ 2295 028970d4 Relocation NEWVALUE for root 00223fc4
+ 2295 028970d4 Relocation NEWVALUE for root 01e411b8
+ ...
+
+\\
+
+COMMAND: histroot.
+!HistRoot <root>
+
+The root value obtained from !HistObjFind can be used to track the movement of
+an object through the GCs.
+
+HistRoot provides information related to both promotions and relocations of the
+root specified as the argument.
+
+ 0:003> !HistRoot 01e411b8
+ GCCount Value MT Promoted? Notes
+ ---------------------------------------------------------
+ 2296 028970d4 5b6c5cd8 yes
+ 2295 028970d4 5b6c5cd8 yes
+ 2294 028970d4 5b6c5cd8 yes
+ 2293 028970d4 5b6c5cd8 yes
+ 2292 028970d4 5b6c5cd8 yes
+ 2291 028970d4 5b6c5cd8 yes
+ 2290 028970d4 5b6c5cd8 yes
+ 2289 028970d4 5b6c5cd8 yes
+ 2288 028970d4 5b6c5cd8 yes
+ 2287 028970d4 5b6c5cd8 yes
+ 2286 028970d4 5b6c5cd8 yes
+ 2285 028970d4 5b6c5cd8 yes
+ 322 028970e8 5b6c5cd8 yes Duplicate promote/relocs
+ ...
+
+\\
+
+COMMAND: histobj.
+!HistObj <obj_address>
+
+This command examines all stress log relocation records and displays the chain
+of GC relocations that may have led to the address passed in as an argument.
+Conceptually the output is:
+
+ GenN obj_address root1, root2, root3,
+ GenN-1 prev_obj_addr root1, root2,
+ GenN-2 prev_prev_oa root1, root4,
+ ...
+
+Sample output:
+ 0:003> !HistObj 028970d4
+ GCCount Object Roots
+ ---------------------------------------------------------
+ 2296 028970d4 00223fc4, 01e411b8,
+ 2295 028970d4 00223fc4, 01e411b8,
+ 2294 028970d4 00223fc4, 01e411b8,
+ 2293 028970d4 00223fc4, 01e411b8,
+ 2292 028970d4 00223fc4, 01e411b8,
+ 2291 028970d4 00223fc4, 01e411b8,
+ 2290 028970d4 00223fc4, 01e411b8,
+ 2289 028970d4 00223fc4, 01e411b8,
+ 2288 028970d4 00223fc4, 01e411b8,
+ 2287 028970d4 00223fc4, 01e411b8,
+ 2286 028970d4 00223fc4, 01e411b8,
+ 2285 028970d4 00223fc4, 01e411b8,
+ 322 028970d4 01e411b8,
+ 0 028970d4
+
+\\
+
+COMMAND: histclear.
+!HistClear
+
+This command releases any resources used by the Hist-family of commands.
+Generally there's no need to call this explicitly, as each HistInit will first
+cleanup the previous resources.
+
+ 0:003> !HistClear
+ Completed successfully.
+
+\\
+
+COMMAND: dumprcw.
+!DumpRCW <RCW address>
+
+This command lists information about a Runtime Callable Wrapper. You can use
+!DumpObj to obtain the RCW address corresponding to a managed object.
+
+The output contains all COM interface pointers that the RCW holds on to, which
+is useful for investigating lifetime issues of interop-heavy applications.
+\\
+
+COMMAND: dumpccw.
+!DumpCCW <CCW address or COM IP>
+
+This command lists information about a COM Callable Wrapper. You can use
+!DumpObj to obtain the CCW address corresponding to a managed object or pass
+a COM interface pointer to which the object has been marshaled.
+
+The output contains the COM reference count of the CCW, which is useful for
+investigating lifetime issues of interop-heavy applications.
+\\
+
+
diff --git a/src/ToolBox/SOS/Strike/sosdocsunix.txt b/src/ToolBox/SOS/Strike/sosdocsunix.txt
new file mode 100644
index 0000000000..52ec86dc4e
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/sosdocsunix.txt
@@ -0,0 +1,1713 @@
+-------------------------------------------------------------------------------
+NOTE: THIS FILE CONTAINS SOS DOCUMENTATION. THE FORMAT OF THE FILE IS:
+
+<optional comments>
+COMMAND: <cmd name, all lower case>
+<descriptive text of the command>
+\\ <these are two backslashes, immediately followed by a newline>
+
+<repeat the sequence above>
+
+The first command is "contents" which is the general help screen. The rest
+correspond to SOS command names. This file is embedded as a resource in the SOS
+binary. Be sure to list any new commands here.
+-------------------------------------------------------------------------------
+
+
+
+COMMAND: contents.
+SOS is a debugger extension DLL designed to aid in the debugging of managed
+programs. Functions are listed by category, then roughly in order of
+importance. Shortcut names for popular functions are listed in parenthesis.
+Type "soshelp <functionname>" for detailed info on that function.
+
+Object Inspection Examining code and stacks
+----------------------------- -----------------------------
+DumpObj (dumpobj) Threads (clrthreads)
+DumpArray ThreadState
+DumpStackObjects (dso) IP2MD (ip2md)
+DumpHeap (dumpheap) u (clru)
+DumpVC DumpStack (dumpstack)
+GCRoot (gcroot) EEStack (eestack)
+PrintException (pe) ClrStack (clrstack)
+ GCInfo
+ EHInfo
+ bpmd (bpmd)
+
+Examining CLR data structures Diagnostic Utilities
+----------------------------- -----------------------------
+DumpDomain VerifyHeap
+EEHeap (eeheap) FindAppDomain
+Name2EE (name2ee) DumpLog (dumplog)
+DumpMT (dumpmt)
+DumpClass (dumpclass)
+DumpMD (dumpmd)
+Token2EE
+DumpModule (dumpmodule)
+DumpAssembly
+DumpRuntimeTypes
+DumpIL (dumpil)
+DumpSig
+DumpSigElem
+
+Examining the GC history Other
+----------------------------- -----------------------------
+HistInit (histinit) FAQ
+HistRoot (histroot) Help (soshelp)
+HistObj (histobj)
+HistObjFind (histobjfind)
+HistClear (histclear)
+\\
+
+COMMAND: faq.
+>> Where can I get the right version of SOS for my build?
+
+If you are running a xplat version of coreclr, the sos module (exact name
+is platform dependent) is installed in the same directory as the main coreclr
+module. There is also an lldb sos plugin command that allows the path where
+the sos, dac and dbi modules are loaded:
+
+ "setsospath /home/user/coreclr/bin/Product/Linux.x64.Debug""
+
+If you are using a dump file created on another machine, it is a little bit
+more complex. You need to make sure the dac module that came with that install
+is in the directory set with the above command.
+
+>> I have a chicken and egg problem. I want to use SOS commands, but the CLR
+ isn't loaded yet. What can I do?
+
+TBD
+
+>> I got the following error message. Now what?
+
+
+ (lldb) sos DumpStackObjects
+ The coreclr module is not loaded yet in the target process
+ (lldb)
+
+This means that the clr is not loaded yet, or has been unloaded. You need to
+wait until your managed program is running in order to use these commands. If
+you have just started the program a good way to do this is to type
+
+ breakpoint set coreclr`EEStartup
+
+in the debugger, and let it run. After the function EEStartup is finished,
+there will be a minimal managed environment for executing SOS commands.
+
+\\
+
+COMMAND: dumpobj.
+DumpObj [-nofields] <object address>
+
+This command allows you to examine the fields of an object, as well as learn
+important properties of the object such as the EEClass, the MethodTable, and
+the size.
+
+You might find an object pointer by running DumpStackObjects and choosing
+from the resultant list. Here is a simple object:
+
+ (lldb) dumpobj a79d40
+ Name: Customer
+ MethodTable: 009038ec
+ EEClass: 03ee1b84
+ Size: 20(0x14) bytes
+ (/home/user/pub/unittest)
+ Fields:
+ MT Field Offset Type VT Attr Value Name
+ 009038ec 4000008 4 Customer 0 instance 00a79ce4 name
+ 009038ec 4000009 8 Bank 0 instance 00a79d2c bank
+
+Note that fields of type Customer and Bank are themselves objects, and you can
+run DumpObj on them too. You could look at the field directly in memory using
+the offset given. "dd a79d40+8 l1" would allow you to look at the bank field
+directly. Be careful about using this to set memory breakpoints, since objects
+can move around in the garbage collected heap.
+
+What else can you do with an object? You might run GCRoot, to determine what
+roots are keeping it alive. Or you can find all objects of that type with
+"dumpheap -type Customer".
+
+The column VT contains the value 1 if the field is a valuetype structure, and
+0 if the field contains a pointer to another object. For valuetypes, you can
+take the MethodTable pointer in the MT column, and the Value and pass them to
+the command DumpVC.
+
+The arguments in detail:
+-nofields: do not print fields of the object, useful for objects like String
+\\
+
+COMMAND: dumparray.
+DumpArray
+ [-start <startIndex>]
+ [-length <length>]
+ [-details]
+ [-nofields]
+ <array object address>
+
+This command allows you to examine elements of an array object.
+The arguments in detail:
+ -start <startIndex>: optional, only supported for single dimension array.
+ Specify from which index the command shows the elements.
+ -length <length>: optional, only supported for single dimension array.
+ Specify how many elements to show.
+ -details: optional. Ask the command to print out details
+ of the element using DumpObj and DumpVC format.
+ -nofields: optional, only takes effect when -details is used. Do
+ not print fields of the elements. Useful for arrays of
+ objects like String
+
+ Example output:
+
+ (lldb) sos DumpArray -start 2 -length 3 -details 00ad28d0
+ Name: Value[]
+ MethodTable: 03e41044
+ EEClass: 03e40fc0
+ Size: 132(0x84) bytes
+ Array: Rank 1, Number of elements 10, Type VALUETYPE
+ Element Type: Value
+ [2] 00ad28f0
+ Name: Value
+ MethodTable 03e40f4c
+ EEClass: 03ef1698
+ Size: 20(0x14) bytes
+ (/home/user/bugs/225271/arraytest)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 5b9a628c 4000001 0 System.Int32 instance 2 x
+ 5b9a628c 4000002 4 System.Int32 instance 4 y
+ 5b9a628c 4000003 8 System.Int32 instance 6 z
+ [3] 00ad28fc
+ Name: Value
+ MethodTable 03e40f4c
+ EEClass: 03ef1698
+ Size: 20(0x14) bytes
+ (/home/user/bugs/225271/arraytest)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 5b9a628c 4000001 0 System.Int32 instance 3 x
+ 5b9a628c 4000002 4 System.Int32 instance 6 y
+ 5b9a628c 4000003 8 System.Int32 instance 9 z
+ [4] 00ad2908
+ Name: Value
+ MethodTable 03e40f4c
+ EEClass: 03ef1698
+ Size: 20(0x14) bytes
+ (/home/user/bugs/225271/arraytest.exe)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 5b9a628c 4000001 0 System.Int32 instance 4 x
+ 5b9a628c 4000002 4 System.Int32 instance 8 y
+ 5b9a628c 4000003 8 System.Int32 instance 12 z
+
+
+\\
+
+COMMAND: dumpstackobjects.
+DumpStackObjects [-verify] [top stack [bottom stack]]
+
+This command will display any managed objects it finds within the bounds of
+the current stack. Combined with the stack tracing commands like K and
+CLRStack, it is a good aid to determining the values of locals and
+parameters.
+
+If you use the -verify option, each non-static CLASS field of an object
+candidate is validated. This helps to eliminate false positives. It is not
+on by default because very often in a debugging scenario, you are
+interested in objects with invalid fields.
+
+The abbreviation dso can be used for brevity.
+\\
+
+COMMAND: dumpheap.
+DumpHeap [-stat]
+ [-strings]
+ [-short]
+ [-min <size>]
+ [-max <size>]
+ [-live]
+ [-dead]
+ [-thinlock]
+ [-startAtLowerBound]
+ [-mt <MethodTable address>]
+ [-type <partial type name>]
+ [start [end]]
+
+DumpHeap is a powerful command that traverses the garbage collected heap,
+collection statistics about objects. With it's various options, it can look for
+particular types, restrict to a range, or look for ThinLocks (see SyncBlk
+documentation). Finally, it will provide a warning if it detects excessive
+fragmentation in the GC heap.
+
+When called without options, the output is first a list of objects in the heap,
+followed by a report listing all the types found, their size and number:
+
+ (lldb) dumpheap
+ Address MT Size
+ 00a71000 0015cde8 12 Free
+ 00a7100c 0015cde8 12 Free
+ 00a71018 0015cde8 12 Free
+ 00a71024 5ba58328 68
+ 00a71068 5ba58380 68
+ 00a710ac 5ba58430 68
+ 00a710f0 5ba5dba4 68
+ ...
+ total 619 objects
+ Statistics:
+ MT Count TotalSize Class Name
+ 5ba7607c 1 12 System.Security.Permissions.HostProtectionResource
+ 5ba75d54 1 12 System.Security.Permissions.SecurityPermissionFlag
+ 5ba61f18 1 12 System.Collections.CaseInsensitiveComparer
+ ...
+ 0015cde8 6 10260 Free
+ 5ba57bf8 318 18136 System.String
+ ...
+
+"Free" objects are simply regions of space the garbage collector can use later.
+If 30% or more of the heap contains "Free" objects, the process may suffer from
+heap fragmentation. This is usually caused by pinning objects for a long time
+combined with a high rate of allocation. Here is example output where DumpHeap
+provides a warning about fragmentation:
+
+ <After the Statistics section>
+ Fragmented blocks larger than 1MB:
+ Addr Size Followed by
+ 00a780c0 1.5MB 00bec800 System.Byte[]
+ 00da4e38 1.2MB 00ed2c00 System.Byte[]
+ 00f16df0 1.2MB 01044338 System.Byte[]
+
+The arguments in detail:
+
+-stat Restrict the output to the statistical type summary
+-strings Restrict the output to a statistical string value summary
+-short Limits output to just the address of each object. This allows you
+ to easily pipe output from the command to another debugger
+ command for automation.
+-min Ignore objects less than the size given in bytes
+-max Ignore objects larger than the size given in bytes
+-live Only print live objects
+-dead Only print dead objects (objects which will be collected in the
+ next full GC)
+-thinlock Report on any ThinLocks (an efficient locking scheme, see SyncBlk
+ documentation for more info)
+-startAtLowerBound
+ Force heap walk to begin at lower bound of a supplied address range.
+ (During plan phase, the heap is often not walkable because objects
+ are being moved. In this case, DumpHeap may report spurious errors,
+ in particular bad objects. It may be possible to traverse more of
+ the heap after the reported bad object. Even if you specify an
+ address range, DumpHeap will start its walk from the beginning of
+ the heap by default. If it finds a bad object before the specified
+ range, it will stop before displaying the part of the heap in which
+ you are interested. This switch will force DumpHeap to begin its
+ walk at the specified lower bound. You must supply the address of a
+ good object as the lower bound for this to work. Display memory at
+ the address of the bad object to manually find the next method
+ table (use DumpMT to verify). If the GC is currently in a call to
+ memcopy, You may also be able to find the next object's address by
+ adding the size to the start address given as parameters.)
+-mt List only those objects with the MethodTable given
+-type List only those objects whose type name is a substring match of the
+ string provided.
+start Begin listing from this address
+end Stop listing at this address
+
+A special note about -type: Often, you'd like to find not only Strings, but
+System.Object arrays that are constrained to contain Strings. ("new
+String[100]" actually creates a System.Object array, but it can only hold
+System.String object pointers). You can use -type in a special way to find
+these arrays. Just pass "-type System.String[]" and those Object arrays will
+be returned. More generally, "-type <Substring of interesting type>[]".
+
+The start/end parameters can be obtained from the output of eeheap -gc. For
+example, if you only want to list objects in the large heap segment:
+
+ (lldb) eeheap -gc
+ Number of GC Heaps: 1
+ generation 0 starts at 0x00c32754
+ generation 1 starts at 0x00c32748
+ generation 2 starts at 0x00a71000
+ segment begin allocated size
+ 00a70000 00a71000 010443a8 005d33a8(6108072)
+ Large object heap starts at 0x01a71000
+ segment begin allocated size
+ 01a70000 01a71000 01a75000 0x00004000(16384)
+ Total Size 0x5d73a8(6124456)
+ ------------------------------
+ GC Heap Size 0x5d73a8(6124456)
+
+ (lldb) dumpheap 1a71000 1a75000
+ Address MT Size
+ 01a71000 5ba88bd8 2064
+ 01a71810 0019fe48 2032 Free
+ 01a72000 5ba88bd8 4096
+ 01a73000 0019fe48 4096 Free
+ 01a74000 5ba88bd8 4096
+ total 5 objects
+ Statistics:
+ MT Count TotalSize Class Name
+ 0019fe48 2 6128 Free
+ 5ba88bd8 3 10256 System.Object[]
+ Total 5 objects
+
+Finally, if GC heap corruption is present, you may see an error like this:
+
+ (lldb) dumpheap -stat
+ object 00a73d24: does not have valid MT
+ curr_object : 00a73d24
+ Last good object: 00a73d14
+ ----------------
+
+That indicates a serious problem. See the help for VerifyHeap for more
+information on diagnosing the cause.
+\\
+
+COMMAND: dumpvc.
+DumpVC <MethodTable address> <Address>
+
+DumpVC allows you to examine the fields of a value class. In C#, this is a
+struct, and lives on the stack or within an Object on the GC heap. You need
+to know the MethodTable address to tell SOS how to interpret the fields, as
+a value class is not a first-class object with it's own MethodTable as the
+first field. For example:
+
+ (lldb) sos DumpObj a79d98
+ Name: Mainy
+ MethodTable: 009032d8
+ EEClass: 03ee1424
+ Size: 28(0x1c) bytes
+ (/home/user/pub/unittest)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 0090320c 4000010 4 VALUETYPE instance 00a79d9c m_valuetype
+ 009032d8 400000f 4 CLASS static 00a79d54 m_sExcep
+
+m_valuetype is a value type. The value in the MT column (0090320c) is the
+MethodTable for it, and the Value column provides the start address:
+
+ (lldb) sos DumpVC 0090320c 00a79d9c
+ Name: Funny
+ MethodTable 0090320c
+ EEClass: 03ee14b8
+ Size: 28(0x1c) bytes
+ (/home/user/pub/unittest)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 0090320c 4000001 0 CLASS instance 00a743d8 signature
+ 0090320c 4000002 8 System.Int32 instance 2345 m1
+ 0090320c 4000003 10 System.Boolean instance 1 b1
+ 0090320c 4000004 c System.Int32 instance 1234 m2
+ 0090320c 4000005 4 CLASS instance 00a79d98 backpointer
+
+DumpVC is quite a specialized function. Some managed programs make heavy use
+of value classes, while others do not.
+\\
+
+COMMAND: gcroot.
+GCRoot [-nostacks] <Object address>
+
+GCRoot looks for references (or roots) to an object. These can exist in four
+places:
+
+ 1. On the stack
+ 2. Within a GC Handle
+ 3. In an object ready for finalization
+ 4. As a member of an object found in 1, 2 or 3 above.
+
+First, all stacks will be searched for roots, then handle tables, and finally
+the freachable queue of the finalizer. Some caution about the stack roots:
+GCRoot doesn't attempt to determine if a stack root it encountered is valid
+or is old (discarded) data. You would have to use CLRStack and U to
+disassemble the frame that the local or argument value belongs to in order to
+determine if it is still in use.
+
+Because people often want to restrict the search to gc handles and freachable
+objects, there is a -nostacks option.
+\\
+
+COMMAND: pe.
+COMMAND: printexception.
+PrintException [-nested] [-lines] [-ccw] [<Exception object address>] [<CCW pointer>]
+
+This will format fields of any object derived from System.Exception. One of the
+more useful aspects is that it will format the _stackTrace field, which is a
+binary array. If _stackTraceString field is not filled in, that can be helpful
+for debugging. You can of course use DumpObj on the same exception object to
+explore more fields.
+
+If called with no parameters, PrintException will look for the last outstanding
+exception on the current thread and print it. This will be the same exception
+that shows up in a run of clrthreads.
+
+PrintException will notify you if there are any nested exceptions on the
+current managed thread. (A nested exception occurs when you throw another
+exception within a catch handler already being called for another exception).
+If there are nested exceptions, you can re-run PrintException with the
+"-nested" option to get full details on the nested exception objects. The
+clrthreads command will also tell you which threads have nested exceptions.
+
+PrintException can display source information if available, by specifying the
+-lines command line argument.
+
+PrintException prints the exception object corresponding to a given CCW pointer,
+which can be specified using the -ccw option.
+
+The abbreviation 'pe' can be used for brevity.
+\\
+
+COMMAND: threadstate.
+ThreadState value
+
+The clrthreads command outputs, among other things, the state of the thread.
+This is a bit field which corresponds to various states the thread is in.
+To check the state of the thread, simply pass that bit field from the
+output of clrthreads into ThreadState.
+
+Example:
+ (lldb) clrthreads
+ ThreadCount: 2
+ UnstartedThread: 0
+ BackgroundThread: 1
+ PendingThread: 0
+ DeadThread: 0
+ Hosted Runtime: no
+ PreEmptive GC Alloc Lock
+ ID OSID ThreadOBJ State GC Context Domain Count APT Exception
+ 0 1 250 0019b068 a020 Disabled 02349668:02349fe8 0015def0 0 MTA
+ 2 2 944 001a6020 b220 Enabled 00000000:00000000 0015def0 0 MTA (Finalizer)
+ 0:003> sos ThreadState b220
+ Legal to Join
+ Background
+ CLR Owns
+ CoInitialized
+ In Multi Threaded Apartment
+
+Possible thread states:
+ Thread Abort Requested
+ GC Suspend Pending
+ User Suspend Pending
+ Debug Suspend Pending
+ GC On Transitions
+ Legal to Join
+ Yield Requested
+ Hijacked by the GC
+ Blocking GC for Stack Overflow
+ Background
+ Unstarted
+ Dead
+ CLR Owns
+ CoInitialized
+ In Single Threaded Apartment
+ In Multi Threaded Apartment
+ Reported Dead
+ Fully initialized
+ Task Reset
+ Sync Suspended
+ Debug Will Sync
+ Stack Crawl Needed
+ Suspend Unstarted
+ Aborted
+ Thread Pool Worker Thread
+ Interruptible
+ Interrupted
+ Completion Port Thread
+ Abort Initiated
+ Finalized
+ Failed to Start
+ Detached
+\\
+COMMAND: threads.
+COMMAND: clrthreads.
+Threads [-live] [-special]
+
+Threads (clrthreads) lists all the mananaged threads in the process.
+
+-live: optional. Only print threads associated with a live thread.
+-special: optional. With this switch, the command will display all the special
+ threads created by CLR. Those threads might not be managed threads
+ so they might not be shown in the first part of the command's
+ output. Example of special threads include: GC threads (in
+ concurrent GC and server GC), Debugger helper threads, Finalizer
+ threads, AppDomain Unload threads, and Threadpool timer threads.
+
+Each thread has many attributes, many of which can be ignored. The important
+ones are discussed below:
+
+There are three ID columns:
+
+1) The debugger shorthand ID (When the runtime is hosted this column might
+ display the special string "<<<<" when this internal thread object is not
+ associated with any physical thread - this may happen when the host reuses
+ the runtime internal thread object)
+2) The CLR Thread ID
+3) The OS thread ID.
+
+If PreEmptiveGC is enabled for a thread, then a garbage collection
+can occur while that thread is running. For example, if you break in while
+a managed thread is making a PInvoke call to a Win32 function, that thread
+will be in PreEmptive GC mode.
+
+The Domain column indicates what AppDomain the thread is currently executing
+in. You can pass this value to DumpDomain to find out more.
+
+The APT column gives the COM apartment mode.
+
+Exception will list the last thrown exception (if any) for the thread. More
+details can be obtained by passing the pointer value to PrintException. If
+you get the notation "(nested exceptions)", you can get details on those
+exceptions by switching to the thread in question, and running
+"PrintException -nested".
+\\
+
+COMMAND: clrstack.
+CLRStack [-a] [-l] [-p] [-n] [-f]
+CLRStack [-a] [-l] [-p] [-i] [variable name] [frame]
+
+CLRStack attempts to provide a true stack trace for managed code only. It is
+handy for clean, simple traces when debugging straightforward managed
+programs. The -p parameter will show arguments to the managed function. The
+-l parameter can be used to show information on local variables in a frame.
+SOS can't retrieve local names at this time, so the output for locals is in
+the format <local address> = <value>. The -a (all) parameter is a short-cut
+for -l and -p combined.
+
+The -f option (full mode) displays the native frames intermixing them with
+the managed frames and the assembly name and function offset for the managed
+frames.
+
+If the debugger has the option SYMOPT_LOAD_LINES specified (either by the
+.lines or .symopt commands), SOS will look up the symbols for every managed
+frame and if successful will display the corresponding source file name and
+line number. The -n (No line numbers) parameter can be specified to disable
+this behavior.
+
+When you see methods with the name "[Frame:...", that indicates a transition
+between managed and unmanaged code. You could run IP2MD on the return
+addresses in the call stack to get more information on each managed method.
+
+On x64 platforms, Transition Frames are not displayed at this time. To avoid
+heavy optimization of parameters and locals one can request the JIT compiler
+to not optimize functions in the managed app by creating a file myapp.ini
+(if your program is myapp.exe) in the same directory. Put the following lines
+in myapp.ini and re-run:
+
+[.NET Framework Debugging Control]
+GenerateTrackingInfo=1
+AllowOptimize=0
+
+The -i option is a new EXPERIMENTAL addition to CLRStack and will use the ICorDebug
+interfaces to display the managed stack and variables. With this option you can also
+view and expand arrays and fields for managed variables. If a stack frame number is
+specified in the command line, CLRStack will show you the parameters and/or locals
+only for that frame (provided you specify -l or -p or -a of course). If a variable
+name and a stack frame number are specified in the command line, CLRStack will show
+you the parameters and/or locals for that frame, and will also show you the fields
+for that variable name you specified. Here are some examples:
+ clrstack -i -a : This will show you all parameters and locals for all frames
+ clrstack -i -a 3 : This will show you all parameters and locals, for frame 3
+ clrstack -i var1 0 : This will show you the fields of 'var1' for frame 0
+ clrstack -i var1.abc 2 : This will show you the fields of 'var1', and expand
+ 'var1.abc' to show you the fields of the 'abc' field,
+ for frame 2.
+ clrstack -i var1.[basetype] 0 : This will show you the fields of 'var1', and
+ expand the base type of 'var1' to show you its
+ fields.
+ clrstack -i var1.[6] 0 : If 'var1' is an array, this will show you the element
+ at index 6 in the array, along with its fields
+The -i options uses DML output for a better debugging experience, so typically you
+should only need to execute "clrstack -i", and from there, click on the DML
+hyperlinks to inspect the different managed stack frames and managed variables.
+\\
+
+COMMAND: ip2md.
+IP2MD <Code address>
+
+Given an address in managed JITTED code, IP2MD attempts to find the MethodDesc
+associated with it. For example, this output from K:
+
+ (lldb) bt
+ ...
+ frame #9: 0x00007fffffffbf60 0x00007ffff61c6d89 libcoreclr.so`MethodDesc::DoPrestub(this=0x00007ffff041f870, pDispatchingMT=0x0000000000000000) + 3001 at prestub.cpp:1490
+ frame #10: 0x00007fffffffc140 0x00007ffff61c5f17 libcoreclr.so`::PreStubWorker(pTransitionBlock=0x00007fffffffc9a8, pMD=0x00007ffff041f870) + 1399 at prestub.cpp:1037
+ frame #11: 0x00007fffffffc920 0x00007ffff5f5238c libcoreclr.so`ThePreStub + 92 at theprestubamd64.S:800
+ frame #12: 0x00007fffffffca10 0x00007ffff04981cc
+ frame #13: 0x00007fffffffca30 0x00007ffff049773c
+ frame #14: 0x00007fffffffca80 0x00007ffff04975ad
+ ...
+ frame #22: 0x00007fffffffcc90 0x00007ffff5f51a0f libcoreclr.so`CallDescrWorkerInternal + 124 at calldescrworkeramd64.S:863
+ frame #23: 0x00007fffffffccb0 0x00007ffff5d6d6dc libcoreclr.so`CallDescrWorkerWithHandler(pCallDescrData=0x00007fffffffce80, fCriticalCall=0) + 476 at callhelpers.cpp:88
+ frame #24: 0x00007fffffffcd00 0x00007ffff5d6eb38 libcoreclr.so`MethodDescCallSite::CallTargetWorker(this=0x00007fffffffd0c8, pArguments=0x00007fffffffd048) + 2504 at callhelpers.cpp:633
+
+ (lldb) ip2md 0x00007ffff049773c
+ MethodDesc: 00007ffff7f71920
+ Method Name: Microsoft.Win32.SafeHandles.SafeFileHandle.Open(System.Func`1<Int32>)
+ Class: 00007ffff0494bf8
+ MethodTable: 00007ffff7f71a58
+ mdToken: 0000000006000008
+ Module: 00007ffff7f6b938
+ IsJitted: yes
+ CodeAddr: 00007ffff04976c0
+ Transparency: Critical
+
+We have taken a return address into Mainy.Main, and discovered information
+about that method. You could run U, DumpMT, DumpClass, DumpMD, or
+DumpModule on the fields listed to learn more.
+
+The "Source line" output will only be present if the debugger can find the
+symbols for the managed module containing the given <code address>, and if the
+debugger is configured to load line number information.
+\\
+
+COMMAND: clru.
+COMMAND: u.
+U [-gcinfo] [-ehinfo] [-n] [-o] <MethodDesc address> | <Code address>
+
+Presents an annotated disassembly of a managed method when given a MethodDesc
+pointer for the method, or a code address within the method body. Unlike the
+debugger "U" function, the entire method from start to finish is printed,
+with annotations that convert metadata tokens to names.
+
+ <example output>
+ ...
+ 03ef015d b901000000 mov ecx,0x1
+ 03ef0162 ff156477a25b call dword ptr [mscorlib_dll+0x3c7764 (5ba27764)] (System.Console.InitializeStdOutError(Boolean), mdToken: 06000713)
+ 03ef0168 a17c20a701 mov eax,[01a7207c] (Object: SyncTextWriter)
+ 03ef016d 89442414 mov [esp+0x14],eax
+
+If you pass the -gcinfo flag, you'll get inline display of the GCInfo for
+the method. You can also obtain this information with the GCInfo command.
+
+If you pass the -ehinfo flag, you'll get inline display of exception info
+for the method. (Beginning and end of try/finally/catch handlers, etc.).
+You can also obtain this information with the EHInfo command.
+
+If you pass the -o flag, the byte offset of each instruction from the
+beginning of the method will be printed in addition to the absolute address of
+the instruction.
+
+If the debugger has the option SYMOPT_LOAD_LINES specified (either by the
+.lines or .symopt commands), and if symbols are available for the managed
+module containing the method being examined, the output of the command will
+include the source file name and line number corresponding to the
+disassembly. The -n (No line numbers) flag can be specified to disable this
+behavior.
+
+ <example output>
+ ...
+ c:\Code\prj.mini\exc.cs @ 38:
+ 001b00b0 8b0d3020ab03 mov ecx,dword ptr ds:[3AB2030h] ("Break in debugger. When done type <Enter> to continue: ")
+ 001b00b6 e8d5355951 call mscorlib_ni+0x8b3690 (51743690) (System.Console.Write(System.String), mdToken: 0600091b)
+ 001b00bb 90 nop
+
+ c:\Code\prj.mini\exc.cs @ 39:
+ 001b00bc e863cdc651 call mscorlib_ni+0xf8ce24 (51e1ce24) (System.Console.ReadLine(), mdToken: 060008f6)
+ >>> 001b00c1 90 nop
+ ...
+\\
+
+COMMAND: dumpstack.
+DumpStack [-EE] [-n] [top stack [bottom stack]]
+
+[x86 and x64 documentation]
+
+This command provides a verbose stack trace obtained by "scraping." Therefore
+the output is very noisy and potentially confusing. The command is good for
+viewing the complete call stack when "kb" gets confused. For best results,
+make sure you have valid symbols.
+
+-EE will only show managed functions.
+
+If the debugger has the option SYMOPT_LOAD_LINES specified (either by the
+.lines or .symopt commands), SOS will look up the symbols for every managed
+frame and if successful will display the corresponding source file name and
+line number. The -n (No line numbers) parameter can be specified to disable
+this behavior.
+
+You can also pass a stack range to limit the output.
+\\
+
+COMMAND: eestack.
+EEStack [-short] [-EE]
+
+This command runs DumpStack on all threads in the process. The -EE option is
+passed directly to DumpStack. The -short option tries to narrow down the
+output to "interesting" threads only, which is defined by
+
+1) The thread has taken a lock.
+2) The thread has been "hijacked" in order to allow a garbage collection.
+3) The thread is currently in managed code.
+
+See the documentation for DumpStack for more info.
+\\
+
+COMMAND: ehinfo.
+EHInfo (<MethodDesc address> | <Code address>)
+
+EHInfo shows the exception handling blocks in a jitted method. For each
+handler, it shows the type, including code addresses and offsets for the clause
+block and the handler block. For a TYPED handler, this would be the "try" and
+"catch" blocks respectively.
+
+Sample output:
+
+ (lldb) sos EHInfo 33bbd3a
+ MethodDesc: 03310f68
+ Method Name: MainClass.Main()
+ Class: 03571358
+ MethodTable: 0331121c
+ mdToken: 0600000b
+ Module: 001e2fd8
+ IsJitted: yes
+ CodeAddr: 033bbca0
+ Transparency: Critical
+
+ EHHandler 0: TYPED catch(System.IO.FileNotFoundException)
+ Clause: [033bbd2b, 033bbd3c] [8b, 9c]
+ Handler: [033bbd3c, 033bbd50] [9c, b0]
+
+ EHHandler 1: FINALLY
+ Clause: [033bbd83, 033bbda3] [e3, 103]
+ Handler: [033bbda3, 033bbdc5] [103, 125]
+
+ EHHandler 2: TYPED catch(System.Exception)
+ Clause: [033bbd7a, 033bbdc5] [da, 125]
+ Handler: [033bbdc5, 033bbdd6] [125, 136]
+
+\\
+
+COMMAND: gcinfo.
+GCInfo (<MethodDesc address> | <Code address>)
+
+GCInfo is especially useful for CLR Devs who are trying to determine if there
+is a bug in the JIT Compiler. It parses the GCEncoding for a method, which is a
+compressed stream of data indicating when registers or stack locations contain
+managed objects. It is important to keep track of this information, because if
+a garbage collection occurs, the collector needs to know where roots are so it
+can update them with new object pointer values.
+
+Here is sample output where you can see the change in register state. Normally
+you would print this output out and read it alongside a disassembly of the
+method. For example, the notation "reg EDI becoming live" at offset 0x11 of the
+method might correspond to a "mov edi,ecx" statement.
+
+ (lldb) sos GCInfo 5b68dbb8 (5b68dbb8 is the start of a JITTED method)
+ entry point 5b68dbb8
+ preJIT generated code
+ GC info 5b9f2f09
+ Method info block:
+ method size = 0036
+ prolog size = 19
+ epilog size = 8
+ epilog count = 1
+ epilog end = yes
+ saved reg. mask = 000B
+ ebp frame = yes
+ fully interruptible=yes
+ double align = no
+ security check = no
+ exception handlers = no
+ local alloc = no
+ edit & continue = no
+ varargs = no
+ argument count = 4
+ stack frame size = 1
+ untracked count = 5
+ var ptr tab count = 0
+ epilog at 002E
+ 36 D4 8C C7 AA |
+ 93 F3 40 05 |
+
+ Pointer table:
+ 14 | [EBP+14H] an untracked local
+ 10 | [EBP+10H] an untracked local
+ 0C | [EBP+0CH] an untracked local
+ 08 | [EBP+08H] an untracked local
+ 44 | [EBP-04H] an untracked local
+ F1 79 | 0011 reg EDI becoming live
+ 72 | 0013 reg ESI becoming live
+ 83 | 0016 push ptr 0
+ 8B | 0019 push ptr 1
+ 93 | 001C push ptr 2
+ 9B | 001F push ptr 3
+ 56 | 0025 reg EDX becoming live
+ 4A | 0027 reg ECX becoming live
+ 0E | 002D reg ECX becoming dead
+ 10 | 002D reg EDX becoming dead
+ E0 | 002D pop 4 ptrs
+ F0 31 | 0036 reg ESI becoming dead
+ 38 | 0036 reg EDI becoming dead
+ FF |
+
+This function is important for CLR Devs, but very difficult for anyone else to
+make sense of it. You would usually come to use it if you suspect a gc heap
+corruption bug caused by invalid GCEncoding for a particular method.
+\\
+
+COMMAND: bpmd.
+bpmd [-nofuturemodule] <module name> <method name> [<il offset>]
+bpmd -md <MethodDesc>
+bpmd -list
+bpmd -clear <pending breakpoint number>
+bpmd -clearall
+
+bpmd provides managed breakpoint support. If it can resolve the method name
+to a loaded, jitted or ngen'd function it will create a breakpoint with "bp".
+If not then either the module that contains the method hasn't been loaded yet
+or the module is loaded, but the function is not jitted yet. In these cases,
+bpmd asks the Windows Debugger to receive CLR Notifications, and waits to
+receive news of module loads and JITs, at which time it will try to resolve
+the function to a breakpoint. -nofuturemodule can be used to suppress
+creating a breakpoint against a module that has not yet been loaded.
+
+Management of the list of pending breakpoints can be done via bpmd -list,
+bpmd -clear, and bpmd -clearall commands. bpmd -list generates a list of
+all of the pending breakpoints. If the pending breakpoint has a non-zero
+module id, then that pending breakpoint is specific to function in that
+particular loaded module. If the pending breakpoint has a zero module id, then
+the breakpoint applies to modules that have not yet been loaded. Use
+bpmd -clear or bpmd -clearall to remove pending breakpoints from the list.
+
+This brings up a good question: "I want to set a breakpoint on the main
+method of my application. How can I do this?"
+
+ 1) Stop after coreclr is loaded - TBD
+
+ 2) Add the breakpoint with command such as:
+ bpmd myapp.exe MyApp.Main
+ 3) g
+ 4) You will stop at the start of MyApp.Main. If you type "bl" you will
+ see the breakpoint listed.
+
+To correctly specify explicitly implemented methods make sure to retrieve the
+method name from the metadata, or from the output of the "dumpmt -md" command.
+For example:
+
+ public interface I1
+ {
+ void M1();
+ }
+ public class ExplicitItfImpl : I1
+ {
+ ...
+ void I1.M1() // this method's name is 'I1.M1'
+ { ... }
+ }
+
+ bpmd myapp.exe ExplicitItfImpl.I1.M1
+
+
+bpmd works equally well with generic types. Adding a breakpoint on a generic
+type sets breakpoints on all already JIT-ted generic methods and sets a pending
+breakpoint for any instantiation that will be JIT-ted in the future.
+
+Example for generics:
+ Given the following two classes:
+
+ class G3<T1, T2, T3>
+ {
+ ...
+ public void F(T1 p1, T2 p2, T3 p3)
+ { ... }
+ }
+
+ public class G1<T> {
+ // static method
+ static public void G<W>(W w)
+ { ... }
+ }
+
+ One would issue the following commands to set breapoints on G3.F() and
+ G1.G():
+
+ bpmd myapp.exe G3`3.F
+ bpmd myapp.exe G1`1.G
+
+And for explicitly implemented methods on generic interfaces:
+ public interface IT1<T>
+ {
+ void M1(T t);
+ }
+
+ public class ExplicitItfImpl<U> : IT1<U>
+ {
+ ...
+ void IT1<U>.M1(U u) // this method's name is 'IT1<U>.M1'
+ { ... }
+ }
+
+ bpmd bpmd.exe ExplicitItfImpl`1.IT1<U>.M1
+
+Additional examples:
+ If IT1 and ExplicitItfImpl are types declared inside another class,
+ Outer, the bpmd command would become:
+
+ bpmd bpmd.exe Outer+ExplicitItfImpl`1.Outer.IT1<U>.M1
+
+ (note that the fully qualified type name for ExplicitItfImpl became
+ Outer+ExplicitItfImpl, using the '+' separator, while the method name
+ is Outer.IT1<U>.M1, using a '.' as the separator)
+
+ Furthermore, if the Outer class resides in a namespace, NS, the bpmd
+ command to use becomes:
+
+ bpmd bpmd.exe NS.Outer+ExplicitItfImpl`1.NS.Outer.IT1<U>.M1
+
+bpmd does not accept offsets nor parameters in the method name. You can add
+an IL offset as an optional parameter seperate from the name. If there are overloaded
+methods, bpmd will set a breakpoint for all of them.
+
+In the case of hosted environments such as SQL, the module name may be
+complex, like 'price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
+For this case, just be sure to surround the module name with single quotes,
+like:
+
+bpmd 'price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' Price.M2
+
+\\
+
+COMMAND: dumpdomain.
+DumpDomain [<Domain address>]
+
+When called with no parameters, DumpDomain will list all the AppDomains in the
+process. It enumerates each Assembly loaded into those AppDomains as well.
+In addition to your application domain, and any domains it might create, there
+are two special domains: the Shared Domain and the System Domain.
+
+Any Assembly pointer in the output can be passed to DumpAssembly. Any Module
+pointer in the output can be passed to DumpModule. Any AppDomain pointer can
+be passed to DumpDomain to limit output only to that AppDomain. Other
+functions provide an AppDomain pointer as well, such as clrthreads where it lists
+the current AppDomain for each thread.
+\\
+
+COMMAND: eeheap.
+EEHeap [-gc] [-loader]
+
+EEHeap enumerates process memory consumed by internal CLR data structures. You
+can limit the output by passing "-gc" or "-loader". All information will be
+displayed otherwise.
+
+The information for the Garbage Collector lists the ranges of each Segment in
+the managed heap. This can be useful if you believe you have an object pointer.
+If the pointer falls within a segment range given by "eeheap -gc", then you do
+have an object pointer, and can attempt to run "dumpobj" on it.
+
+Here is output for a simple program:
+
+ (lldb) eeheap -gc
+ Number of GC Heaps: 1
+ generation 0 starts at 0x00a71018
+ generation 1 starts at 0x00a7100c
+ generation 2 starts at 0x00a71000
+ segment begin allocated size
+ 00a70000 00a71000 00a7e01c 0000d01c(53276)
+ Large object heap starts at 0x01a71000
+ segment begin allocated size
+ 01a70000 01a71000 01a76000 0x00005000(20480)
+ Total Size 0x1201c(73756)
+ ------------------------------
+ GC Heap Size 0x1201c(73756)
+
+So the total size of the GC Heap is only 72K. On a large web server, with
+multiple processors, you can expect to see a GC Heap of 400MB or more. The
+Garbage Collector attempts to collect and reclaim memory only when required to
+by memory pressure for better performance. You can also see the notion of
+"generations," wherein the youngest objects live in generation 0, and
+long-lived objects eventually get "promoted" to generation 2.
+
+The loader output lists various private heaps associated with AppDomains. It
+also lists heaps associated with the JIT compiler, and heaps associated with
+Modules. For example:
+
+ (lldb) eeheap -loader
+ Loader Heap:
+ --------------------------------------
+ System Domain: 5e0662a0
+ LowFrequencyHeap:008f0000(00002000:00001000) Size: 0x00001000 bytes.
+ HighFrequencyHeap:008f2000(00008000:00001000) Size: 0x00001000 bytes.
+ StubHeap:008fa000(00002000:00001000) Size: 0x00001000 bytes.
+ Total size: 0x3000(12288)bytes
+ --------------------------------------
+ Shared Domain: 5e066970
+ LowFrequencyHeap:00920000(00002000:00001000) 03e30000(00010000:00003000) Size: 0x00004000 bytes.
+ Wasted: 0x00001000 bytes.
+ HighFrequencyHeap:00922000(00008000:00001000) Size: 0x00001000 bytes.
+ StubHeap:0092a000(00002000:00001000) Size: 0x00001000 bytes.
+ Total size: 0x6000(24576)bytes
+ --------------------------------------
+ Domain 1: 14f000
+ LowFrequencyHeap:00900000(00002000:00001000) 03ee0000(00010000:00003000) Size: 0x00004000 bytes.
+ Wasted: 0x00001000 bytes.
+ HighFrequencyHeap:00902000(00008000:00003000) Size: 0x00003000 bytes.
+ StubHeap:0090a000(00002000:00001000) Size: 0x00001000 bytes.
+ Total size: 0x8000(32768)bytes
+ --------------------------------------
+ Jit code heap:
+ Normal JIT:03ef0000(00010000:00002000) Size: 0x00002000 bytes.
+ Total size: 0x2000(8192)bytes
+ --------------------------------------
+ Module Thunk heaps:
+ Module 5ba22410: Size: 0x00000000 bytes.
+ Module 001c1320: Size: 0x00000000 bytes.
+ Module 001c03f0: Size: 0x00000000 bytes.
+ Module 001caa38: Size: 0x00000000 bytes.
+ Total size: 0x0(0)bytes
+ --------------------------------------
+ Module Lookup Table heaps:
+ Module 5ba22410:Size: 0x00000000 bytes.
+ Module 001c1320:Size: 0x00000000 bytes.
+ Module 001c03f0:Size: 0x00000000 bytes.
+ Module 001caa38:03ec0000(00010000:00002000) Size: 0x00002000 bytes.
+ Total size: 0x2000(8192)bytes
+ --------------------------------------
+ Total LoaderHeap size: 0x15000(86016)bytes
+ =======================================
+
+By using eeheap to keep track of the growth of these private heaps, we are
+able to rule out or include them as a source of a memory leak.
+\\
+
+COMMAND: name2ee.
+Name2EE <module name> <type or method name>
+Name2EE <module name>!<type or method name>
+
+This function allows you to turn a class name into a MethodTable and EEClass.
+It turns a method name into a MethodDesc. Here is an example for a method:
+
+ (lldb) name2ee unittest.exe MainClass.Main
+ Module: 001caa38
+ Token: 0x0600000d
+ MethodDesc: 00902f40
+ Name: MainClass.Main()
+ JITTED Code Address: 03ef00b8
+
+and for a class:
+
+ (lldb) name2ee unittest!MainClass
+ Module: 001caa38
+ Token: 0x02000005
+ MethodTable: 009032d8
+ EEClass: 03ee1424
+ Name: MainClass
+
+The module you are "browsing" with Name2EE needs to be loaded in the process.
+To get a type name exactly right, first browse the module with ILDASM. You
+can also pass * as the <module name> to search all loaded managed modules.
+<module name> can also be the debugger's name for a module, such as
+mscorlib or image00400000.
+
+The <module>!<type> syntax is also supported. You can use an asterisk on the
+left of the !, but the type on the right side needs to be fully qualified.
+
+If you are looking for a way to display a static field of a class (and you
+don't have an instance of the class, so dumpobj won't help you), note that
+once you have the EEClass, you can run DumpClass, which will display the
+value of all static fields.
+
+There is yet one more way to specify a module name. In the case of modules
+loaded from an assembly store (such as a SQL db) rather than disk, the
+module name will look like this:
+
+price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
+
+For this kind of module, simply use price as the module name:
+
+ 0:044> name2ee price Price
+ Module: 10f028b0 (price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null)
+ Token: 0x02000002
+ MethodTable: 11a47ae0
+ EEClass: 11a538c8
+ Name: Price
+
+Where are we getting these module names from? Run DumpDomain to see a list of
+all loaded modules in all domains. And remember that you can browse all the
+types in a module with DumpModule -mt <module pointer>.
+\\
+
+COMMAND: dumpmt.
+DumpMT [-MD] <MethodTable address>
+
+Examine a MethodTable. Each managed object has a MethodTable pointer at the
+start. If you pass the "-MD" flag, you'll also see a list of all the methods
+defined on the object.
+\\
+
+COMMAND: dumpclass.
+DumpClass <EEClass address>
+
+The EEClass is a data structure associated with an object type. DumpClass
+will show attributes, as well as list the fields of the type. The output is
+similar to DumpObj. Although static field values will be displayed,
+non-static values won't because you need an instance of an object for that.
+
+You can get an EEClass to look at from DumpMT, DumpObj, Name2EE, and
+Token2EE among others.
+\\
+
+COMMAND: dumpmd.
+DumpMD <MethodDesc address>
+
+This command lists information about a MethodDesc. You can use ip2md to turn
+a code address in a managed function into a MethodDesc:
+
+ (lldb) dumpmd 902f40
+ Method Name: Mainy.Main()
+ Class: 03ee1424
+ MethodTable: 009032d8
+ mdToken: 0600000d
+ Module: 001caa78
+ IsJitted: yes
+ CodeAddr: 03ef00b8
+
+If IsJitted is "yes," you can run U on the CodeAddr pointer to see a
+disassembly of the JITTED code. You can call also DumpClass, DumpMT,
+DumpModule on the Class, MethodTable and Module fields above.
+\\
+
+COMMAND: token2ee.
+Token2EE <module name> <token>
+
+This function allows you to turn a metadata token into a MethodTable or
+MethodDesc. Here is an example showing class tokens being resolved:
+
+ (lldb) sos Token2EE unittest.exe 02000003
+ Module: 001caa38
+ Token: 0x02000003
+ MethodTable: 0090375c
+ EEClass: 03ee1ae0
+ Name: Bank
+ (lldb) sos Token2EE image00400000 02000004
+ Module: 001caa38
+ Token: 0x02000004
+ MethodTable: 009038ec
+ EEClass: 03ee1b84
+ Name: Customer
+
+The module you are "browsing" with Token2EE needs to be loaded in the process.
+This function doesn't see much use, especially since a tool like ILDASM can
+show the mapping between metadata tokens and types/methods in a friendlier way.
+But it could be handy sometimes.
+
+You can pass "*" for <module name> to find what that token maps to in every
+loaded managed module. <module name> can also be the debugger's name for a
+module, such as mscorlib or image00400000.
+\\
+
+COMMAND: dumpmodule.
+DumpModule [-mt] <Module address>
+
+You can get a Module address from DumpDomain, DumpAssembly and other
+functions. Here is sample output:
+
+ (lldb) sos DumpModule 1caa50
+ Name: /home/user/pub/unittest
+ Attributes: PEFile
+ Assembly: 001ca248
+ LoaderHeap: 001cab3c
+ TypeDefToMethodTableMap: 03ec0010
+ TypeRefToMethodTableMap: 03ec0024
+ MethodDefToDescMap: 03ec0064
+ FieldDefToDescMap: 03ec00a4
+ MemberRefToDescMap: 03ec00e8
+ FileReferencesMap: 03ec0128
+ AssemblyReferencesMap: 03ec012c
+ MetaData start address: 00402230 (1888 bytes)
+
+The Maps listed map metadata tokens to CLR data structures. Without going into
+too much detail, you can examine memory at those addresses to find the
+appropriate structures. For example, the TypeDefToMethodTableMap above can be
+examined:
+
+ (lldb) dd 3ec0010
+ 03ec0010 00000000 00000000 0090320c 0090375c
+ 03ec0020 009038ec ...
+
+This means TypeDef token 2 maps to a MethodTable with the value 0090320c. You
+can run DumpMT to verify that. The MethodDefToDescMap takes a MethodDef token
+and maps it to a MethodDesc, which can be passed to dumpmd.
+
+There is a new option "-mt", which will display the types defined in a module,
+and the types referenced by the module. For example:
+
+ (lldb) sos DumpModule -mt 1aa580
+ Name: /home/user/pub/unittest
+ ...<etc>...
+ MetaData start address: 0040220c (1696 bytes)
+
+ Types defined in this module
+
+ MT TypeDef Name
+ --------------------------------------------------------------------------
+ 030d115c 0x02000002 Funny
+ 030d1228 0x02000003 Mainy
+
+ Types referenced in this module
+
+ MT TypeRef Name
+ --------------------------------------------------------------------------
+ 030b6420 0x01000001 System.ValueType
+ 030b5cb0 0x01000002 System.Object
+ 030fceb4 0x01000003 System.Exception
+ 0334e374 0x0100000c System.Console
+ 03167a50 0x0100000e System.Runtime.InteropServices.GCHandle
+ 0336a048 0x0100000f System.GC
+
+\\
+
+COMMAND: dumpassembly.
+DumpAssembly <Assembly address>
+
+Example output:
+
+ (lldb) sos DumpAssembly 1ca248
+ Parent Domain: 0014f000
+ Name: /home/user/pub/unittest
+ ClassLoader: 001ca060
+ Module Name
+ 001caa50 /home/user/pub/unittest
+
+An assembly can consist of multiple modules, and those will be listed. You can
+get an Assembly address from the output of DumpDomain.
+\\
+
+COMMAND: dumpruntimetypes.
+DumpRuntimeTypes
+
+DumpRuntimeTypes finds all System.RuntimeType objects in the gc heap and
+prints the type name and MethodTable they refer too. Sample output:
+
+ Address Domain MT Type Name
+ ------------------------------------------------------------------------------
+ a515f4 14a740 5baf8d28 System.TypedReference
+ a51608 14a740 5bb05764 System.Globalization.BaseInfoTable
+ a51958 14a740 5bb05b24 System.Globalization.CultureInfo
+ a51a44 14a740 5bb06298 System.Globalization.GlobalizationAssembly
+ a51de0 14a740 5bb069c8 System.Globalization.TextInfo
+ a56b98 14a740 5bb12d28 System.Security.Permissions.HostProtectionResource
+ a56bbc 14a740 5baf7248 System.Int32
+ a56bd0 14a740 5baf3fdc System.String
+ a56cfc 14a740 5baf36a4 System.ValueType
+ ...
+
+This command will print a "?" in the domain column if the type is loaded into multiple
+AppDomains. For example:
+
+ (lldb) sos DumpRuntimeTypes
+ Address Domain MT Type Name
+ ------------------------------------------------------------------------------
+ 28435a0 ? 3f6a8c System.TypedReference
+ 28435b4 ? 214d6c System.ValueType
+ 28435c8 ? 216314 System.Enum
+ 28435dc ? 2147cc System.Object
+ 284365c ? 3cd57c System.IntPtr
+ 2843670 ? 3feaac System.Byte
+ 2843684 ? 23a544c System.IEquatable`1[[System.IntPtr, mscorlib]]
+ 2843784 ? 3c999c System.Int32
+ 2843798 ? 3caa04 System.IEquatable`1[[System.Int32, mscorlib]]
+\\
+
+COMMAND: dumpsig.
+DumpSig <sigaddr> <moduleaddr>
+
+This command dumps the signature of a method or field given by <sigaddr>. This is
+useful when you are debugging parts of the runtime which returns a raw PCCOR_SIGNATURE
+structure and need to know what its contents are.
+
+Sample output for a method:
+ 0:000> sos DumpSig 0x000007fe`ec20879d 0x000007fe`eabd1000
+ [DEFAULT] [hasThis] Void (Boolean,String,String)
+
+The first section of the output is the calling convention. This includes, but is not
+limited to, "[DEFAULT]", "[C]", "[STDCALL]", "[THISCALL]", and so on. The second
+portion of the output is either "[hasThis]" or "[explicit]" for whether the method
+is an instance method or a static method respectively. The third portion of the
+output is the return value (in this case a "void"). Finally, the method's arguments
+are printed as the final portion of the output.
+
+Sample output for a field:
+ 0:000> sos DumpSig 0x000007fe`eb7fd8cd 0x000007fe`eabd1000
+ [FIELD] ValueClass System.RuntimeTypeHandle
+
+DumpSig will also work with generics. Here is the output for the following
+function:
+ public A Test(IEnumerable<B> n)
+
+ 0:000> sos DumpSig 00000000`00bc2437 000007ff00043178
+ [DEFAULT] [hasThis] __Canon (Class System.Collections.Generic.IEnumerable`1<__Canon>)
+\\
+
+COMMAND: dumpsigelem.
+DumpSigElem <sigaddr> <moduleaddr>
+
+This command dumps a single element of a signature object. For most circumstances,
+you should use DumpSig to look at individual signature objects, but if you find a
+signature that has been corrupted in some manner you can use DumpSigElem to read out
+the valid portions of it.
+
+If we look at a valid signature object for a method we see the following:
+ 0:000> dumpsig 0x000007fe`ec20879d 0x000007fe`eabd1000
+ [DEFAULT] [hasThis] Void (Boolean,String,String)
+
+We can look at the individual elements of this object by adding the offsets into the
+object which correspond to the return value and parameters:
+ 0:000> sos DumpSigElem 0x000007fe`ec20879d+2 0x000007fe`eabd1000
+ Void
+ 0:000> sos DumpSigElem 0x000007fe`ec20879d+3 0x000007fe`eabd1000
+ Boolean
+ 0:000> sos DumpSigElem 0x000007fe`ec20879d+4 0x000007fe`eabd1000
+ String
+ 0:000> sos DumpSigElem 0x000007fe`ec20879d+5 0x000007fe`eabd1000
+ String
+
+We can do something similar for fields. Here is the full signature of a field:
+ 0:000> dumpsig 0x000007fe`eb7fd8cd 0x000007fe`eabd1000
+ [FIELD] ValueClass System.RuntimeTypeHandle
+
+Using DumpSigElem we can find the type of the field by adding the offset of it (1) to
+the address of the signature:
+ 0:000> sos DumpSigElem 0x000007fe`eb7fd8cd+1 0x000007fe`eabd1000
+ ValueClass System.RuntimeTypeHandle
+
+DumpSigElem will also work with generics. Let a function be defined as follows:
+ public A Test(IEnumerable<B> n)
+
+The elements of this signature can be obtained by adding offsets into the signature
+when calling DumpSigElem:
+
+ 0:000> sos DumpSigElem 00000000`00bc2437+2 000007ff00043178
+ __Canon
+ 0:000> sos DumpSigElem 00000000`00bc2437+4 000007ff00043178
+ Class System.Collections.Generic.IEnumerable`1<__Canon>
+
+The actual offsets that you should add are determined by the contents of the
+signature itself. By trial and error you should be able to find various elements
+of the signature.
+\\
+
+COMMAND: dumpil.
+DumpIL <Managed DynamicMethod object> |
+ <DynamicMethodDesc pointer> |
+ <MethodDesc pointer> |
+ /i <IL pointer>
+
+DumpIL prints the IL code associated with a managed method. We added this
+function specifically to debug DynamicMethod code which was constructed on
+the fly. Happily it works for non-dynamic code as well.
+
+You can use it in four ways:
+
+ 1) If you have a System.Reflection.Emit.DynamicMethod object, just pass
+ the pointer as the first argument.
+ 2) If you have a DynamicMethodDesc pointer you can use that to print the
+ IL associated with the dynamic method.
+ 3) If you have an ordinary MethodDesc, you can see the IL for that as well,
+ just pass it as the first argument.
+ 4) If you have a pointer directly to the IL, specify /i followed by the
+ the IL address. This is useful for writers of profilers that instrument
+ IL.
+
+
+Note that dynamic IL is constructed a bit differently. Rather than referring
+to metadata tokens, the IL points to objects in a managed object array. Here
+is a simple example of the output for a dynamic method:
+
+ 0:000> sos DumpIL b741dc
+ This is dynamic IL. Exception info is not reported at this time.
+ If a token is unresolved, run "sos DumpObj <addr>" on the addr given
+ in parenthesis. You can also look at the token table yourself, by
+ running "DumpArray 00b77388".
+
+ IL_0000: ldstr 70000002 "Inside invoked method "
+ IL_0005: call 6000003 System.Console.WriteLine(System.String)
+ IL_000a: ldc.i4.1
+ IL_000b: newarr 2000004 "System.Int32"
+ IL_0010: stloc.0
+ IL_0011: ldloc.0
+ IL_0012: ret
+\\
+
+COMMAND: verifyheap.
+VerifyHeap
+
+VerifyHeap is a diagnostic tool that checks the garbage collected heap for
+signs of corruption. It walks objects one by one in a pattern like this:
+
+ o = firstobject;
+ while(o != endobject)
+ {
+ o.ValidateAllFields();
+ o = (Object *) o + o.Size();
+ }
+
+If an error is found, VerifyHeap will report it. I'll take a perfectly good
+object and corrupt it:
+
+ (lldb) dumpobj a79d40
+ Name: Customer
+ MethodTable: 009038ec
+ EEClass: 03ee1b84
+ Size: 20(0x14) bytes
+ (/home/user/pub/unittest)
+ Fields:
+ MT Field Offset Type Attr Value Name
+ 009038ec 4000008 4 CLASS instance 00a79ce4 name
+ 009038ec 4000009 8 CLASS instance 00a79d2c bank
+ 009038ec 400000a c System.Boolean instance 1 valid
+
+ (lldb) ed a79d40+4 01 (change the name field to the bogus pointer value 1)
+ (lldb) sos VerifyHeap
+ object 01ee60dc: bad member 00000003 at 01EE6168
+ Last good object: 01EE60C4.
+
+If this gc heap corruption exists, there is a serious bug in your own code or
+in the CLR. In user code, an error in constructing PInvoke calls can cause
+this problem, and running with Managed Debugging Assistants is advised. If that
+possibility is eliminated, consider contacting Microsoft Product Support for
+help.
+\\
+
+COMMAND: dumplog.
+DumpLog [-addr <addressOfStressLog>] [<Filename>]
+
+To aid in diagnosing hard-to-reproduce stress failures, the CLR team added an
+in-memory log capability. The idea was to avoid using locks or I/O which could
+disturb a fragile repro environment. The DumpLog function allows you to write
+that log out to a file. If no Filename is specified, the file "Stresslog.txt"
+in the current directory is created.
+
+The optional argument addr allows one to specify a stress log other then the
+default one.
+
+ (lldb) dumplog
+ Attempting to dump Stress log to file 'StressLog.txt'
+ .................
+ SUCCESS: Stress log dumped
+
+To turn on the stress log, set the following registry keys under
+HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework:
+
+
+(DWORD) StressLog = 1
+(DWORD) LogFacility = 0xffffffbf (this is a bit mask, almost all logging is on.
+ This is also the default value if the key
+ isn't specified)
+(DWORD) StressLogSize = 65536 (this is the default value if the key isn't
+ specified)
+(DWORD) LogLevel = 6 (this is the default value if the key isn't
+ specified. The higher the number the more
+ detailed logs are generated. The maximum
+ value is decimal 10)
+
+StressLogSize is the size in bytes of the in-memory log allocated for each
+thread in the process. In the case above, each thread gets a 64K log. You
+could increase this to get more logging, but more memory will be required for
+this log in the process. For example, 20 threads with 524288 bytes per thread
+has a memory demand of 10 Megabytes. The stress log is circular so new entries
+will replace older ones on threads which have reached their buffer limit.
+
+The log facilities are defined as follows:
+ GC 0x00000001
+ GCINFO 0x00000002
+ STUBS 0x00000004
+ JIT 0x00000008
+ LOADER 0x00000010
+ METADATA 0x00000020
+ SYNC 0x00000040
+ EEMEM 0x00000080
+ GCALLOC 0x00000100
+ CORDB 0x00000200
+ CLASSLOADER 0x00000400
+ CORPROF 0x00000800
+ REMOTING 0x00001000
+ DBGALLOC 0x00002000
+ EH 0x00004000
+ ENC 0x00008000
+ ASSERT 0x00010000
+ VERIFIER 0x00020000
+ THREADPOOL 0x00040000
+ GCROOTS 0x00080000
+ INTEROP 0x00100000
+ MARSHALER 0x00200000
+ IJW 0x00400000
+ ZAP 0x00800000
+ STARTUP 0x01000000
+ APPDOMAIN 0x02000000
+ CODESHARING 0x04000000
+ STORE 0x08000000
+ SECURITY 0x10000000
+ LOCKS 0x20000000
+ BCL 0x40000000
+
+Here is some sample output:
+
+ 3560 9.981137099 : `SYNC` RareEnablePremptiveGC: entering.
+ Thread state = a030
+
+ 3560 9.981135033 : `GC`GCALLOC`GCROOTS` ========== ENDGC 4194 (gen = 2,
+ collect_classes = 0) ==========={
+
+ 3560 9.981125826 : `GC` Segment mem 00C61000 alloc
+ = 00D071F0 used 00D09254 committed 00D17000
+
+ 3560 9.981125726 : `GC` Generation 0 [00CED07C, 00000000
+ ] cur = 00000000
+
+ 3560 9.981125529 : `GC` Generation 1 [00CED070, 00000000
+ ] cur = 00000000
+
+ 3560 9.981125103 : `GC` Generation 2 [00C61000, 00000000
+ ] cur = 00000000
+
+ 3560 9.981124963 : `GC` GC Heap 00000000
+
+ 3560 9.980618994 : `GC`GCROOTS` GcScanHandles (Promotion Phase = 0)
+
+The first column is the OS thread ID for the thread appending to the log,
+the second column is the timestamp, the third is the facility category for the
+log entry, and the fourth contains the log message. The facility field is
+expressed as `facility1`facility2`facility3`. This facilitates the creation of
+filters for displaying only specific message categories. To make sense of this
+log, you would probably want the Shared Source CLI to find out exactly where
+the log comes from.
+\\
+
+COMMAND: findappdomain.
+FindAppDomain <Object address>
+
+FindAppDomain will attempt to resolve the AppDomain of an object. For example,
+using an Object Pointer from the output of DumpStackObjects:
+
+ (lldb) sos FindAppDomain 00a79d98
+ AppDomain: 0014f000
+ Name: unittest.exe
+ ID: 1
+
+You can find out more about the AppDomain with the DumpDomain command. Not
+every object has enough clues about it's origin to determine the AppDomain.
+Objects with Finalizers are the easiest case, as the CLR needs to be able to
+call those when an AppDomain shuts down.
+\\
+
+COMMAND: histinit.
+HistInit
+
+Before running any of the Hist - family commands you need to initialize the SOS
+structures from the stress log saved in the debuggee. This is achieved by the
+HistInit command.
+
+Sample output:
+
+ (lldb) histinit
+ Attempting to read Stress log
+ STRESS LOG:
+ facilitiesToLog = 0xffffffff
+ levelToLog = 6
+ MaxLogSizePerThread = 0x10000 (65536)
+ MaxTotalLogSize = 0x1000000 (16777216)
+ CurrentTotalLogChunk = 9
+ ThreadsWithLogs = 3
+ Clock frequency = 3.392 GHz
+ Start time 15:26:31
+ Last message time 15:26:56
+ Total elapsed time 25.077 sec
+ .....................................
+ ---------------------------- 2407 total entries -----------------------------
+
+
+ SUCCESS: GCHist structures initialized
+
+\\
+
+COMMAND: histobjfind.
+HistObjFind <obj_address>
+
+To examine log entries related to an object whose present address is known one
+would use this command. The output of this command contains all entries that
+reference the object:
+
+ (lldb) histobjfind 028970d4
+ GCCount Object Message
+ ---------------------------------------------------------
+ 2296 028970d4 Promotion for root 01e411b8 (MT = 5b6c5cd8)
+ 2296 028970d4 Relocation NEWVALUE for root 00223fc4
+ 2296 028970d4 Relocation NEWVALUE for root 01e411b8
+ ...
+ 2295 028970d4 Promotion for root 01e411b8 (MT = 5b6c5cd8)
+ 2295 028970d4 Relocation NEWVALUE for root 00223fc4
+ 2295 028970d4 Relocation NEWVALUE for root 01e411b8
+ ...
+
+\\
+
+COMMAND: histroot.
+HistRoot <root>
+
+The root value obtained from !HistObjFind can be used to track the movement of
+an object through the GCs.
+
+HistRoot provides information related to both promotions and relocations of the
+root specified as the argument.
+
+ (lldb) histroot 01e411b8
+ GCCount Value MT Promoted? Notes
+ ---------------------------------------------------------
+ 2296 028970d4 5b6c5cd8 yes
+ 2295 028970d4 5b6c5cd8 yes
+ 2294 028970d4 5b6c5cd8 yes
+ 2293 028970d4 5b6c5cd8 yes
+ 2292 028970d4 5b6c5cd8 yes
+ 2291 028970d4 5b6c5cd8 yes
+ 2290 028970d4 5b6c5cd8 yes
+ 2289 028970d4 5b6c5cd8 yes
+ 2288 028970d4 5b6c5cd8 yes
+ 2287 028970d4 5b6c5cd8 yes
+ 2286 028970d4 5b6c5cd8 yes
+ 2285 028970d4 5b6c5cd8 yes
+ 322 028970e8 5b6c5cd8 yes Duplicate promote/relocs
+ ...
+
+\\
+
+COMMAND: histobj.
+HistObj <obj_address>
+
+This command examines all stress log relocation records and displays the chain
+of GC relocations that may have led to the address passed in as an argument.
+Conceptually the output is:
+
+ GenN obj_address root1, root2, root3,
+ GenN-1 prev_obj_addr root1, root2,
+ GenN-2 prev_prev_oa root1, root4,
+ ...
+
+Sample output:
+ (lldb) histobj 028970d4
+ GCCount Object Roots
+ ---------------------------------------------------------
+ 2296 028970d4 00223fc4, 01e411b8,
+ 2295 028970d4 00223fc4, 01e411b8,
+ 2294 028970d4 00223fc4, 01e411b8,
+ 2293 028970d4 00223fc4, 01e411b8,
+ 2292 028970d4 00223fc4, 01e411b8,
+ 2291 028970d4 00223fc4, 01e411b8,
+ 2290 028970d4 00223fc4, 01e411b8,
+ 2289 028970d4 00223fc4, 01e411b8,
+ 2288 028970d4 00223fc4, 01e411b8,
+ 2287 028970d4 00223fc4, 01e411b8,
+ 2286 028970d4 00223fc4, 01e411b8,
+ 2285 028970d4 00223fc4, 01e411b8,
+ 322 028970d4 01e411b8,
+ 0 028970d4
+
+\\
+
+COMMAND: histclear.
+HistClear
+
+This command releases any resources used by the Hist-family of commands.
+Generally there's no need to call this explicitly, as each HistInit will first
+cleanup the previous resources.
+
+ (lldb) histclear
+ Completed successfully.
+
+\\
diff --git a/src/ToolBox/SOS/Strike/stressLogDump.cpp b/src/ToolBox/SOS/Strike/stressLogDump.cpp
new file mode 100644
index 0000000000..f277f92434
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/stressLogDump.cpp
@@ -0,0 +1,549 @@
+// 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 "strike.h"
+#include "util.h"
+#include <stdio.h>
+#include <ctype.h>
+
+#ifndef STRESS_LOG
+#define STRESS_LOG
+#endif // STRESS_LOG
+#define STRESS_LOG_READONLY
+#include "stresslog.h"
+
+
+void GcHistClear();
+void GcHistAddLog(LPCSTR msg, StressMsg* stressMsg);
+
+
+/*********************************************************************************/
+static const WCHAR* getTime(const FILETIME* time, __out_ecount (buffLen) WCHAR* buff, int buffLen)
+{
+ SYSTEMTIME systemTime;
+ static const WCHAR badTime[] = W("BAD TIME");
+
+ if (!FileTimeToSystemTime(time, &systemTime))
+ return badTime;
+
+#ifdef FEATURE_PAL
+ int length = _snwprintf(buff, buffLen, W("%02d:%02d:%02d"), systemTime.wHour, systemTime.wMinute, systemTime.wSecond);
+ if (length <= 0)
+ return badTime;
+#else // FEATURE_PAL
+ static const WCHAR format[] = W("HH:mm:ss");
+
+ SYSTEMTIME localTime;
+ SystemTimeToTzSpecificLocalTime(NULL, &systemTime, &localTime);
+
+ // we want a non null buff for the following
+ int ret = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &localTime, format, buff, buffLen);
+ if (ret == 0)
+ return badTime;
+#endif // FEATURE_PAL else
+
+ return buff;
+}
+
+/*********************************************************************************/
+static inline __int64& toInt64(FILETIME& t)
+{
+ return *((__int64 *) &t);
+}
+
+/*********************************************************************************/
+ThreadStressLog* ThreadStressLog::FindLatestThreadLog() const
+{
+ const ThreadStressLog* latestLog = 0;
+ for (const ThreadStressLog* ptr = this; ptr != NULL; ptr = ptr->next)
+ {
+ if (ptr->readPtr != NULL)
+ if (latestLog == 0 || ptr->readPtr->timeStamp > latestLog->readPtr->timeStamp)
+ latestLog = ptr;
+ }
+ return const_cast<ThreadStressLog*>(latestLog);
+}
+
+const char *getFacilityName(DWORD_PTR lf)
+{
+ struct FacilityName_t { size_t lf; const char* lfName; };
+ #define DEFINE_LOG_FACILITY(logname, value) {logname, #logname},
+ static FacilityName_t facilities[] =
+ {
+ #include <loglf.h>
+ { LF_ALWAYS, "LF_ALWAYS" }
+ };
+ static char buff[1024] = "`";
+ if ( lf == LF_ALL )
+ {
+ return "`ALL`";
+ }
+ else
+ {
+ buff[1] = '\0';
+ for ( int i = 0; i < 32; ++i )
+ {
+ if ( lf & 0x1 )
+ {
+ strcat_s ( buff, _countof(buff), &(facilities[i].lfName[3]) );
+ strcat_s ( buff, _countof(buff), "`" );
+ }
+ lf >>= 1;
+ }
+ return buff;
+ }
+}
+
+/***********************************************************************************/
+/* recognize special pretty printing instructions in the format string */
+/* Note that this function might have side effect such that args array value might */
+/* be altered if format string contains %s */
+// TODO: This function assumes the pointer size of the target equals the pointer size of the host
+// TODO: replace uses of void* with appropriate TADDR or CLRDATA_ADDRESS
+void formatOutput(struct IDebugDataSpaces* memCallBack, ___in FILE* file, __inout __inout_z char* format, unsigned threadId, double timeStamp, DWORD_PTR facility, ___in void** args)
+{
+ fprintf(file, "%4x %13.9f : ", threadId, timeStamp);
+ fprintf(file, "%-20s ", getFacilityName ( facility ));
+
+ CQuickBytes fullname;
+ char* ptr = format;
+ void** argsPtr = args;
+ const SIZE_T capacity_buff = 2048;
+ LPWSTR buff = (LPWSTR)alloca(capacity_buff * sizeof(WCHAR));
+ static char formatCopy[256];
+
+ int iArgCount = 0;
+
+ strcpy_s(formatCopy, _countof(formatCopy), format);
+ for(;;)
+ {
+ char c = *ptr++;
+ if (c == 0)
+ break;
+ if (c == '{') // Reverse the '{' 's because the log is displayed backwards
+ ptr[-1] = '}';
+ else if (c == '}')
+ ptr[-1] = '{';
+ else if (c == '%')
+ {
+ argsPtr++; // This format will consume one of the args
+ if (*ptr == '%')
+ {
+ ptr++; // skip the whole %%
+ --argsPtr; // except for a %%
+ }
+ else if (*ptr == 'p')
+ { // It is a %p
+ ptr++;
+ if (isalpha(*ptr))
+ { // It is a special %p formatter
+ // Print the string up to that point
+ c = *ptr;
+ *ptr = 0; // Terminate the string temporarily
+ fprintf(file, format, args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
+ *ptr = c; // Put it back
+
+ // move the argument pointers past the part the was printed
+ format = ptr + 1;
+ args = argsPtr;
+ iArgCount = -1;
+ DWORD_PTR arg = DWORD_PTR(argsPtr[-1]);
+
+ switch (c)
+ {
+ case 'M': // format as a method Desc
+ if (g_bDacBroken)
+ {
+ fprintf(file," (MethodDesc: %p)",arg);
+ }
+ else
+ {
+ if (!IsMethodDesc(arg))
+ {
+ if (arg != 0)
+ fprintf(file, " (BAD Method)");
+ }
+ else
+ {
+ DacpMethodDescData MethodDescData;
+ MethodDescData.Request(g_sos,(CLRDATA_ADDRESS)arg);
+
+ static WCHAR wszNameBuffer[1024]; // should be large enough
+ if (g_sos->GetMethodDescName(arg, 1024, wszNameBuffer, NULL) != S_OK)
+ {
+ wcscpy_s(wszNameBuffer, _countof(wszNameBuffer), W("UNKNOWN METHODDESC"));
+ }
+
+ wcscpy_s(buff, capacity_buff, wszNameBuffer);
+ fprintf(file, " (%S)", wszNameBuffer);
+ }
+ }
+ break;
+
+ // fall through
+ case 'T': // format as a MethodTable
+ if (g_bDacBroken)
+ {
+ fprintf(file, "(MethodTable: %p)",arg);
+ }
+ else
+ {
+ if (arg & 3)
+ {
+ arg &= ~3; // GC steals the lower bits for its own use during GC.
+ fprintf(file, " Low Bit(s) Set");
+ }
+ if (!IsMethodTable(arg))
+ {
+ fprintf(file, " (BAD MethodTable)");
+ }
+ else
+ {
+ NameForMT_s (arg, g_mdName, mdNameLen);
+ fprintf(file, " (%S)", g_mdName);
+ }
+ }
+ break;
+
+ case 'V':
+ { // format as a C vtable pointer
+ char Symbol[1024];
+ ULONG64 Displacement;
+ HRESULT hr = g_ExtSymbols->GetNameByOffset(TO_CDADDR(arg), Symbol, 1024, NULL, &Displacement);
+ if (SUCCEEDED(hr) && Symbol[0] != '\0' && Displacement == 0)
+ fprintf(file, " (%s)", Symbol);
+ else
+ fprintf(file, " (Unknown VTable)");
+ }
+ break;
+ case 'K':
+ { // format a frame in stack trace
+ char Symbol[1024];
+ ULONG64 Displacement;
+ HRESULT hr = g_ExtSymbols->GetNameByOffset (TO_CDADDR(arg), Symbol, 1024, NULL, &Displacement);
+ if (SUCCEEDED (hr) && Symbol[0] != '\0')
+ {
+ fprintf (file, " (%s", Symbol);
+ if (Displacement)
+ {
+ fprintf (file, "+%#x", Displacement);
+ }
+ fprintf (file, ")");
+ }
+ else
+ fprintf (file, " (Unknown function)");
+ }
+ break;
+ default:
+ format = ptr; // Just print the character.
+ }
+ }
+ }
+ else if (*ptr == 's' || (*ptr == 'h' && *(ptr+1) == 's' && ++ptr))
+ {
+ HRESULT hr;
+
+ // need to _alloca, instead of declaring a local buffer
+ // since we may have more than one %s in the format
+ ULONG cbStrBuf = 256;
+ char* strBuf = (char *)_alloca(cbStrBuf);
+
+ hr = memCallBack->ReadVirtual(TO_CDADDR((char* )args[iArgCount]), strBuf, cbStrBuf, 0);
+ if (hr != S_OK)
+ {
+ strcpy_s(strBuf, cbStrBuf, "(#Could not read address of string#)");
+ }
+
+ args[iArgCount] = strBuf;
+ }
+ else if (*ptr == 'S' || (*ptr == 'l' && *(ptr+1) == 's' && ++ptr))
+ {
+ HRESULT hr;
+
+ // need to _alloca, instead of declaring a local buffer
+ // since we may have more than one %s in the format
+ ULONG cbWstrBuf = 256 * sizeof(WCHAR);
+ WCHAR* wstrBuf = (WCHAR *)_alloca(cbWstrBuf);
+
+ hr = memCallBack->ReadVirtual(TO_CDADDR((char* )args[iArgCount]), wstrBuf, cbWstrBuf, 0);
+ if (hr != S_OK)
+ {
+ wcscpy_s(wstrBuf, cbWstrBuf/sizeof(WCHAR), W("(#Could not read address of string#)"));
+ }
+
+ args[iArgCount] = wstrBuf;
+ }
+ iArgCount++;
+ }
+ }
+ // Print anything after the last special format instruction.
+ fprintf(file, format, args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
+ fprintf(file, "\n");
+}
+
+void __cdecl
+vDoOut(BOOL bToConsole, FILE* file, PCSTR Format, ...)
+{
+ va_list Args;
+
+ va_start(Args, Format);
+
+ if (bToConsole)
+ {
+ g_ExtControl->OutputVaList(DEBUG_OUTPUT_NORMAL, Format, Args);
+ }
+ else
+ {
+ vfprintf(file, Format, Args);
+ }
+
+ va_end(Args);
+}
+
+
+/*********************************************************************************/
+HRESULT StressLog::Dump(ULONG64 outProcLog, const char* fileName, struct IDebugDataSpaces* memCallBack)
+{
+ ULONG64 g_hThisInst;
+ BOOL bDoGcHist = (fileName == NULL);
+ FILE* file = NULL;
+
+ // Fetch the circular buffer bookeeping data
+ StressLog inProcLog;
+ HRESULT hr = memCallBack->ReadVirtual(UL64_TO_CDA(outProcLog), &inProcLog, sizeof(StressLog), 0);
+ if (hr != S_OK)
+ {
+ return hr;
+ }
+ if (inProcLog.logs.Load() == NULL || inProcLog.moduleOffset == 0)
+ {
+ ExtOut ( "----- No thread logs in the image: The stress log was probably not initialized correctly. -----\n");
+ return S_FALSE;
+ }
+
+ g_hThisInst = (ULONG64) inProcLog.moduleOffset;
+
+ if (bDoGcHist)
+ {
+ GcHistClear();
+ }
+ else
+ {
+ ExtOut("Writing to file: %s\n", fileName);
+ ExtOut("Stress log in module 0x%p\n", SOS_PTR(g_hThisInst));
+ ExtOut("Stress log address = 0x%p\n", SOS_PTR(outProcLog));
+ }
+ // Fetch the circular buffers for each thread into the 'logs' list
+ ThreadStressLog* logs = 0;
+
+ CLRDATA_ADDRESS outProcPtr = TO_CDADDR(inProcLog.logs.Load());
+ ThreadStressLog* inProcPtr;
+ ThreadStressLog** logsPtr = &logs;
+ int threadCtr = 0;
+ unsigned __int64 lastTimeStamp = 0;// timestamp of last log entry
+
+ while(outProcPtr != 0) {
+ inProcPtr = new ThreadStressLog;
+ hr = memCallBack->ReadVirtual(outProcPtr, inProcPtr, sizeof (*inProcPtr), 0);
+ if (hr != S_OK || inProcPtr->chunkListHead == NULL)
+ {
+ delete inProcPtr;
+ goto FREE_MEM;
+ }
+
+ CLRDATA_ADDRESS outProcListHead = TO_CDADDR(inProcPtr->chunkListHead);
+ CLRDATA_ADDRESS outProcChunkPtr = outProcListHead;
+ StressLogChunk ** chunksPtr = &inProcPtr->chunkListHead;
+ StressLogChunk * inProcPrevChunkPtr = NULL;
+ BOOL curPtrInitialized = FALSE;
+ do
+ {
+ StressLogChunk * inProcChunkPtr = new StressLogChunk;
+ hr = memCallBack->ReadVirtual (outProcChunkPtr, inProcChunkPtr, sizeof (*inProcChunkPtr), NULL);
+ if (hr != S_OK || !inProcChunkPtr->IsValid ())
+ {
+ if (hr != S_OK)
+ ExtOut ("ReadVirtual failed with code hr = %x.\n", hr );
+ else
+ ExtOut ("Invalid stress log chunk: %p", SOS_PTR(outProcChunkPtr));
+
+ // Now cleanup
+ delete inProcChunkPtr;
+ // if this is the first time through, inProcPtr->chunkListHead may still contain
+ // the out-of-process value for the chunk pointer. NULL it to avoid AVs
+ if (TO_CDADDR(inProcPtr->chunkListHead) == outProcListHead)
+ inProcPtr->chunkListHead = NULL;
+ delete inProcPtr;
+ goto FREE_MEM;
+ }
+
+ if (!curPtrInitialized && outProcChunkPtr == TO_CDADDR(inProcPtr->curWriteChunk))
+ {
+ inProcPtr->curPtr = (StressMsg *)((BYTE *)inProcChunkPtr + ((BYTE *)inProcPtr->curPtr - (BYTE *)inProcPtr->curWriteChunk));
+ inProcPtr->curWriteChunk = inProcChunkPtr;
+ curPtrInitialized = TRUE;
+ }
+
+ outProcChunkPtr = TO_CDADDR(inProcChunkPtr->next);
+ *chunksPtr = inProcChunkPtr;
+ chunksPtr = &inProcChunkPtr->next;
+ inProcChunkPtr->prev = inProcPrevChunkPtr;
+ inProcPrevChunkPtr = inProcChunkPtr;
+
+ if (outProcChunkPtr == outProcListHead)
+ {
+ inProcChunkPtr->next = inProcPtr->chunkListHead;
+ inProcPtr->chunkListHead->prev = inProcChunkPtr;
+ inProcPtr->chunkListTail = inProcChunkPtr;
+ }
+ } while (outProcChunkPtr != outProcListHead);
+
+ if (!curPtrInitialized)
+ {
+ delete inProcPtr;
+ goto FREE_MEM;
+ }
+
+ // TODO: fix on 64 bit
+ inProcPtr->Activate ();
+ if (inProcPtr->readPtr->timeStamp > lastTimeStamp)
+ {
+ lastTimeStamp = inProcPtr->readPtr->timeStamp;
+ }
+
+ outProcPtr = TO_CDADDR(inProcPtr->next);
+ *logsPtr = inProcPtr;
+ logsPtr = &inProcPtr->next;
+ threadCtr++;
+ }
+
+ if (!bDoGcHist && ((file = fopen(fileName, "w")) == NULL))
+ {
+ hr = GetLastError();
+ goto FREE_MEM;
+ }
+ hr = S_FALSE; // return false if there are no message to print to the log
+
+ vDoOut(bDoGcHist, file, "STRESS LOG:\n"
+ " facilitiesToLog = 0x%x\n"
+ " levelToLog = %d\n"
+ " MaxLogSizePerThread = 0x%x (%d)\n"
+ " MaxTotalLogSize = 0x%x (%d)\n"
+ " CurrentTotalLogChunk = %d\n"
+ " ThreadsWithLogs = %d\n",
+ inProcLog.facilitiesToLog, inProcLog.levelToLog, inProcLog.MaxSizePerThread, inProcLog.MaxSizePerThread,
+ inProcLog.MaxSizeTotal, inProcLog.MaxSizeTotal, inProcLog.totalChunk.Load(), threadCtr);
+
+ FILETIME endTime;
+ double totalSecs;
+ totalSecs = ((double) (lastTimeStamp - inProcLog.startTimeStamp)) / inProcLog.tickFrequency;
+ toInt64(endTime) = toInt64(inProcLog.startTime) + ((__int64) (totalSecs * 1.0E7));
+
+ WCHAR timeBuff[64];
+ vDoOut(bDoGcHist, file, " Clock frequency = %5.3f GHz\n", inProcLog.tickFrequency / 1.0E9);
+ vDoOut(bDoGcHist, file, " Start time %S\n", getTime(&inProcLog.startTime, timeBuff, 64));
+ vDoOut(bDoGcHist, file, " Last message time %S\n", getTime(&endTime, timeBuff, 64));
+ vDoOut(bDoGcHist, file, " Total elapsed time %5.3f sec\n", totalSecs);
+
+ if (!bDoGcHist)
+ {
+ fprintf(file, "\nTHREAD TIMESTAMP FACILITY MESSAGE\n");
+ fprintf(file, " ID (sec from start)\n");
+ fprintf(file, "--------------------------------------------------------------------------------------\n");
+ }
+ char format[257];
+ format[256] = format[0] = 0;
+ void** args;
+ unsigned msgCtr;
+ msgCtr = 0;
+ for (;;)
+ {
+ ThreadStressLog* latestLog = logs->FindLatestThreadLog();
+
+ if (IsInterrupt())
+ {
+ vDoOut(bDoGcHist, file, "----- Interrupted by user -----\n");
+ break;
+ }
+
+ if (latestLog == 0)
+ {
+ break;
+ }
+
+ StressMsg* latestMsg = latestLog->readPtr;
+ if (latestMsg->formatOffset != 0 && !latestLog->CompletedDump())
+ {
+ TADDR taFmt = (latestMsg->formatOffset) + TO_TADDR(g_hThisInst);
+ hr = memCallBack->ReadVirtual(TO_CDADDR(taFmt), format, 256, 0);
+ if (hr != S_OK)
+ strcpy_s(format, _countof(format), "Could not read address of format string");
+
+ double deltaTime = ((double) (latestMsg->timeStamp - inProcLog.startTimeStamp)) / inProcLog.tickFrequency;
+ if (bDoGcHist)
+ {
+ if (strcmp(format, ThreadStressLog::TaskSwitchMsg()) == 0)
+ {
+ latestLog->threadId = (unsigned)(size_t)latestMsg->args[0];
+ }
+ GcHistAddLog(format, latestMsg);
+ }
+ else
+ {
+ if (strcmp(format, ThreadStressLog::TaskSwitchMsg()) == 0)
+ {
+ fprintf (file, "Task was switched from %x\n", (unsigned)(size_t)latestMsg->args[0]);
+ latestLog->threadId = (unsigned)(size_t)latestMsg->args[0];
+ }
+ else
+ {
+ args = latestMsg->args;
+ formatOutput(memCallBack, file, format, latestLog->threadId, deltaTime, latestMsg->facility, args);
+ }
+ }
+ msgCtr++;
+ }
+
+ latestLog->readPtr = latestLog->AdvanceRead();
+ if (latestLog->CompletedDump())
+ {
+ latestLog->readPtr = NULL;
+ if (!bDoGcHist)
+ {
+ fprintf(file, "------------ Last message from thread %x -----------\n", latestLog->threadId);
+ }
+ }
+
+ if (msgCtr % 64 == 0)
+ {
+ ExtOut("."); // to indicate progress
+ if (msgCtr % (64*64) == 0)
+ ExtOut("\n");
+ }
+ }
+ ExtOut("\n");
+
+ vDoOut(bDoGcHist, file, "---------------------------- %d total entries ------------------------------------\n", msgCtr);
+ if (!bDoGcHist)
+ {
+ fclose(file);
+ }
+
+FREE_MEM:
+ // clean up the 'logs' list
+ while (logs) {
+ ThreadStressLog* temp = logs;
+ logs = logs->next;
+ delete temp;
+ }
+
+ return hr;
+}
+
diff --git a/src/ToolBox/SOS/Strike/strike.cpp b/src/ToolBox/SOS/Strike/strike.cpp
new file mode 100644
index 0000000000..731e2f505d
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/strike.cpp
@@ -0,0 +1,14462 @@
+// 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.
+
+// ==++==
+//
+
+//
+// ==--==
+
+// ===========================================================================
+// STRIKE.CPP
+// ===========================================================================
+//
+// History:
+// 09/07/99 Microsoft Created
+//
+//************************************************************************************************
+// SOS is the native debugging extension designed to support investigations into CLR (mis-)
+// behavior by both users of the runtime as well as the code owners. It allows inspection of
+// internal structures, of user visible entities, as well as execution control.
+//
+// This is the main SOS file hosting the implementation of all the exposed commands. A good
+// starting point for understanding the semantics of these commands is the sosdocs.txt file.
+//
+// #CrossPlatformSOS
+// SOS currently supports cross platform debugging from x86 to ARM. It takes a different approach
+// from the DAC: whereas for the DAC we produce one binary for each supported host-target
+// architecture pair, for SOS we produce only one binary for each host architecture; this one
+// binary contains code for all supported target architectures. In doing this SOS depends on two
+// assumptions:
+// . that the debugger will load the appropriate DAC, and
+// . that the host and target word size is identical.
+// The second assumption is identical to the DAC assumption, and there will be considerable effort
+// required (in the EE, the DAC, and SOS) if we ever need to remove it.
+//
+// In an ideal world SOS would be able to retrieve all platform specific information it needs
+// either from the debugger or from DAC. However, SOS has taken some subtle and not so subtle
+// dependencies on the CLR and the target platform.
+// To resolve this problem, SOS now abstracts the target behind the IMachine interface, and uses
+// calls on IMachine to take target-specific actions. It implements X86Machine, ARMMachine, and
+// AMD64Machine. An instance of these exists in each appropriate host (e.g. the X86 version of SOS
+// contains instaces of X86Machine and ARMMachine, the ARM version contains an instance of
+// ARMMachine, and the AMD64 version contains an instance of AMD64Machine). The code included in
+// each version if determined by the SosTarget*** MSBuild symbols, and SOS_TARGET_*** conditional
+// compilation symbols (as specified in sos.targets).
+//
+// Most of the target specific code is hosted in disasm.h/.cpp, and disasmX86.cpp, disasmARM.cpp.
+// Some code currently under _TARGET_*** ifdefs may need to be reviewed/revisited.
+//
+// Issues:
+// The one-binary-per-host decision does have some drawbacks:
+// . Currently including system headers or even CLR headers will only account for the host
+// target, IOW, when building the X86 version of SOS, CONTEXT will refer to the X86 CONTEXT
+// structure, so we need to be careful when debugging ARM targets. The CONTEXT issue is
+// partially resolved by CROSS_PLATFORM_CONTEXT (there is still a need to be very careful
+// when handling arrays of CONTEXTs - see _EFN_StackTrace for details on this).
+// . For larger includes (e.g. GC info), we will need to include files in specific namespaces,
+// with specific _TARGET_*** macros defined in order to avoid name clashes and ensure correct
+// system types are used.
+// -----------------------------------------------------------------------------------------------
+
+#define DO_NOT_DISABLE_RAND //this is a standalone tool, and can use rand()
+
+#include <windows.h>
+#include <winver.h>
+#include <winternl.h>
+#include <psapi.h>
+#ifndef FEATURE_PAL
+#include <list>
+#endif // !FEATURE_PAL
+#include <wchar.h>
+
+#include "platformspecific.h"
+
+#define NOEXTAPI
+#define KDEXT_64BIT
+#include <wdbgexts.h>
+#undef DECLARE_API
+#undef StackTrace
+
+#include <dbghelp.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stddef.h>
+
+#include "strike.h"
+#include "sos.h"
+
+#ifndef STRESS_LOG
+#define STRESS_LOG
+#endif // STRESS_LOG
+#define STRESS_LOG_READONLY
+#include "stresslog.h"
+
+#include "util.h"
+
+#include "corhdr.h"
+#include "cor.h"
+#include "cordebug.h"
+#include "dacprivate.h"
+#include "corexcep.h"
+
+#define CORHANDLE_MASK 0x1
+#define SWITCHED_OUT_FIBER_OSID 0xbaadf00d;
+
+#define DEFINE_EXT_GLOBALS
+
+#include "data.h"
+#include "disasm.h"
+
+#include "predeftlsslot.h"
+
+#include "hillclimbing.h"
+
+#include "sos_md.h"
+
+#ifndef FEATURE_PAL
+
+#include "ExpressionNode.h"
+#include "WatchCmd.h"
+
+#include <set>
+#include <algorithm>
+#include <vector>
+
+#include "tls.h"
+
+typedef struct _VM_COUNTERS {
+ SIZE_T PeakVirtualSize;
+ SIZE_T VirtualSize;
+ ULONG PageFaultCount;
+ SIZE_T PeakWorkingSetSize;
+ SIZE_T WorkingSetSize;
+ SIZE_T QuotaPeakPagedPoolUsage;
+ SIZE_T QuotaPagedPoolUsage;
+ SIZE_T QuotaPeakNonPagedPoolUsage;
+ SIZE_T QuotaNonPagedPoolUsage;
+ SIZE_T PagefileUsage;
+ SIZE_T PeakPagefileUsage;
+} VM_COUNTERS;
+typedef VM_COUNTERS *PVM_COUNTERS;
+
+const PROCESSINFOCLASS ProcessVmCounters = static_cast<PROCESSINFOCLASS>(3);
+
+#endif // !FEATURE_PAL
+
+BOOL CallStatus;
+BOOL ControlC = FALSE;
+
+IMetaDataDispenserEx *pDisp = NULL;
+WCHAR g_mdName[mdNameLen];
+
+#ifndef FEATURE_PAL
+HMODULE g_hInstance = NULL;
+#include <vector>
+#include <algorithm>
+#endif // !FEATURE_PAL
+
+#ifdef _MSC_VER
+#pragma warning(disable:4244) // conversion from 'unsigned int' to 'unsigned short', possible loss of data
+#pragma warning(disable:4189) // local variable is initialized but not referenced
+#endif
+
+#ifdef FEATURE_PAL
+#define SOSPrefix ""
+#else
+#define SOSPrefix "!"
+#endif
+
+#if defined _X86_ && !defined FEATURE_PAL
+// disable FPO for X86 builds
+#pragma optimize("y", off)
+#endif
+
+#undef assert
+
+#ifdef _MSC_VER
+#pragma warning(default:4244)
+#pragma warning(default:4189)
+#endif
+
+#ifndef FEATURE_PAL
+#include "ntinfo.h"
+#endif // FEATURE_PAL
+
+#ifndef IfFailRet
+#define IfFailRet(EXPR) do { Status = (EXPR); if(FAILED(Status)) { return (Status); } } while (0)
+#endif
+
+#ifdef FEATURE_PAL
+
+#define NOTHROW
+#define MINIDUMP_NOT_SUPPORTED()
+
+#else // !FEATURE_PAL
+
+#define MINIDUMP_NOT_SUPPORTED() \
+ if (IsMiniDumpFile()) \
+ { \
+ ExtOut("This command is not supported in a minidump without full memory\n"); \
+ ExtOut("To try the command anyway, run !MinidumpMode 0\n"); \
+ return Status; \
+ }
+
+#define NOTHROW (std::nothrow)
+
+#include "safemath.h"
+
+DECLARE_API (MinidumpMode)
+{
+ INIT_API ();
+ DWORD_PTR Value=0;
+
+ CMDValue arg[] =
+ { // vptr, type
+ {&Value, COHEX}
+ };
+
+ size_t nArg;
+ if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+ if (nArg == 0)
+ {
+ // Print status of current mode
+ ExtOut("Current mode: %s - unsafe minidump commands are %s.\n",
+ g_InMinidumpSafeMode ? "1" : "0",
+ g_InMinidumpSafeMode ? "disabled" : "enabled");
+ }
+ else
+ {
+ if (Value != 0 && Value != 1)
+ {
+ ExtOut("Mode must be 0 or 1\n");
+ return Status;
+ }
+
+ g_InMinidumpSafeMode = (BOOL) Value;
+ ExtOut("Unsafe minidump commands are %s.\n",
+ g_InMinidumpSafeMode ? "disabled" : "enabled");
+ }
+
+ return Status;
+}
+
+#endif // FEATURE_PAL
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to get the MethodDesc for a given eip *
+* *
+\**********************************************************************/
+DECLARE_API(IP2MD)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ BOOL dml = FALSE;
+ TADDR IP = 0;
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE},
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&IP, COHEX},
+ };
+ size_t nArg;
+
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+ EnableDMLHolder dmlHolder(dml);
+
+ if (IP == 0)
+ {
+ ExtOut("%s is not IP\n", args);
+ return Status;
+ }
+
+ CLRDATA_ADDRESS cdaStart = TO_CDADDR(IP);
+ CLRDATA_ADDRESS pMD;
+
+
+ if ((Status = g_sos->GetMethodDescPtrFromIP(cdaStart, &pMD)) != S_OK)
+ {
+ ExtOut("Failed to request MethodData, not in JIT code range\n");
+ return Status;
+ }
+
+ DMLOut("MethodDesc: %s\n", DMLMethodDesc(pMD));
+ DumpMDInfo(TO_TADDR(pMD), cdaStart, FALSE /* fStackTraceFormat */);
+
+ WCHAR filename[MAX_LONGPATH];
+ ULONG linenum;
+ // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
+ ULONG symlines = 0;
+ if (SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
+ {
+ symlines &= SYMOPT_LOAD_LINES;
+ }
+
+ if (symlines != 0 &&
+ SUCCEEDED(GetLineByOffset(TO_CDADDR(IP), &linenum, filename, _countof(filename))))
+ {
+ ExtOut("Source file: %S @ %d\n", filename, linenum);
+ }
+
+ return Status;
+}
+
+// (MAX_STACK_FRAMES is also used by x86 to prevent infinite loops in _EFN_StackTrace)
+#define MAX_STACK_FRAMES 1000
+
+#ifdef _TARGET_WIN64_
+
+// I use a global set of frames for stack walking on win64 because the debugger's
+// GetStackTrace function doesn't provide a way to find out the total size of a stackwalk,
+// and I'd like to have a reasonably big maximum without overflowing the stack by declaring
+// the buffer locally and I also want to get a managed trace in a low memory environment
+// (so no dynamic allocation if possible).
+DEBUG_STACK_FRAME g_Frames[MAX_STACK_FRAMES];
+AMD64_CONTEXT g_X64FrameContexts[MAX_STACK_FRAMES];
+
+static HRESULT
+GetContextStackTrace(PULONG pnumFrames)
+{
+ PDEBUG_CONTROL4 debugControl4;
+ HRESULT hr;
+
+ // Do we have advanced capability?
+ if ((hr = g_ExtControl->QueryInterface(__uuidof(IDebugControl4), (void **)&debugControl4)) == S_OK)
+ {
+ // GetContextStackTrace fills g_X64FrameContexts as an array of
+ // contexts packed as target architecture contexts. We cannot
+ // safely cast this as an array of CROSS_PLATFORM_CONTEXT, since
+ // sizeof(CROSS_PLATFORM_CONTEXT) != sizeof(TGT_CONTEXT)
+ hr = debugControl4->GetContextStackTrace(
+ NULL,
+ 0,
+ g_Frames,
+ MAX_STACK_FRAMES,
+ g_X64FrameContexts,
+ MAX_STACK_FRAMES*g_targetMachine->GetContextSize(),
+ g_targetMachine->GetContextSize(),
+ pnumFrames);
+
+ debugControl4->Release();
+ }
+ return hr;
+}
+
+#endif // _TARGET_WIN64_
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function displays the stack trace. It looks at each DWORD *
+* on stack. If the DWORD is a return address, the symbol name or
+* managed function name is displayed. *
+* *
+\**********************************************************************/
+void DumpStackInternal(DumpStackFlag *pDSFlag)
+{
+ ReloadSymbolWithLineInfo();
+
+ ULONG64 StackOffset;
+ g_ExtRegisters->GetStackOffset (&StackOffset);
+ if (pDSFlag->top == 0) {
+ pDSFlag->top = TO_TADDR(StackOffset);
+ }
+ size_t value;
+ while (g_ExtData->ReadVirtual(TO_CDADDR(pDSFlag->top), &value, sizeof(size_t), NULL) != S_OK) {
+ if (IsInterrupt())
+ return;
+ pDSFlag->top = NextOSPageAddress(pDSFlag->top);
+ }
+
+#ifndef FEATURE_PAL
+ if (pDSFlag->end == 0) {
+ // Find the current stack range
+ NT_TIB teb;
+ ULONG64 dwTebAddr=0;
+
+ g_ExtSystem->GetCurrentThreadTeb(&dwTebAddr);
+ if (SafeReadMemory(TO_TADDR(dwTebAddr), &teb, sizeof(NT_TIB), NULL))
+ {
+ if (pDSFlag->top > TO_TADDR(teb.StackLimit)
+ && pDSFlag->top <= TO_TADDR(teb.StackBase))
+ {
+ if (pDSFlag->end == 0 || pDSFlag->end > TO_TADDR(teb.StackBase))
+ pDSFlag->end = TO_TADDR(teb.StackBase);
+ }
+ }
+ }
+#endif // FEATURE_PAL
+
+ if (pDSFlag->end == 0)
+ {
+ ExtOut("TEB information is not available so a stack size of 0xFFFF is assumed\n");
+ pDSFlag->end = pDSFlag->top + 0xFFFF;
+ }
+
+ if (pDSFlag->end < pDSFlag->top)
+ {
+ ExtOut("Wrong option: stack selection wrong\n");
+ return;
+ }
+
+ DumpStackWorker(*pDSFlag);
+}
+
+
+DECLARE_API(DumpStack)
+{
+ INIT_API_NO_RET_ON_FAILURE();
+
+ MINIDUMP_NOT_SUPPORTED();
+
+ DumpStackFlag DSFlag;
+ DSFlag.fEEonly = FALSE;
+ DSFlag.fSuppressSrcInfo = FALSE;
+ DSFlag.top = 0;
+ DSFlag.end = 0;
+
+ BOOL dml = FALSE;
+ CMDOption option[] = {
+ // name, vptr, type, hasValue
+ {"-EE", &DSFlag.fEEonly, COBOOL, FALSE},
+ {"-n", &DSFlag.fSuppressSrcInfo, COBOOL, FALSE},
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE}
+#endif
+ };
+ CMDValue arg[] = {
+ // vptr, type
+ {&DSFlag.top, COHEX},
+ {&DSFlag.end, COHEX}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ return Status;
+
+ // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
+ ULONG symlines = 0;
+ if (!DSFlag.fSuppressSrcInfo && SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
+ {
+ symlines &= SYMOPT_LOAD_LINES;
+ }
+ DSFlag.fSuppressSrcInfo = DSFlag.fSuppressSrcInfo || (symlines == 0);
+
+ EnableDMLHolder enabledml(dml);
+
+ ULONG id = 0;
+ g_ExtSystem->GetCurrentThreadSystemId(&id);
+ ExtOut("OS Thread Id: 0x%x ", id);
+ g_ExtSystem->GetCurrentThreadId(&id);
+ ExtOut("(%d)\n", id);
+
+ DumpStackInternal(&DSFlag);
+ return Status;
+}
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function displays the stack trace for threads that EE knows *
+* from ThreadStore. *
+* *
+\**********************************************************************/
+DECLARE_API (EEStack)
+{
+ INIT_API();
+
+ MINIDUMP_NOT_SUPPORTED();
+
+ DumpStackFlag DSFlag;
+ DSFlag.fEEonly = FALSE;
+ DSFlag.fSuppressSrcInfo = FALSE;
+ DSFlag.top = 0;
+ DSFlag.end = 0;
+
+ BOOL bShortList = FALSE;
+ BOOL dml = FALSE;
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-EE", &DSFlag.fEEonly, COBOOL, FALSE},
+ {"-short", &bShortList, COBOOL, FALSE},
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE}
+#endif
+ };
+
+ if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder enableDML(dml);
+
+ ULONG Tid;
+ g_ExtSystem->GetCurrentThreadId(&Tid);
+
+ DacpThreadStoreData ThreadStore;
+ if ((Status = ThreadStore.Request(g_sos)) != S_OK)
+ {
+ ExtOut("Failed to request ThreadStore\n");
+ return Status;
+ }
+
+ CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
+ while (CurThread)
+ {
+ if (IsInterrupt())
+ break;
+
+ DacpThreadData Thread;
+ if ((Status = Thread.Request(g_sos, CurThread)) != S_OK)
+ {
+ ExtOut("Failed to request Thread at %p\n", CurThread);
+ return Status;
+ }
+
+ ULONG id=0;
+ if (g_ExtSystem->GetThreadIdBySystemId (Thread.osThreadId, &id) != S_OK)
+ {
+ CurThread = Thread.nextThread;
+ continue;
+ }
+
+ ExtOut("---------------------------------------------\n");
+ ExtOut("Thread %3d\n", id);
+ BOOL doIt = FALSE;
+
+
+#define TS_Hijacked 0x00000080
+
+ if (!bShortList)
+ {
+ doIt = TRUE;
+ }
+ else if ((Thread.lockCount > 0) || (Thread.state & TS_Hijacked))
+ {
+ // TODO: bring back || (int)vThread.m_pFrame != -1 {
+ doIt = TRUE;
+ }
+ else
+ {
+ ULONG64 IP;
+ g_ExtRegisters->GetInstructionOffset (&IP);
+ JITTypes jitType;
+ TADDR methodDesc;
+ TADDR gcinfoAddr;
+ IP2MethodDesc (TO_TADDR(IP), methodDesc, jitType, gcinfoAddr);
+ if (methodDesc)
+ {
+ doIt = TRUE;
+ }
+ }
+
+ if (doIt)
+ {
+ g_ExtSystem->SetCurrentThreadId(id);
+ DSFlag.top = 0;
+ DSFlag.end = 0;
+ DumpStackInternal(&DSFlag);
+ }
+
+ CurThread = Thread.nextThread;
+ }
+
+ g_ExtSystem->SetCurrentThreadId(Tid);
+ return Status;
+}
+
+HRESULT DumpStackObjectsRaw(size_t nArg, __in_z LPSTR exprBottom, __in_z LPSTR exprTop, BOOL bVerify)
+{
+ size_t StackTop = 0;
+ size_t StackBottom = 0;
+ if (nArg==0)
+ {
+ ULONG64 StackOffset;
+ g_ExtRegisters->GetStackOffset(&StackOffset);
+
+ StackTop = TO_TADDR(StackOffset);
+ }
+ else
+ {
+ StackTop = GetExpression(exprTop);
+ if (StackTop == 0)
+ {
+ ExtOut("wrong option: %s\n", exprTop);
+ return E_FAIL;
+ }
+
+ if (nArg==2)
+ {
+ StackBottom = GetExpression(exprBottom);
+ if (StackBottom == 0)
+ {
+ ExtOut("wrong option: %s\n", exprBottom);
+ return E_FAIL;
+ }
+ }
+ }
+
+#ifndef FEATURE_PAL
+ NT_TIB teb;
+ ULONG64 dwTebAddr=0;
+ HRESULT hr = g_ExtSystem->GetCurrentThreadTeb(&dwTebAddr);
+ if (SUCCEEDED(hr) && SafeReadMemory (TO_TADDR(dwTebAddr), &teb, sizeof (NT_TIB), NULL))
+ {
+ if (StackTop > TO_TADDR(teb.StackLimit) && StackTop <= TO_TADDR(teb.StackBase))
+ {
+ if (StackBottom == 0 || StackBottom > TO_TADDR(teb.StackBase))
+ StackBottom = TO_TADDR(teb.StackBase);
+ }
+ }
+#endif
+
+ if (StackBottom == 0)
+ StackBottom = StackTop + 0xFFFF;
+
+ if (StackBottom < StackTop)
+ {
+ ExtOut("Wrong option: stack selection wrong\n");
+ return E_FAIL;
+ }
+
+ // We can use the gc snapshot to eliminate object addresses that are
+ // not on the gc heap.
+ if (!g_snapshot.Build())
+ {
+ ExtOut("Unable to determine bounds of gc heap\n");
+ return E_FAIL;
+ }
+
+ // Print thread ID.
+ ULONG id = 0;
+ g_ExtSystem->GetCurrentThreadSystemId (&id);
+ ExtOut("OS Thread Id: 0x%x ", id);
+ g_ExtSystem->GetCurrentThreadId (&id);
+ ExtOut("(%d)\n", id);
+
+ DumpStackObjectsHelper(StackTop, StackBottom, bVerify);
+ return S_OK;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the address and name of all *
+* Managed Objects on the stack. *
+* *
+\**********************************************************************/
+DECLARE_API(DumpStackObjects)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+ StringHolder exprTop, exprBottom;
+
+ BOOL bVerify = FALSE;
+ BOOL dml = FALSE;
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-verify", &bVerify, COBOOL, FALSE},
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE}
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&exprTop.data, COSTRING},
+ {&exprBottom.data, COSTRING}
+ };
+ size_t nArg;
+
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder enableDML(dml);
+
+ return DumpStackObjectsRaw(nArg, exprBottom.data, exprTop.data, bVerify);
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the contents of a MethodDesc *
+* for a given address *
+* *
+\**********************************************************************/
+DECLARE_API(DumpMD)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ DWORD_PTR dwStartAddr = NULL;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE},
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&dwStartAddr, COHEX},
+ };
+ size_t nArg;
+
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+
+ DumpMDInfo(dwStartAddr);
+
+ return Status;
+}
+
+BOOL GatherDynamicInfo(TADDR DynamicMethodObj, DacpObjectData *codeArray,
+ DacpObjectData *tokenArray, TADDR *ptokenArrayAddr)
+{
+ BOOL bRet = FALSE;
+ int iOffset;
+ DacpObjectData objData; // temp object
+
+ if (codeArray == NULL || tokenArray == NULL)
+ return bRet;
+
+ if (objData.Request(g_sos, TO_CDADDR(DynamicMethodObj)) != S_OK)
+ return bRet;
+
+ iOffset = GetObjFieldOffset(DynamicMethodObj, objData.MethodTable, W("m_resolver"));
+ if (iOffset <= 0)
+ return bRet;
+
+ TADDR resolverPtr;
+ if (FAILED(MOVE(resolverPtr, DynamicMethodObj + iOffset)))
+ return bRet;
+
+ if (objData.Request(g_sos, TO_CDADDR(resolverPtr)) != S_OK)
+ return bRet;
+
+ iOffset = GetObjFieldOffset(resolverPtr, objData.MethodTable, W("m_code"));
+ if (iOffset <= 0)
+ return bRet;
+
+ TADDR codePtr;
+ if (FAILED(MOVE(codePtr, resolverPtr + iOffset)))
+ return bRet;
+
+ if (codeArray->Request(g_sos, TO_CDADDR(codePtr)) != S_OK)
+ return bRet;
+
+ if (codeArray->dwComponentSize != 1)
+ return bRet;
+
+ // We also need the resolution table
+ iOffset = GetObjFieldOffset (resolverPtr, objData.MethodTable, W("m_scope"));
+ if (iOffset <= 0)
+ return bRet;
+
+ TADDR scopePtr;
+ if (FAILED(MOVE(scopePtr, resolverPtr + iOffset)))
+ return bRet;
+
+ if (objData.Request(g_sos, TO_CDADDR(scopePtr)) != S_OK)
+ return bRet;
+
+ iOffset = GetObjFieldOffset (scopePtr, objData.MethodTable, W("m_tokens"));
+ if (iOffset <= 0)
+ return bRet;
+
+ TADDR tokensPtr;
+ if (FAILED(MOVE(tokensPtr, scopePtr + iOffset)))
+ return bRet;
+
+ if (objData.Request(g_sos, TO_CDADDR(tokensPtr)) != S_OK)
+ return bRet;
+
+ iOffset = GetObjFieldOffset(tokensPtr, objData.MethodTable, W("_items"));
+ if (iOffset <= 0)
+ return bRet;
+
+ TADDR itemsPtr;
+ MOVE (itemsPtr, tokensPtr + iOffset);
+
+ *ptokenArrayAddr = itemsPtr;
+
+ if (tokenArray->Request(g_sos, TO_CDADDR(itemsPtr)) != S_OK)
+ return bRet;
+
+ bRet = TRUE; // whew.
+ return bRet;
+}
+
+DECLARE_API(DumpIL)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+ DWORD_PTR dwStartAddr = NULL;
+ DWORD_PTR dwDynamicMethodObj = NULL;
+ BOOL dml = FALSE;
+ BOOL fILPointerDirectlySpecified = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"/d", &dml, COBOOL, FALSE},
+ {"/i", &fILPointerDirectlySpecified, COBOOL, FALSE},
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&dwStartAddr, COHEX},
+ };
+ size_t nArg;
+
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ if (dwStartAddr == NULL)
+ {
+ ExtOut("Must pass a valid expression\n");
+ return Status;
+ }
+
+ if (fILPointerDirectlySpecified)
+ {
+ return DecodeILFromAddress(NULL, dwStartAddr);
+ }
+
+ if (!g_snapshot.Build())
+ {
+ ExtOut("Unable to build snapshot of the garbage collector state\n");
+ return Status;
+ }
+
+ if (g_snapshot.GetHeap(dwStartAddr) != NULL)
+ {
+ dwDynamicMethodObj = dwStartAddr;
+ }
+
+ if (dwDynamicMethodObj == NULL)
+ {
+ // We have been given a MethodDesc
+ DacpMethodDescData MethodDescData;
+ if (MethodDescData.Request(g_sos, TO_CDADDR(dwStartAddr)) != S_OK)
+ {
+ ExtOut("%p is not a MethodDesc\n", SOS_PTR(dwStartAddr));
+ return Status;
+ }
+
+ if (MethodDescData.bIsDynamic && MethodDescData.managedDynamicMethodObject)
+ {
+ dwDynamicMethodObj = TO_TADDR(MethodDescData.managedDynamicMethodObject);
+ if (dwDynamicMethodObj == NULL)
+ {
+ ExtOut("Unable to print IL for DynamicMethodDesc %p\n", SOS_PTR(dwDynamicMethodObj));
+ return Status;
+ }
+ }
+ else
+ {
+ // This is not a dynamic method, print the IL for it.
+ // Get the module
+ DacpModuleData dmd;
+ if (dmd.Request(g_sos, MethodDescData.ModulePtr) != S_OK)
+ {
+ ExtOut("Unable to get module\n");
+ return Status;
+ }
+
+ ToRelease<IMetaDataImport> pImport = MDImportForModule(&dmd);
+ if (pImport == NULL)
+ {
+ ExtOut("bad import\n");
+ return Status;
+ }
+
+ ULONG pRva;
+ DWORD dwFlags;
+ if (pImport->GetRVA(MethodDescData.MDToken, &pRva, &dwFlags) != S_OK)
+ {
+ ExtOut("error in import\n");
+ return Status;
+ }
+
+ CLRDATA_ADDRESS ilAddrClr;
+ if (g_sos->GetILForModule(MethodDescData.ModulePtr, pRva, &ilAddrClr) != S_OK)
+ {
+ ExtOut("FindIL failed\n");
+ return Status;
+ }
+
+ TADDR ilAddr = TO_TADDR(ilAddrClr);
+ IfFailRet(DecodeILFromAddress(pImport, ilAddr));
+ }
+ }
+
+ if (dwDynamicMethodObj != NULL)
+ {
+ // We have a DynamicMethod managed object, let us visit the town and paint.
+ DacpObjectData codeArray;
+ DacpObjectData tokenArray;
+ DWORD_PTR tokenArrayAddr;
+ if (!GatherDynamicInfo (dwDynamicMethodObj, &codeArray, &tokenArray, &tokenArrayAddr))
+ {
+ DMLOut("Error gathering dynamic info from object at %s.\n", DMLObject(dwDynamicMethodObj));
+ return Status;
+ }
+
+ // Read the memory into a local buffer
+ BYTE *pArray = new NOTHROW BYTE[(SIZE_T)codeArray.dwNumComponents];
+ if (pArray == NULL)
+ {
+ ExtOut("Not enough memory to read IL\n");
+ return Status;
+ }
+
+ Status = g_ExtData->ReadVirtual(UL64_TO_CDA(codeArray.ArrayDataPtr), pArray, (ULONG)codeArray.dwNumComponents, NULL);
+ if (Status != S_OK)
+ {
+ ExtOut("Failed to read memory\n");
+ delete [] pArray;
+ return Status;
+ }
+
+ // Now we have a local copy of the IL, and a managed array for token resolution.
+ // Visit our IL parser with this info.
+ ExtOut("This is dynamic IL. Exception info is not reported at this time.\n");
+ ExtOut("If a token is unresolved, run \"!do <addr>\" on the addr given\n");
+ ExtOut("in parenthesis. You can also look at the token table yourself, by\n");
+ ExtOut("running \"!DumpArray %p\".\n\n", SOS_PTR(tokenArrayAddr));
+ DecodeDynamicIL(pArray, (ULONG)codeArray.dwNumComponents, tokenArray);
+
+ delete [] pArray;
+ }
+ return Status;
+}
+
+void DumpSigWorker (
+ DWORD_PTR dwSigAddr,
+ DWORD_PTR dwModuleAddr,
+ BOOL fMethod)
+{
+ //
+ // Find the length of the signature and copy it into the debugger process.
+ //
+
+ ULONG cbSig = 0;
+ const ULONG cbSigInc = 256;
+ ArrayHolder<COR_SIGNATURE> pSig = new NOTHROW COR_SIGNATURE[cbSigInc];
+ if (pSig == NULL)
+ {
+ ReportOOM();
+ return;
+ }
+
+ CQuickBytes sigString;
+ for (;;)
+ {
+ if (IsInterrupt())
+ return;
+
+ ULONG cbCopied;
+ if (!SafeReadMemory(TO_TADDR(dwSigAddr + cbSig), pSig + cbSig, cbSigInc, &cbCopied))
+ return;
+ cbSig += cbCopied;
+
+ sigString.ReSize(0);
+ GetSignatureStringResults result;
+ if (fMethod)
+ result = GetMethodSignatureString(pSig, cbSig, dwModuleAddr, &sigString);
+ else
+ result = GetSignatureString(pSig, cbSig, dwModuleAddr, &sigString);
+
+ if (GSS_ERROR == result)
+ return;
+
+ if (GSS_SUCCESS == result)
+ break;
+
+ // If we didn't get the full amount back, and we failed to parse the
+ // signature, it's not valid because of insufficient data
+ if (cbCopied < 256)
+ {
+ ExtOut("Invalid signature\n");
+ return;
+ }
+
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:6280) // "Suppress PREFast warning about mismatch alloc/free"
+#endif
+
+ PCOR_SIGNATURE pSigNew = (PCOR_SIGNATURE)realloc(pSig, cbSig+cbSigInc);
+
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+ if (pSigNew == NULL)
+ {
+ ExtOut("Out of memory\n");
+ return;
+ }
+
+ pSig = pSigNew;
+ }
+
+ ExtOut("%S\n", (PCWSTR)sigString.Ptr());
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump a signature object. *
+* *
+\**********************************************************************/
+DECLARE_API(DumpSig)
+{
+ INIT_API();
+
+ MINIDUMP_NOT_SUPPORTED();
+
+ //
+ // Fetch arguments
+ //
+
+ StringHolder sigExpr;
+ StringHolder moduleExpr;
+ CMDValue arg[] =
+ {
+ {&sigExpr.data, COSTRING},
+ {&moduleExpr.data, COSTRING}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+ if (nArg != 2)
+ {
+ ExtOut("!DumpSig <sigaddr> <moduleaddr>\n");
+ return Status;
+ }
+
+ DWORD_PTR dwSigAddr = GetExpression(sigExpr.data);
+ DWORD_PTR dwModuleAddr = GetExpression(moduleExpr.data);
+
+ if (dwSigAddr == 0 || dwModuleAddr == 0)
+ {
+ ExtOut("Invalid parameters %s %s\n", sigExpr.data, moduleExpr.data);
+ return Status;
+ }
+
+ DumpSigWorker(dwSigAddr, dwModuleAddr, TRUE);
+ return Status;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump a portion of a signature object. *
+* *
+\**********************************************************************/
+DECLARE_API(DumpSigElem)
+{
+ INIT_API();
+
+ MINIDUMP_NOT_SUPPORTED();
+
+
+ //
+ // Fetch arguments
+ //
+
+ StringHolder sigExpr;
+ StringHolder moduleExpr;
+ CMDValue arg[] =
+ {
+ {&sigExpr.data, COSTRING},
+ {&moduleExpr.data, COSTRING}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ if (nArg != 2)
+ {
+ ExtOut("!DumpSigElem <sigaddr> <moduleaddr>\n");
+ return Status;
+ }
+
+ DWORD_PTR dwSigAddr = GetExpression(sigExpr.data);
+ DWORD_PTR dwModuleAddr = GetExpression(moduleExpr.data);
+
+ if (dwSigAddr == 0 || dwModuleAddr == 0)
+ {
+ ExtOut("Invalid parameters %s %s\n", sigExpr.data, moduleExpr.data);
+ return Status;
+ }
+
+ DumpSigWorker(dwSigAddr, dwModuleAddr, FALSE);
+ return Status;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the contents of an EEClass from *
+* a given address
+* *
+\**********************************************************************/
+DECLARE_API(DumpClass)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ DWORD_PTR dwStartAddr = 0;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE},
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&dwStartAddr, COHEX}
+ };
+
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ if (nArg == 0)
+ {
+ ExtOut("Missing EEClass address\n");
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+
+ CLRDATA_ADDRESS methodTable;
+ if ((Status=g_sos->GetMethodTableForEEClass(TO_CDADDR(dwStartAddr), &methodTable)) != S_OK)
+ {
+ ExtOut("Invalid EEClass address\n");
+ return Status;
+ }
+
+ DacpMethodTableData mtdata;
+ if ((Status=mtdata.Request(g_sos, TO_CDADDR(methodTable)))!=S_OK)
+ {
+ ExtOut("EEClass has an invalid MethodTable address\n");
+ return Status;
+ }
+
+ sos::MethodTable mt = TO_TADDR(methodTable);
+ ExtOut("Class Name: %S\n", mt.GetName());
+
+ WCHAR fileName[MAX_LONGPATH];
+ FileNameForModule(TO_TADDR(mtdata.Module), fileName);
+ ExtOut("mdToken: %p\n", mtdata.cl);
+ ExtOut("File: %S\n", fileName);
+
+ CLRDATA_ADDRESS ParentEEClass = NULL;
+ if (mtdata.ParentMethodTable)
+ {
+ DacpMethodTableData mtdataparent;
+ if ((Status=mtdataparent.Request(g_sos, TO_CDADDR(mtdata.ParentMethodTable)))!=S_OK)
+ {
+ ExtOut("EEClass has an invalid MethodTable address\n");
+ return Status;
+ }
+ ParentEEClass = mtdataparent.Class;
+ }
+
+ DMLOut("Parent Class: %s\n", DMLClass(ParentEEClass));
+ DMLOut("Module: %s\n", DMLModule(mtdata.Module));
+ DMLOut("Method Table: %s\n", DMLMethodTable(methodTable));
+ ExtOut("Vtable Slots: %x\n", mtdata.wNumVirtuals);
+ ExtOut("Total Method Slots: %x\n", mtdata.wNumVtableSlots);
+ ExtOut("Class Attributes: %x ", mtdata.dwAttrClass);
+
+ if (IsTdInterface(mtdata.dwAttrClass))
+ ExtOut("Interface, ");
+ if (IsTdAbstract(mtdata.dwAttrClass))
+ ExtOut("Abstract, ");
+ if (IsTdImport(mtdata.dwAttrClass))
+ ExtOut("ComImport, ");
+
+ ExtOut("\n");
+
+ DacpMethodTableTransparencyData transparency;
+ if (SUCCEEDED(transparency.Request(g_sos, methodTable)))
+ {
+ ExtOut("Transparency: %s\n", GetTransparency(transparency));
+ }
+
+ DacpMethodTableFieldData vMethodTableFields;
+ if (SUCCEEDED(vMethodTableFields.Request(g_sos, methodTable)))
+ {
+ ExtOut("NumInstanceFields: %x\n", vMethodTableFields.wNumInstanceFields);
+ ExtOut("NumStaticFields: %x\n", vMethodTableFields.wNumStaticFields);
+
+ if (vMethodTableFields.wNumThreadStaticFields != 0)
+ {
+ ExtOut("NumThreadStaticFields: %x\n", vMethodTableFields.wNumThreadStaticFields);
+ }
+
+
+ if (vMethodTableFields.wContextStaticsSize)
+ {
+ ExtOut("ContextStaticOffset: %x\n", vMethodTableFields.wContextStaticOffset);
+ ExtOut("ContextStaticsSize: %x\n", vMethodTableFields.wContextStaticsSize);
+ }
+
+
+ if (vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0)
+ {
+ DisplayFields(methodTable, &mtdata, &vMethodTableFields, NULL, TRUE, FALSE);
+ }
+ }
+
+ return Status;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the contents of a MethodTable *
+* from a given address *
+* *
+\**********************************************************************/
+DECLARE_API(DumpMT)
+{
+ DWORD_PTR dwStartAddr=0;
+ DWORD_PTR dwOriginalAddr;
+
+ INIT_API();
+
+ MINIDUMP_NOT_SUPPORTED();
+
+ BOOL bDumpMDTable = FALSE;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-MD", &bDumpMDTable, COBOOL, FALSE},
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE}
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&dwStartAddr, COHEX}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ TableOutput table(2, 16, AlignLeft, false);
+
+ if (nArg == 0)
+ {
+ Print("Missing MethodTable address\n");
+ return Status;
+ }
+
+ dwOriginalAddr = dwStartAddr;
+ dwStartAddr = dwStartAddr&~3;
+
+ if (!IsMethodTable(dwStartAddr))
+ {
+ Print(dwOriginalAddr, " is not a MethodTable\n");
+ return Status;
+ }
+
+ DacpMethodTableData vMethTable;
+ vMethTable.Request(g_sos, TO_CDADDR(dwStartAddr));
+
+ if (vMethTable.bIsFree)
+ {
+ Print("Free MethodTable\n");
+ return Status;
+ }
+
+ table.WriteRow("EEClass:", EEClassPtr(vMethTable.Class));
+
+ table.WriteRow("Module:", ModulePtr(vMethTable.Module));
+
+ sos::MethodTable mt = (TADDR)dwStartAddr;
+ table.WriteRow("Name:", mt.GetName());
+
+ WCHAR fileName[MAX_LONGPATH];
+ FileNameForModule(TO_TADDR(vMethTable.Module), fileName);
+ table.WriteRow("mdToken:", Pointer(vMethTable.cl));
+ table.WriteRow("File:", fileName[0] ? fileName : W("Unknown Module"));
+
+ table.WriteRow("BaseSize:", PrefixHex(vMethTable.BaseSize));
+ table.WriteRow("ComponentSize:", PrefixHex(vMethTable.ComponentSize));
+ table.WriteRow("Slots in VTable:", Decimal(vMethTable.wNumMethods));
+
+ table.SetColWidth(0, 29);
+ table.WriteRow("Number of IFaces in IFaceMap:", Decimal(vMethTable.wNumInterfaces));
+
+ if (bDumpMDTable)
+ {
+ table.ReInit(4, POINTERSIZE_HEX, AlignRight);
+ table.SetColAlignment(3, AlignLeft);
+ table.SetColWidth(2, 6);
+
+ Print("--------------------------------------\n");
+ Print("MethodDesc Table\n");
+
+ table.WriteRow("Entry", "MethodDesc", "JIT", "Name");
+
+ for (DWORD n = 0; n < vMethTable.wNumMethods; n++)
+ {
+ JITTypes jitType;
+ DWORD_PTR methodDesc=0;
+ DWORD_PTR gcinfoAddr;
+
+ CLRDATA_ADDRESS entry;
+ if (g_sos->GetMethodTableSlot(dwStartAddr, n, &entry) != S_OK)
+ {
+ PrintLn("<error getting slot ", Decimal(n), ">");
+ continue;
+ }
+
+ IP2MethodDesc((DWORD_PTR)entry, methodDesc, jitType, gcinfoAddr);
+ table.WriteColumn(0, entry);
+ table.WriteColumn(1, MethodDescPtr(methodDesc));
+
+ if (jitType == TYPE_UNKNOWN && methodDesc != NULL)
+ {
+ // We can get a more accurate jitType from NativeCodeAddr of the methoddesc,
+ // because the methodtable entry hasn't always been patched.
+ DacpMethodDescData tmpMethodDescData;
+ if (tmpMethodDescData.Request(g_sos, TO_CDADDR(methodDesc)) == S_OK)
+ {
+ DacpCodeHeaderData codeHeaderData;
+ if (codeHeaderData.Request(g_sos,tmpMethodDescData.NativeCodeAddr) == S_OK)
+ {
+ jitType = (JITTypes) codeHeaderData.JITType;
+ }
+ }
+ }
+
+ const char *pszJitType = "NONE";
+ if (jitType == TYPE_JIT)
+ pszJitType = "JIT";
+ else if (jitType == TYPE_PJIT)
+ pszJitType = "PreJIT";
+ else
+ {
+ DacpMethodDescData MethodDescData;
+ if (MethodDescData.Request(g_sos, TO_CDADDR(methodDesc)) == S_OK)
+ {
+ // Is it an fcall?
+ if ((TO_TADDR(MethodDescData.NativeCodeAddr) >= TO_TADDR(moduleInfo[MSCORWKS].baseAddr)) &&
+ ((TO_TADDR(MethodDescData.NativeCodeAddr) < TO_TADDR(moduleInfo[MSCORWKS].baseAddr + moduleInfo[MSCORWKS].size))))
+ {
+ pszJitType = "FCALL";
+ }
+ }
+ }
+
+ table.WriteColumn(2, pszJitType);
+
+ NameForMD_s(methodDesc,g_mdName,mdNameLen);
+ table.WriteColumn(3, g_mdName);
+ }
+ }
+ return Status;
+}
+
+extern size_t Align (size_t nbytes);
+
+HRESULT PrintVC(TADDR taMT, TADDR taObject, BOOL bPrintFields = TRUE)
+{
+ HRESULT Status;
+ DacpMethodTableData mtabledata;
+ if ((Status = mtabledata.Request(g_sos, TO_CDADDR(taMT)))!=S_OK)
+ return Status;
+
+ size_t size = mtabledata.BaseSize;
+ if ((Status=g_sos->GetMethodTableName(TO_CDADDR(taMT), mdNameLen, g_mdName, NULL))!=S_OK)
+ return Status;
+
+ ExtOut("Name: %S\n", g_mdName);
+ DMLOut("MethodTable: %s\n", DMLMethodTable(taMT));
+ DMLOut("EEClass: %s\n", DMLClass(mtabledata.Class));
+ ExtOut("Size: %d(0x%x) bytes\n", size, size);
+
+ FileNameForModule(TO_TADDR(mtabledata.Module), g_mdName);
+ ExtOut("File: %S\n", g_mdName[0] ? g_mdName : W("Unknown Module"));
+
+ if (bPrintFields)
+ {
+ DacpMethodTableFieldData vMethodTableFields;
+ if ((Status = vMethodTableFields.Request(g_sos,TO_CDADDR(taMT)))!=S_OK)
+ return Status;
+
+ ExtOut("Fields:\n");
+
+ if (vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0)
+ DisplayFields(TO_CDADDR(taMT), &mtabledata, &vMethodTableFields, taObject, TRUE, TRUE);
+ }
+
+ return S_OK;
+}
+
+void PrintRuntimeTypeInfo(TADDR p_rtObject, const DacpObjectData & rtObjectData)
+{
+ // Get the method table
+ int iOffset = GetObjFieldOffset(p_rtObject, rtObjectData.MethodTable, W("m_handle"));
+ if (iOffset > 0)
+ {
+ TADDR mtPtr;
+ if (SUCCEEDED(GetMTOfObject(p_rtObject + iOffset, &mtPtr)))
+ {
+ sos::MethodTable mt = mtPtr;
+ ExtOut("Type Name: %S\n", mt.GetName());
+ DMLOut("Type MT: %s\n", DMLMethodTable(mtPtr));
+ }
+ }
+}
+
+HRESULT PrintObj(TADDR taObj, BOOL bPrintFields = TRUE)
+{
+ if (!sos::IsObject(taObj, true))
+ {
+ ExtOut("<Note: this object has an invalid CLASS field>\n");
+ }
+
+ DacpObjectData objData;
+ HRESULT Status;
+ if ((Status=objData.Request(g_sos, TO_CDADDR(taObj))) != S_OK)
+ {
+ ExtOut("Invalid object\n");
+ return Status;
+ }
+
+ if (objData.ObjectType==OBJ_FREE)
+ {
+ ExtOut("Free Object\n");
+ DWORD_PTR size = (DWORD_PTR)objData.Size;
+ ExtOut("Size: %" POINTERSIZE_TYPE "d(0x%" POINTERSIZE_TYPE "x) bytes\n", size, size);
+ return S_OK;
+ }
+
+ sos::Object obj = taObj;
+ ExtOut("Name: %S\n", obj.GetTypeName());
+ DMLOut("MethodTable: %s\n", DMLMethodTable(objData.MethodTable));
+
+
+ DacpMethodTableData mtabledata;
+ if ((Status=mtabledata.Request(g_sos,objData.MethodTable)) == S_OK)
+ {
+ DMLOut("EEClass: %s\n", DMLClass(mtabledata.Class));
+ }
+ else
+ {
+ ExtOut("Invalid EEClass address\n");
+ return Status;
+ }
+
+ if (objData.RCW != NULL)
+ {
+ DMLOut("RCW: %s\n", DMLRCWrapper(objData.RCW));
+ }
+ if (objData.CCW != NULL)
+ {
+ DMLOut("CCW: %s\n", DMLCCWrapper(objData.CCW));
+ }
+
+ DWORD_PTR size = (DWORD_PTR)objData.Size;
+ ExtOut("Size: %" POINTERSIZE_TYPE "d(0x%" POINTERSIZE_TYPE "x) bytes\n", size, size);
+
+ if (_wcscmp(obj.GetTypeName(), W("System.RuntimeType")) == 0)
+ {
+ PrintRuntimeTypeInfo(taObj, objData);
+ }
+
+ if (_wcscmp(obj.GetTypeName(), W("System.RuntimeType+RuntimeTypeCache")) == 0)
+ {
+ // Get the method table
+ int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, W("m_runtimeType"));
+ if (iOffset > 0)
+ {
+ TADDR rtPtr;
+ if (MOVE(rtPtr, taObj + iOffset) == S_OK)
+ {
+ DacpObjectData rtObjectData;
+ if ((Status=rtObjectData.Request(g_sos, TO_CDADDR(rtPtr))) != S_OK)
+ {
+ ExtOut("Error when reading RuntimeType field\n");
+ return Status;
+ }
+
+ PrintRuntimeTypeInfo(rtPtr, rtObjectData);
+ }
+ }
+ }
+
+ if (objData.ObjectType==OBJ_ARRAY)
+ {
+ ExtOut("Array: Rank %d, Number of elements %" POINTERSIZE_TYPE "d, Type %s",
+ objData.dwRank, (DWORD_PTR)objData.dwNumComponents, ElementTypeName(objData.ElementType));
+
+ IfDMLOut(" (<exec cmd=\"!DumpArray /d %p\">Print Array</exec>)", SOS_PTR(taObj));
+ ExtOut("\n");
+
+ if (objData.ElementType == ELEMENT_TYPE_I1 ||
+ objData.ElementType == ELEMENT_TYPE_U1 ||
+ objData.ElementType == ELEMENT_TYPE_CHAR)
+ {
+ bool wide = objData.ElementType == ELEMENT_TYPE_CHAR;
+
+ // Get the size of the character array, but clamp it to a reasonable length.
+ TADDR pos = taObj + (2 * sizeof(DWORD_PTR));
+ DWORD_PTR num;
+ moveN(num, taObj + sizeof(DWORD_PTR));
+
+ if (IsDMLEnabled())
+ DMLOut("<exec cmd=\"%s %x L%x\">Content</exec>: ", (wide) ? "dw" : "db", pos, num);
+ else
+ ExtOut("Content: ");
+ CharArrayContent(pos, (ULONG)(num <= 128 ? num : 128), wide);
+ ExtOut("\n");
+ }
+ }
+ else
+ {
+ FileNameForModule(TO_TADDR(mtabledata.Module), g_mdName);
+ ExtOut("File: %S\n", g_mdName[0] ? g_mdName : W("Unknown Module"));
+ }
+
+ if (objData.ObjectType == OBJ_STRING)
+ {
+ ExtOut("String: ");
+ StringObjectContent(taObj);
+ ExtOut("\n");
+ }
+ else if (objData.ObjectType == OBJ_OBJECT)
+ {
+ ExtOut("Object\n");
+ }
+
+ if (bPrintFields)
+ {
+ DacpMethodTableFieldData vMethodTableFields;
+ if ((Status = vMethodTableFields.Request(g_sos,TO_CDADDR(objData.MethodTable)))!=S_OK)
+ return Status;
+
+ ExtOut("Fields:\n");
+ if (vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0)
+ {
+ DisplayFields(objData.MethodTable, &mtabledata, &vMethodTableFields, taObj, TRUE, FALSE);
+ }
+ else
+ {
+ ExtOut("None\n");
+ }
+ }
+
+ sos::ThinLockInfo lockInfo;
+ if (obj.GetThinLock(lockInfo))
+ {
+ ExtOut("ThinLock owner %x (%p), Recursive %x\n", lockInfo.ThreadId,
+ SOS_PTR(lockInfo.ThreadPtr), lockInfo.Recursion);
+ }
+
+ return S_OK;
+}
+
+BOOL IndicesInRange (DWORD * indices, DWORD * lowerBounds, DWORD * bounds, DWORD rank)
+{
+ int i = 0;
+ if (!ClrSafeInt<int>::subtraction((int)rank, 1, i))
+ {
+ ExtOut("<integer underflow>\n");
+ return FALSE;
+ }
+
+ for (; i >= 0; i--)
+ {
+ if (indices[i] >= bounds[i] + lowerBounds[i])
+ {
+ if (i == 0)
+ {
+ return FALSE;
+ }
+
+ indices[i] = lowerBounds[i];
+ indices[i - 1]++;
+ }
+ }
+
+ return TRUE;
+}
+
+void ExtOutIndices (DWORD * indices, DWORD rank)
+{
+ for (DWORD i = 0; i < rank; i++)
+ {
+ ExtOut("[%d]", indices[i]);
+ }
+}
+
+size_t OffsetFromIndices (DWORD * indices, DWORD * lowerBounds, DWORD * bounds, DWORD rank)
+{
+ _ASSERTE(rank >= 0);
+ size_t multiplier = 1;
+ size_t offset = 0;
+ int i = 0;
+ if (!ClrSafeInt<int>::subtraction((int)rank, 1, i))
+ {
+ ExtOut("<integer underflow>\n");
+ return 0;
+ }
+
+ for (; i >= 0; i--)
+ {
+ DWORD curIndex = indices[i] - lowerBounds[i];
+ offset += curIndex * multiplier;
+ multiplier *= bounds[i];
+ }
+
+ return offset;
+}
+HRESULT PrintArray(DacpObjectData& objData, DumpArrayFlags& flags, BOOL isPermSetPrint);
+#ifdef _DEBUG
+HRESULT PrintPermissionSet (TADDR p_PermSet)
+{
+ HRESULT Status = S_OK;
+
+ DacpObjectData PermSetData;
+ if ((Status=PermSetData.Request(g_sos, TO_CDADDR(p_PermSet))) != S_OK)
+ {
+ ExtOut("Invalid object\n");
+ return Status;
+ }
+
+
+ sos::MethodTable mt = TO_TADDR(PermSetData.MethodTable);
+ if (_wcscmp (W("System.Security.PermissionSet"), mt.GetName()) != 0 && _wcscmp(W("System.Security.NamedPermissionSet"), mt.GetName()) != 0)
+ {
+ ExtOut("Invalid PermissionSet object\n");
+ return S_FALSE;
+ }
+
+ ExtOut("PermissionSet object: %p\n", SOS_PTR(p_PermSet));
+
+ // Print basic info
+
+ // Walk the fields, printing some fields in a special way.
+
+ int iOffset = GetObjFieldOffset (p_PermSet, PermSetData.MethodTable, W("m_Unrestricted"));
+
+ if (iOffset > 0)
+ {
+ BYTE unrestricted;
+ MOVE(unrestricted, p_PermSet + iOffset);
+ if (unrestricted)
+ ExtOut("Unrestricted: TRUE\n");
+ else
+ ExtOut("Unrestricted: FALSE\n");
+ }
+
+ iOffset = GetObjFieldOffset (p_PermSet, PermSetData.MethodTable, W("m_permSet"));
+ if (iOffset > 0)
+ {
+ TADDR tbSetPtr;
+ MOVE(tbSetPtr, p_PermSet + iOffset);
+ if (tbSetPtr != NULL)
+ {
+ DacpObjectData tbSetData;
+ if ((Status=tbSetData.Request(g_sos, TO_CDADDR(tbSetPtr))) != S_OK)
+ {
+ ExtOut("Invalid object\n");
+ return Status;
+ }
+
+ iOffset = GetObjFieldOffset (tbSetPtr, tbSetData.MethodTable, W("m_Set"));
+ if (iOffset > 0)
+ {
+ DWORD_PTR PermsArrayPtr;
+ MOVE(PermsArrayPtr, tbSetPtr + iOffset);
+ if (PermsArrayPtr != NULL)
+ {
+ // Print all the permissions in the array
+ DacpObjectData objData;
+ if ((Status=objData.Request(g_sos, TO_CDADDR(PermsArrayPtr))) != S_OK)
+ {
+ ExtOut("Invalid object\n");
+ return Status;
+ }
+ DumpArrayFlags flags;
+ flags.bDetail = TRUE;
+ return PrintArray(objData, flags, TRUE);
+ }
+ }
+
+ iOffset = GetObjFieldOffset (tbSetPtr, tbSetData.MethodTable, W("m_Obj"));
+ if (iOffset > 0)
+ {
+ DWORD_PTR PermObjPtr;
+ MOVE(PermObjPtr, tbSetPtr + iOffset);
+ if (PermObjPtr != NULL)
+ {
+ // Print the permission object
+ return PrintObj(PermObjPtr);
+ }
+ }
+
+
+ }
+ }
+ return Status;
+}
+
+#endif // _DEBUG
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the contents of an object from a *
+* given address
+* *
+\**********************************************************************/
+DECLARE_API(DumpArray)
+{
+ INIT_API();
+
+ DumpArrayFlags flags;
+
+ MINIDUMP_NOT_SUPPORTED();
+
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-start", &flags.startIndex, COSIZE_T, TRUE},
+ {"-length", &flags.Length, COSIZE_T, TRUE},
+ {"-details", &flags.bDetail, COBOOL, FALSE},
+ {"-nofields", &flags.bNoFieldsForElement, COBOOL, FALSE},
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE},
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&flags.strObject, COSTRING}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ DWORD_PTR p_Object = GetExpression (flags.strObject);
+ if (p_Object == 0)
+ {
+ ExtOut("Invalid parameter %s\n", flags.strObject);
+ return Status;
+ }
+
+ if (!sos::IsObject(p_Object, true))
+ {
+ ExtOut("<Note: this object has an invalid CLASS field>\n");
+ }
+
+ DacpObjectData objData;
+ if ((Status=objData.Request(g_sos, TO_CDADDR(p_Object))) != S_OK)
+ {
+ ExtOut("Invalid object\n");
+ return Status;
+ }
+
+ if (objData.ObjectType != OBJ_ARRAY)
+ {
+ ExtOut("Not an array, please use !DumpObj instead\n");
+ return S_OK;
+ }
+ return PrintArray(objData, flags, FALSE);
+}
+
+
+HRESULT PrintArray(DacpObjectData& objData, DumpArrayFlags& flags, BOOL isPermSetPrint)
+{
+ HRESULT Status = S_OK;
+
+ if (objData.dwRank != 1 && (flags.Length != (DWORD_PTR)-1 ||flags.startIndex != 0))
+ {
+ ExtOut("For multi-dimension array, length and start index are supported\n");
+ return S_OK;
+ }
+
+ if (flags.startIndex > objData.dwNumComponents)
+ {
+ ExtOut("Start index out of range\n");
+ return S_OK;
+ }
+
+ if (!flags.bDetail && flags.bNoFieldsForElement)
+ {
+ ExtOut("-nofields has no effect unless -details is specified\n");
+ }
+
+ DWORD i;
+ if (!isPermSetPrint)
+ {
+ // TODO: don't depend on this being a MethodTable
+ NameForMT_s(TO_TADDR(objData.ElementTypeHandle), g_mdName, mdNameLen);
+
+ ExtOut("Name: %S[", g_mdName);
+ for (i = 1; i < objData.dwRank; i++)
+ ExtOut(",");
+ ExtOut("]\n");
+
+ DMLOut("MethodTable: %s\n", DMLMethodTable(objData.MethodTable));
+
+ {
+ DacpMethodTableData mtdata;
+ if (SUCCEEDED(mtdata.Request(g_sos, objData.MethodTable)))
+ {
+ DMLOut("EEClass: %s\n", DMLClass(mtdata.Class));
+ }
+ }
+
+ DWORD_PTR size = (DWORD_PTR)objData.Size;
+ ExtOut("Size: %" POINTERSIZE_TYPE "d(0x%" POINTERSIZE_TYPE "x) bytes\n", size, size);
+
+ ExtOut("Array: Rank %d, Number of elements %" POINTERSIZE_TYPE "d, Type %s\n",
+ objData.dwRank, (DWORD_PTR)objData.dwNumComponents, ElementTypeName(objData.ElementType));
+ DMLOut("Element Methodtable: %s\n", DMLMethodTable(objData.ElementTypeHandle));
+ }
+
+ BOOL isElementValueType = IsElementValueType(objData.ElementType);
+
+ DWORD dwRankAllocSize;
+ if (!ClrSafeInt<DWORD>::multiply(sizeof(DWORD), objData.dwRank, dwRankAllocSize))
+ {
+ ExtOut("Integer overflow on array rank\n");
+ return Status;
+ }
+
+ DWORD *lowerBounds = (DWORD *)alloca(dwRankAllocSize);
+ if (!SafeReadMemory(objData.ArrayLowerBoundsPtr, lowerBounds, dwRankAllocSize, NULL))
+ {
+ ExtOut("Failed to read lower bounds info from the array\n");
+ return S_OK;
+ }
+
+ DWORD *bounds = (DWORD *)alloca(dwRankAllocSize);
+ if (!SafeReadMemory (objData.ArrayBoundsPtr, bounds, dwRankAllocSize, NULL))
+ {
+ ExtOut("Failed to read bounds info from the array\n");
+ return S_OK;
+ }
+
+ //length is only supported for single-dimension array
+ if (objData.dwRank == 1 && flags.Length != (DWORD_PTR)-1)
+ {
+ bounds[0] = _min(bounds[0], (DWORD)(flags.Length + flags.startIndex) - lowerBounds[0]);
+ }
+
+ DWORD *indices = (DWORD *)alloca(dwRankAllocSize);
+ for (i = 0; i < objData.dwRank; i++)
+ {
+ indices[i] = lowerBounds[i];
+ }
+
+ //start index is only supported for single-dimension array
+ if (objData.dwRank == 1)
+ {
+ indices[0] = (DWORD)flags.startIndex;
+ }
+
+ //Offset should be calculated by OffsetFromIndices. However because of the way
+ //how we grow indices, incrementing offset by one happens to match indices in every iteration
+ for (size_t offset = OffsetFromIndices (indices, lowerBounds, bounds, objData.dwRank);
+ IndicesInRange (indices, lowerBounds, bounds, objData.dwRank);
+ indices[objData.dwRank - 1]++, offset++)
+ {
+ if (IsInterrupt())
+ {
+ ExtOut("interrupted by user\n");
+ break;
+ }
+
+ TADDR elementAddress = TO_TADDR(objData.ArrayDataPtr + offset * objData.dwComponentSize);
+ TADDR p_Element = NULL;
+ if (isElementValueType)
+ {
+ p_Element = elementAddress;
+ }
+ else if (!SafeReadMemory (elementAddress, &p_Element, sizeof (p_Element), NULL))
+ {
+ ExtOut("Failed to read element at ");
+ ExtOutIndices(indices, objData.dwRank);
+ ExtOut("\n");
+ continue;
+ }
+
+ if (p_Element)
+ {
+ ExtOutIndices(indices, objData.dwRank);
+
+ if (isElementValueType)
+ {
+ DMLOut( " %s\n", DMLValueClass(objData.MethodTable, p_Element));
+ }
+ else
+ {
+ DMLOut(" %s\n", DMLObject(p_Element));
+ }
+ }
+ else if (!isPermSetPrint)
+ {
+ ExtOutIndices(indices, objData.dwRank);
+ ExtOut(" null\n");
+ }
+
+ if (flags.bDetail)
+ {
+ IncrementIndent();
+ if (isElementValueType)
+ {
+ PrintVC(TO_TADDR(objData.ElementTypeHandle), elementAddress, !flags.bNoFieldsForElement);
+ }
+ else if (p_Element != NULL)
+ {
+ PrintObj(p_Element, !flags.bNoFieldsForElement);
+ }
+ DecrementIndent();
+ }
+ }
+
+ return S_OK;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the contents of an object from a *
+* given address
+* *
+\**********************************************************************/
+DECLARE_API(DumpObj)
+{
+ INIT_API();
+
+ MINIDUMP_NOT_SUPPORTED();
+
+ BOOL dml = FALSE;
+ BOOL bNoFields = FALSE;
+ BOOL bRefs = FALSE;
+ StringHolder str_Object;
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-nofields", &bNoFields, COBOOL, FALSE},
+ {"-refs", &bRefs, COBOOL, FALSE},
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE},
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&str_Object.data, COSTRING}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ DWORD_PTR p_Object = GetExpression(str_Object.data);
+ EnableDMLHolder dmlHolder(dml);
+ if (p_Object == 0)
+ {
+ ExtOut("Invalid parameter %s\n", args);
+ return Status;
+ }
+
+ Status = PrintObj(p_Object, !bNoFields);
+
+ if (SUCCEEDED(Status) && bRefs)
+ {
+ ExtOut("GC Refs:\n");
+ TableOutput out(2, POINTERSIZE_HEX, AlignRight, 4);
+ out.WriteRow("offset", "object");
+ for (sos::RefIterator itr(TO_TADDR(p_Object)); itr; ++itr)
+ out.WriteRow(Hex(itr.GetOffset()), ObjectPtr(*itr));
+ }
+
+ return Status;
+}
+
+CLRDATA_ADDRESS isExceptionObj(CLRDATA_ADDRESS mtObj)
+{
+ // We want to follow back until we get the mt for System.Exception
+ DacpMethodTableData dmtd;
+ CLRDATA_ADDRESS walkMT = mtObj;
+ while(walkMT != NULL)
+ {
+ if (dmtd.Request(g_sos, walkMT) != S_OK)
+ {
+ break;
+ }
+ if (walkMT == g_special_usefulGlobals.ExceptionMethodTable)
+ {
+ return walkMT;
+ }
+ walkMT = dmtd.ParentMethodTable;
+ }
+ return NULL;
+}
+
+CLRDATA_ADDRESS isSecurityExceptionObj(CLRDATA_ADDRESS mtObj)
+{
+ // We want to follow back until we get the mt for System.Exception
+ DacpMethodTableData dmtd;
+ CLRDATA_ADDRESS walkMT = mtObj;
+ while(walkMT != NULL)
+ {
+ if (dmtd.Request(g_sos, walkMT) != S_OK)
+ {
+ break;
+ }
+ NameForMT_s(TO_TADDR(walkMT), g_mdName, mdNameLen);
+ if (_wcscmp(W("System.Security.SecurityException"), g_mdName) == 0)
+ {
+ return walkMT;
+ }
+ walkMT = dmtd.ParentMethodTable;
+ }
+ return NULL;
+}
+
+// Fill the passed in buffer with a text header for generated exception information.
+// Returns the number of characters in the wszBuffer array on exit.
+// If NULL is passed for wszBuffer, just returns the number of characters needed.
+size_t AddExceptionHeader (__out_ecount_opt(bufferLength) WCHAR *wszBuffer, size_t bufferLength)
+{
+#ifdef _TARGET_WIN64_
+ const WCHAR *wszHeader = W(" SP IP Function\n");
+#else
+ const WCHAR *wszHeader = W(" SP IP Function\n");
+#endif // _TARGET_WIN64_
+ if (wszBuffer)
+ {
+ swprintf_s(wszBuffer, bufferLength, wszHeader);
+ }
+ return _wcslen(wszHeader);
+}
+
+struct StackTraceElement
+{
+ UINT_PTR ip;
+ UINT_PTR sp;
+ DWORD_PTR pFunc; // MethodDesc
+#if defined(FEATURE_EXCEPTIONDISPATCHINFO)
+ // TRUE if this element represents the last frame of the foreign
+ // exception stack trace.
+ BOOL fIsLastFrameFromForeignStackTrace;
+#endif // defined(FEATURE_EXCEPTIONDISPATCHINFO)
+
+};
+
+#include "sos_stacktrace.h"
+
+class StringOutput
+{
+public:
+ CQuickString cs;
+ StringOutput()
+ {
+ cs.Alloc(1024);
+ cs.String()[0] = L'\0';
+ }
+
+ BOOL Append(__in_z LPCWSTR pszStr)
+ {
+ size_t iInputLen = _wcslen (pszStr);
+ size_t iCurLen = _wcslen (cs.String());
+ if ((iCurLen + iInputLen + 1) > cs.Size())
+ {
+ if (cs.ReSize(iCurLen + iInputLen + 1) != S_OK)
+ {
+ return FALSE;
+ }
+ }
+
+ wcsncat_s (cs.String(), cs.Size(), pszStr, _TRUNCATE);
+ return TRUE;
+ }
+
+ size_t Length()
+ {
+ return _wcslen(cs.String());
+ }
+
+ WCHAR *String()
+ {
+ return cs.String();
+ }
+};
+
+static HRESULT DumpMDInfoBuffer(DWORD_PTR dwStartAddr, DWORD Flags, ULONG64 Esp, ULONG64 IPAddr, StringOutput& so);
+
+// Using heuristics to determine if an exception object represented an async (hardware) or a
+// managed exception
+// We need to use these heuristics when the System.Exception object is not the active exception
+// on some thread, but it's something found somewhere on the managed heap.
+
+// uses the MapWin32FaultToCOMPlusException to figure out how we map async exceptions
+// to managed exceptions and their HRESULTs
+static const HRESULT AsyncHResultValues[] =
+{
+ COR_E_ARITHMETIC, // kArithmeticException
+ COR_E_OVERFLOW, // kOverflowException
+ COR_E_DIVIDEBYZERO, // kDivideByZeroException
+ COR_E_FORMAT, // kFormatException
+ COR_E_NULLREFERENCE, // kNullReferenceException
+ E_POINTER, // kAccessViolationException
+ // the EE is raising the next exceptions more often than the OS will raise an async
+ // exception for these conditions, so in general treat these as Synchronous
+ // COR_E_INDEXOUTOFRANGE, // kIndexOutOfRangeException
+ // COR_E_OUTOFMEMORY, // kOutOfMemoryException
+ // COR_E_STACKOVERFLOW, // kStackOverflowException
+ COR_E_DATAMISALIGNED, // kDataMisalignedException
+
+};
+BOOL IsAsyncException(TADDR taObj, TADDR mtObj)
+{
+ // by default we'll treat exceptions as synchronous
+ UINT32 xcode = EXCEPTION_COMPLUS;
+ int iOffset = GetObjFieldOffset (taObj, mtObj, W("_xcode"));
+ if (iOffset > 0)
+ {
+ HRESULT hr = MOVE(xcode, taObj + iOffset);
+ if (hr != S_OK)
+ {
+ xcode = EXCEPTION_COMPLUS;
+ goto Done;
+ }
+ }
+
+ if (xcode == EXCEPTION_COMPLUS)
+ {
+ HRESULT ehr = 0;
+ iOffset = GetObjFieldOffset (taObj, mtObj, W("_HResult"));
+ if (iOffset > 0)
+ {
+ HRESULT hr = MOVE(ehr, taObj + iOffset);
+ if (hr != S_OK)
+ {
+ xcode = EXCEPTION_COMPLUS;
+ goto Done;
+ }
+ for (size_t idx = 0; idx < _countof(AsyncHResultValues); ++idx)
+ {
+ if (ehr == AsyncHResultValues[idx])
+ {
+ xcode = ehr;
+ break;
+ }
+ }
+ }
+ }
+Done:
+ return xcode != EXCEPTION_COMPLUS;
+}
+
+// Overload that mirrors the code above when the ExceptionObjectData was already retrieved from LS
+BOOL IsAsyncException(const DacpExceptionObjectData & excData)
+{
+ if (excData.XCode != EXCEPTION_COMPLUS)
+ return TRUE;
+
+ HRESULT ehr = excData.HResult;
+ for (size_t idx = 0; idx < _countof(AsyncHResultValues); ++idx)
+ {
+ if (ehr == AsyncHResultValues[idx])
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+#define SOS_STACKTRACE_SHOWEXPLICITFRAMES 0x00000002
+size_t FormatGeneratedException (DWORD_PTR dataPtr,
+ UINT bytes,
+ __out_ecount_opt(bufferLength) WCHAR *wszBuffer,
+ size_t bufferLength,
+ BOOL bAsync,
+ BOOL bNestedCase = FALSE,
+ BOOL bLineNumbers = FALSE)
+{
+ UINT count = bytes / sizeof(StackTraceElement);
+ size_t Length = 0;
+
+ if (wszBuffer && bufferLength > 0)
+ {
+ wszBuffer[0] = L'\0';
+ }
+
+ // Buffer is calculated for sprintf below (" %p %p %S\n");
+ WCHAR wszLineBuffer[mdNameLen + 8 + sizeof(size_t)*2 + MAX_LONGPATH + 8];
+
+ if (count == 0)
+ {
+ return 0;
+ }
+
+ if (bNestedCase)
+ {
+ // If we are computing the call stack for a nested exception, we
+ // don't want to print the last frame, because the outer exception
+ // will have that frame.
+ count--;
+ }
+
+ for (UINT i = 0; i < count; i++)
+ {
+ StackTraceElement ste;
+ MOVE (ste, dataPtr + i*sizeof(StackTraceElement));
+
+ // ste.ip must be adjusted because of an ancient workaround in the exception
+ // infrastructure. The workaround is that the exception needs to have
+ // an ip address that will map to the line number where the exception was thrown.
+ // (It doesn't matter that it's not a valid instruction). (see /vm/excep.cpp)
+ //
+ // This "counterhack" is not 100% accurate
+ // The biggest issue is that !PrintException must work with exception objects
+ // that may not be currently active; as a consequence we cannot rely on the
+ // state of some "current thread" to infer whether the IP values stored in
+ // the exception object have been adjusted or not. If we could, we may examine
+ // the topmost "Frame" and make the decision based on whether it's a
+ // FaultingExceptionFrame or not.
+ // 1. On IA64 the IP values are never adjusted by the EE so there's nothing
+ // to adjust back.
+ // 2. On AMD64:
+ // (a) if the exception was an async (hardware) exception add 1 to all
+ // IP values in the exception object
+ // (b) if the exception was a managed exception (either raised by the
+ // EE or thrown by managed code) do not adjust any IP values
+ // 3. On X86:
+ // (a) if the exception was an async (hardware) exception add 1 to
+ // all but the topmost IP value in the exception object
+ // (b) if the exception was a managed exception (either raised by
+ // the EE or thrown by managed code) add 1 to all IP values in
+ // the exception object
+#if defined(_TARGET_AMD64_)
+ if (bAsync)
+ {
+ ste.ip += 1;
+ }
+#elif defined(_TARGET_X86_)
+ if (IsDbgTargetX86() && (!bAsync || i != 0))
+ {
+ ste.ip += 1;
+ }
+#endif // defined(_TARGET_AMD64_) || defined(_TARGET__X86_)
+
+ StringOutput so;
+ HRESULT Status = DumpMDInfoBuffer(ste.pFunc, SOS_STACKTRACE_SHOWADDRESSES|SOS_STACKTRACE_SHOWEXPLICITFRAMES, ste.sp, ste.ip, so);
+
+ // If DumpMDInfoBuffer failed (due to out of memory or missing metadata),
+ // or did not update so (when ste is an explicit frames), do not update wszBuffer
+ if (Status == S_OK)
+ {
+ WCHAR filename[MAX_LONGPATH] = W("");
+ ULONG linenum = 0;
+ if (bLineNumbers &&
+ SUCCEEDED(GetLineByOffset(TO_CDADDR(ste.ip), &linenum, filename, _countof(filename))))
+ {
+ swprintf_s(wszLineBuffer, _countof(wszLineBuffer), W(" %s [%s @ %d]\n"), so.String(), filename, linenum);
+ }
+ else
+ {
+ swprintf_s(wszLineBuffer, _countof(wszLineBuffer), W(" %s\n"), so.String());
+ }
+
+ Length += _wcslen(wszLineBuffer);
+
+ if (wszBuffer)
+ {
+ wcsncat_s(wszBuffer, bufferLength, wszLineBuffer, _TRUNCATE);
+ }
+ }
+ }
+
+ return Length;
+}
+
+// ExtOut has an internal limit for the string size
+void SosExtOutLargeString(__inout_z __inout_ecount_opt(len) WCHAR * pwszLargeString, size_t len)
+{
+ const size_t chunkLen = 2048;
+
+ WCHAR *pwsz = pwszLargeString; // beginning of a chunk
+ size_t count = len/chunkLen;
+ // write full chunks
+ for (size_t idx = 0; idx < count; ++idx)
+ {
+ WCHAR *pch = pwsz + chunkLen; // after the chunk
+ // zero terminate the chunk
+ WCHAR ch = *pch;
+ *pch = L'\0';
+
+ ExtOut("%S", pwsz);
+
+ // restore whacked char
+ *pch = ch;
+
+ // advance to next chunk
+ pwsz += chunkLen;
+ }
+
+ // last chunk
+ ExtOut("%S", pwsz);
+}
+
+HRESULT FormatException(TADDR taObj, BOOL bLineNumbers = FALSE)
+{
+ HRESULT Status = S_OK;
+
+ DacpObjectData objData;
+ if ((Status=objData.Request(g_sos, TO_CDADDR(taObj))) != S_OK)
+ {
+ ExtOut("Invalid object\n");
+ return Status;
+ }
+
+ // Make sure it is an exception object, and get the MT of Exception
+ CLRDATA_ADDRESS exceptionMT = isExceptionObj(objData.MethodTable);
+ if (exceptionMT == NULL)
+ {
+ ExtOut("Not a valid exception object\n");
+ return Status;
+ }
+
+ DMLOut("Exception object: %s\n", DMLObject(taObj));
+
+ if (NameForMT_s(TO_TADDR(objData.MethodTable), g_mdName, mdNameLen))
+ {
+ ExtOut("Exception type: %S\n", g_mdName);
+ }
+ else
+ {
+ ExtOut("Exception type: <Unknown>\n");
+ }
+
+ // Print basic info
+
+ // First try to get exception object data using ISOSDacInterface2
+ DacpExceptionObjectData excData;
+ BOOL bGotExcData = SUCCEEDED(excData.Request(g_sos, TO_CDADDR(taObj)));
+
+ // Walk the fields, printing some fields in a special way.
+ // HR, InnerException, Message, StackTrace, StackTraceString
+
+ {
+ TADDR taMsg = 0;
+ if (bGotExcData)
+ {
+ taMsg = TO_TADDR(excData.Message);
+ }
+ else
+ {
+ int iOffset = GetObjFieldOffset(taObj, objData.MethodTable, W("_message"));
+ if (iOffset > 0)
+ {
+ MOVE (taMsg, taObj + iOffset);
+ }
+ }
+
+ ExtOut("Message: ");
+
+ if (taMsg)
+ StringObjectContent(taMsg);
+ else
+ ExtOut("<none>");
+
+ ExtOut("\n");
+ }
+
+ {
+ TADDR taInnerExc = 0;
+ if (bGotExcData)
+ {
+ taInnerExc = TO_TADDR(excData.InnerException);
+ }
+ else
+ {
+ int iOffset = GetObjFieldOffset(taObj, objData.MethodTable, W("_innerException"));
+ if (iOffset > 0)
+ {
+ MOVE (taInnerExc, taObj + iOffset);
+ }
+ }
+
+ ExtOut("InnerException: ");
+ if (taInnerExc)
+ {
+ TADDR taMT;
+ if (SUCCEEDED(GetMTOfObject(taInnerExc, &taMT)))
+ {
+ NameForMT_s(taMT, g_mdName, mdNameLen);
+ ExtOut("%S, ", g_mdName);
+ if (IsDMLEnabled())
+ DMLOut("Use <exec cmd=\"!PrintException /d %p\">!PrintException %p</exec> to see more.\n", taInnerExc, taInnerExc);
+ else
+ ExtOut("Use !PrintException %p to see more.\n", SOS_PTR(taInnerExc));
+ }
+ else
+ {
+ ExtOut("<invalid MethodTable of inner exception>");
+ }
+ }
+ else
+ {
+ ExtOut("<none>\n");
+ }
+ }
+
+ BOOL bAsync = bGotExcData ? IsAsyncException(excData)
+ : IsAsyncException(taObj, TO_TADDR(objData.MethodTable));
+
+ {
+ TADDR taStackTrace = 0;
+ if (bGotExcData)
+ {
+ taStackTrace = TO_TADDR(excData.StackTrace);
+ }
+ else
+ {
+ int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, W("_stackTrace"));
+ if (iOffset > 0)
+ {
+ MOVE(taStackTrace, taObj + iOffset);
+ }
+ }
+
+ ExtOut("StackTrace (generated):\n");
+ if (taStackTrace)
+ {
+ DWORD arrayLen;
+ HRESULT hr = MOVE(arrayLen, taStackTrace + sizeof(DWORD_PTR));
+
+ if (arrayLen != 0 && hr == S_OK)
+ {
+#ifdef _TARGET_WIN64_
+ DWORD_PTR dataPtr = taStackTrace + sizeof(DWORD_PTR) + sizeof(DWORD) + sizeof(DWORD);
+#else
+ DWORD_PTR dataPtr = taStackTrace + sizeof(DWORD_PTR) + sizeof(DWORD);
+#endif // _TARGET_WIN64_
+ size_t stackTraceSize = 0;
+ MOVE (stackTraceSize, dataPtr);
+
+ DWORD cbStackSize = static_cast<DWORD>(stackTraceSize * sizeof(StackTraceElement));
+ dataPtr += sizeof(size_t) + sizeof(size_t); // skip the array header, then goes the data
+
+ if (stackTraceSize == 0)
+ {
+ ExtOut("Unable to decipher generated stack trace\n");
+ }
+ else
+ {
+ size_t iHeaderLength = AddExceptionHeader (NULL, 0);
+ size_t iLength = FormatGeneratedException (dataPtr, cbStackSize, NULL, 0, bAsync, FALSE, bLineNumbers);
+ WCHAR *pwszBuffer = new NOTHROW WCHAR[iHeaderLength + iLength + 1];
+ if (pwszBuffer)
+ {
+ AddExceptionHeader(pwszBuffer, iHeaderLength + 1);
+ FormatGeneratedException(dataPtr, cbStackSize, pwszBuffer + iHeaderLength, iLength + 1, bAsync, FALSE, bLineNumbers);
+ SosExtOutLargeString(pwszBuffer, iHeaderLength + iLength + 1);
+ delete[] pwszBuffer;
+ }
+ ExtOut("\n");
+ }
+ }
+ else
+ {
+ ExtOut("<Not Available>\n");
+ }
+ }
+ else
+ {
+ ExtOut("<none>\n");
+ }
+ }
+
+ {
+ TADDR taStackString;
+ if (bGotExcData)
+ {
+ taStackString = TO_TADDR(excData.StackTraceString);
+ }
+ else
+ {
+ int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, W("_stackTraceString"));
+ MOVE (taStackString, taObj + iOffset);
+ }
+
+ ExtOut("StackTraceString: ");
+ if (taStackString)
+ {
+ StringObjectContent(taStackString);
+ ExtOut("\n\n"); // extra newline looks better
+ }
+ else
+ {
+ ExtOut("<none>\n");
+ }
+ }
+
+ {
+ DWORD hResult;
+ if (bGotExcData)
+ {
+ hResult = excData.HResult;
+ }
+ else
+ {
+ int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, W("_HResult"));
+ MOVE (hResult, taObj + iOffset);
+ }
+
+ ExtOut("HResult: %lx\n", hResult);
+ }
+
+ if (isSecurityExceptionObj(objData.MethodTable) != NULL)
+ {
+ // We have a SecurityException Object: print out the debugString if present
+ int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, W("m_debugString"));
+ if (iOffset > 0)
+ {
+ TADDR taDebugString;
+ MOVE (taDebugString, taObj + iOffset);
+
+ if (taDebugString)
+ {
+ ExtOut("SecurityException Message: ");
+ StringObjectContent(taDebugString);
+ ExtOut("\n\n"); // extra newline looks better
+ }
+ }
+ }
+
+ return Status;
+}
+
+DECLARE_API(PrintException)
+{
+ INIT_API();
+
+ BOOL dml = FALSE;
+ BOOL bShowNested = FALSE;
+ BOOL bLineNumbers = FALSE;
+ BOOL bCCW = FALSE;
+ StringHolder strObject;
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-nested", &bShowNested, COBOOL, FALSE},
+ {"-lines", &bLineNumbers, COBOOL, FALSE},
+ {"-l", &bLineNumbers, COBOOL, FALSE},
+ {"-ccw", &bCCW, COBOOL, FALSE},
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE}
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&strObject, COSTRING}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+ if (bLineNumbers)
+ {
+ ULONG symlines = 0;
+ if (SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
+ {
+ symlines &= SYMOPT_LOAD_LINES;
+ }
+ if (symlines == 0)
+ {
+ ExtOut("In order for the option -lines to enable display of source information\n"
+ "the debugger must be configured to load the line number information from\n"
+ "the symbol files. Use the \".lines; .reload\" command to achieve this.\n");
+ // don't even try
+ bLineNumbers = FALSE;
+ }
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ DWORD_PTR p_Object = NULL;
+ if (nArg == 0)
+ {
+ if (bCCW)
+ {
+ ExtOut("No CCW pointer specified\n");
+ return Status;
+ }
+
+ // Look at the last exception object on this thread
+
+ CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
+ DacpThreadData Thread;
+
+ if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
+ {
+ ExtOut("The current thread is unmanaged\n");
+ return Status;
+ }
+
+ DWORD_PTR dwAddr = NULL;
+ if ((!SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle),
+ &dwAddr,
+ sizeof(dwAddr), NULL)) || (dwAddr==NULL))
+ {
+ ExtOut("There is no current managed exception on this thread\n");
+ }
+ else
+ {
+ p_Object = dwAddr;
+ }
+ }
+ else
+ {
+ p_Object = GetExpression(strObject.data);
+ if (p_Object == 0)
+ {
+ if (bCCW)
+ {
+ ExtOut("Invalid CCW pointer %s\n", args);
+ }
+ else
+ {
+ ExtOut("Invalid exception object %s\n", args);
+ }
+ return Status;
+ }
+
+ if (bCCW)
+ {
+ // check if the address is a CCW pointer and then
+ // get the exception object from it
+ DacpCCWData ccwData;
+ if (ccwData.Request(g_sos, p_Object) == S_OK)
+ {
+ p_Object = TO_TADDR(ccwData.managedObject);
+ }
+ }
+ }
+
+ if (p_Object)
+ {
+ FormatException(p_Object, bLineNumbers);
+ }
+
+ // Are there nested exceptions?
+ CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
+ DacpThreadData Thread;
+
+ if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
+ {
+ ExtOut("The current thread is unmanaged\n");
+ return Status;
+ }
+
+ if (Thread.firstNestedException)
+ {
+ if (!bShowNested)
+ {
+ ExtOut("There are nested exceptions on this thread. Run with -nested for details\n");
+ return Status;
+ }
+
+ CLRDATA_ADDRESS currentNested = Thread.firstNestedException;
+ do
+ {
+ CLRDATA_ADDRESS obj = 0, next = 0;
+ Status = g_sos->GetNestedExceptionData(currentNested, &obj, &next);
+
+ if (Status != S_OK)
+ {
+ ExtOut("Error retrieving nested exception info %p\n", SOS_PTR(currentNested));
+ return Status;
+ }
+
+ if (IsInterrupt())
+ {
+ ExtOut("<aborted>\n");
+ return Status;
+ }
+
+ ExtOut("\nNested exception -------------------------------------------------------------\n");
+ Status = FormatException((DWORD_PTR) obj, bLineNumbers);
+ if (Status != S_OK)
+ {
+ return Status;
+ }
+
+ currentNested = next;
+ }
+ while(currentNested != NULL);
+ }
+ return Status;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the contents of an object from a *
+* given address
+* *
+\**********************************************************************/
+DECLARE_API(DumpVC)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ DWORD_PTR p_MT = NULL;
+ DWORD_PTR p_Object = NULL;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE}
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&p_MT, COHEX},
+ {&p_Object, COHEX},
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ if (nArg!=2)
+ {
+ ExtOut("Usage: !DumpVC <Method Table> <Value object start addr>\n");
+ return Status;
+ }
+
+ if (!IsMethodTable(p_MT))
+ {
+ ExtOut("Not a managed object\n");
+ return S_OK;
+ }
+
+ return PrintVC(p_MT, p_Object);
+}
+
+#ifndef FEATURE_PAL
+
+#ifdef FEATURE_COMINTEROP
+
+DECLARE_API(DumpRCW)
+{
+ INIT_API();
+
+ BOOL dml = FALSE;
+ StringHolder strObject;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"/d", &dml, COBOOL, FALSE}
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&strObject, COSTRING}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ if (nArg == 0)
+ {
+ ExtOut("Missing RCW address\n");
+ return Status;
+ }
+ else
+ {
+ DWORD_PTR p_RCW = GetExpression(strObject.data);
+ if (p_RCW == 0)
+ {
+ ExtOut("Invalid RCW %s\n", args);
+ }
+ else
+ {
+ DacpRCWData rcwData;
+ if ((Status = rcwData.Request(g_sos, p_RCW)) != S_OK)
+ {
+ ExtOut("Error requesting RCW data\n");
+ return Status;
+ }
+ BOOL isDCOMProxy;
+ if (FAILED(rcwData.IsDCOMProxy(g_sos, p_RCW, &isDCOMProxy)))
+ {
+ isDCOMProxy = FALSE;
+ }
+
+ DMLOut("Managed object: %s\n", DMLObject(rcwData.managedObject));
+ DMLOut("Creating thread: %p\n", SOS_PTR(rcwData.creatorThread));
+ ExtOut("IUnknown pointer: %p\n", SOS_PTR(rcwData.unknownPointer));
+ ExtOut("COM Context: %p\n", SOS_PTR(rcwData.ctxCookie));
+ ExtOut("Managed ref count: %d\n", rcwData.refCount);
+ ExtOut("IUnknown V-table pointer : %p (captured at RCW creation time)\n", SOS_PTR(rcwData.vtablePtr));
+
+ ExtOut("Flags: %s%s%s%s%s%s%s%s\n",
+ (rcwData.isDisconnected? "IsDisconnected " : ""),
+ (rcwData.supportsIInspectable? "SupportsIInspectable " : ""),
+ (rcwData.isAggregated? "IsAggregated " : ""),
+ (rcwData.isContained? "IsContained " : ""),
+ (rcwData.isJupiterObject? "IsJupiterObject " : ""),
+ (rcwData.isFreeThreaded? "IsFreeThreaded " : ""),
+ (rcwData.identityPointer == TO_CDADDR(p_RCW)? "IsUnique " : ""),
+ (isDCOMProxy ? "IsDCOMProxy " : "")
+ );
+
+ // Jupiter data hidden by default
+ if (rcwData.isJupiterObject)
+ {
+ ExtOut("IJupiterObject: %p\n", SOS_PTR(rcwData.jupiterObject));
+ }
+
+ ExtOut("COM interface pointers:\n");
+
+ ArrayHolder<DacpCOMInterfacePointerData> pArray = new NOTHROW DacpCOMInterfacePointerData[rcwData.interfaceCount];
+ if (pArray == NULL)
+ {
+ ReportOOM();
+ return Status;
+ }
+
+ if ((Status = g_sos->GetRCWInterfaces(p_RCW, rcwData.interfaceCount, pArray, NULL)) != S_OK)
+ {
+ ExtOut("Error requesting COM interface pointers\n");
+ return Status;
+ }
+
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s Type\n", "IP", "Context", "MT");
+ for (int i = 0; i < rcwData.interfaceCount; i++)
+ {
+ // Ignore any NULL MethodTable interface cache. At this point only IJupiterObject
+ // is saved as NULL MethodTable at first slot, and we've already printed outs its
+ // value earlier.
+ if (pArray[i].methodTable == NULL)
+ continue;
+
+ NameForMT_s(TO_TADDR(pArray[i].methodTable), g_mdName, mdNameLen);
+
+ DMLOut("%p %p %s %S\n", SOS_PTR(pArray[i].interfacePtr), SOS_PTR(pArray[i].comContext), DMLMethodTable(pArray[i].methodTable), g_mdName);
+ }
+ }
+ }
+
+ return Status;
+}
+
+DECLARE_API(DumpCCW)
+{
+ INIT_API();
+
+ BOOL dml = FALSE;
+ StringHolder strObject;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"/d", &dml, COBOOL, FALSE}
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&strObject, COSTRING}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ if (nArg == 0)
+ {
+ ExtOut("Missing CCW address\n");
+ return Status;
+ }
+ else
+ {
+ DWORD_PTR p_CCW = GetExpression(strObject.data);
+ if (p_CCW == 0)
+ {
+ ExtOut("Invalid CCW %s\n", args);
+ }
+ else
+ {
+ DacpCCWData ccwData;
+ if ((Status = ccwData.Request(g_sos, p_CCW)) != S_OK)
+ {
+ ExtOut("Error requesting CCW data\n");
+ return Status;
+ }
+
+ if (ccwData.ccwAddress != p_CCW)
+ ExtOut("CCW: %p\n", SOS_PTR(ccwData.ccwAddress));
+
+ DMLOut("Managed object: %s\n", DMLObject(ccwData.managedObject));
+ ExtOut("Outer IUnknown: %p\n", SOS_PTR(ccwData.outerIUnknown));
+ ExtOut("Ref count: %d%s\n", ccwData.refCount, ccwData.isNeutered ? " (NEUTERED)" : "");
+ ExtOut("Flags: %s%s\n",
+ (ccwData.isExtendsCOMObject? "IsExtendsCOMObject " : ""),
+ (ccwData.isAggregated? "IsAggregated " : "")
+ );
+
+ // Jupiter information hidden by default
+ if (ccwData.jupiterRefCount > 0)
+ {
+ ExtOut("Jupiter ref count: %d%s%s%s%s\n",
+ ccwData.jupiterRefCount,
+ (ccwData.isPegged || ccwData.isGlobalPegged) ? ", Pegged by" : "",
+ ccwData.isPegged ? " Jupiter " : "",
+ (ccwData.isPegged && ccwData.isGlobalPegged) ? "&" : "",
+ ccwData.isGlobalPegged ? " CLR " : ""
+ );
+ }
+
+ ExtOut("RefCounted Handle: %p%s\n",
+ SOS_PTR(ccwData.handle),
+ (ccwData.hasStrongRef ? " (STRONG)" : " (WEAK)"));
+
+ ExtOut("COM interface pointers:\n");
+
+ ArrayHolder<DacpCOMInterfacePointerData> pArray = new NOTHROW DacpCOMInterfacePointerData[ccwData.interfaceCount];
+ if (pArray == NULL)
+ {
+ ReportOOM();
+ return Status;
+ }
+
+ if ((Status = g_sos->GetCCWInterfaces(p_CCW, ccwData.interfaceCount, pArray, NULL)) != S_OK)
+ {
+ ExtOut("Error requesting COM interface pointers\n");
+ return Status;
+ }
+
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s Type\n", "IP", "MT", "Type");
+ for (int i = 0; i < ccwData.interfaceCount; i++)
+ {
+ if (pArray[i].methodTable == NULL)
+ {
+ wcscpy_s(g_mdName, mdNameLen, W("IDispatch/IUnknown"));
+ }
+ else
+ {
+ NameForMT_s(TO_TADDR(pArray[i].methodTable), g_mdName, mdNameLen);
+ }
+
+ DMLOut("%p %s %S\n", pArray[i].interfacePtr, DMLMethodTable(pArray[i].methodTable), g_mdName);
+ }
+ }
+ }
+
+ return Status;
+}
+
+#endif // FEATURE_COMINTEROP
+
+#ifdef _DEBUG
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the contents of a PermissionSet *
+* from a given address. *
+* *
+\**********************************************************************/
+/*
+ COMMAND: dumppermissionset.
+ !DumpPermissionSet <PermissionSet object address>
+
+ This command allows you to examine a PermissionSet object. Note that you can
+ also use DumpObj such an object in greater detail. DumpPermissionSet attempts
+ to extract all the relevant information from a PermissionSet that you might be
+ interested in when performing Code Access Security (CAS) related debugging.
+
+ Here is a simple PermissionSet object:
+
+ 0:000> !DumpPermissionSet 014615f4
+ PermissionSet object: 014615f4
+ Unrestricted: TRUE
+
+ Note that this is an unrestricted PermissionSet object that does not contain
+ any individual permissions.
+
+ Here is another example of a PermissionSet object, one that is not unrestricted
+ and contains a single permission:
+
+ 0:003> !DumpPermissionSet 01469fa8
+ PermissionSet object: 01469fa8
+ Unrestricted: FALSE
+ Name: System.Security.Permissions.ReflectionPermission
+ MethodTable: 5b731308
+ EEClass: 5b7e0d78
+ Size: 12(0xc) bytes
+ (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_32\mscorlib\2.0.
+ 0.0__b77a5c561934e089\mscorlib.dll)
+
+ Fields:
+ MT Field Offset Type VT Attr Value Name
+ 5b73125c 4001d66 4 System.Int32 0 instance 2 m_flags
+
+ Here is another example of an unrestricted PermissionSet, one that contains
+ several permissions. The numbers in parentheses before each Permission object
+ represents the index of that Permission in the PermissionSet.
+
+ 0:003> !DumpPermissionSet 01467bd8
+ PermissionSet object: 01467bd8
+ Unrestricted: FALSE
+ [1] 01467e90
+ Name: System.Security.Permissions.FileDialogPermission
+ MethodTable: 5b73023c
+ EEClass: 5b7dfb18
+ Size: 12(0xc) bytes
+ (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
+ Fields:
+ MT Field Offset Type VT Attr Value Name
+ 5b730190 4001cc2 4 System.Int32 0 instance 1 access
+ [4] 014682a8
+ Name: System.Security.Permissions.ReflectionPermission
+ MethodTable: 5b731308
+ EEClass: 5b7e0d78
+ Size: 12(0xc) bytes
+ (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
+ Fields:
+ MT Field Offset Type VT Attr Value Name
+ 5b73125c 4001d66 4 System.Int32 0 instance 0 m_flags
+ [17] 0146c060
+ Name: System.Diagnostics.EventLogPermission
+ MethodTable: 569841c4
+ EEClass: 56a03e5c
+ Size: 28(0x1c) bytes
+ (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
+ Fields:
+ MT Field Offset Type VT Attr Value Name
+ 5b6d65d4 4003078 4 System.Object[] 0 instance 0146c190 tagNames
+ 5b6c9ed8 4003079 8 System.Type 0 instance 0146c17c permissionAccessType
+ 5b6cd928 400307a 10 System.Boolean 0 instance 0 isUnrestricted
+ 5b6c45f8 400307b c ...ections.Hashtable 0 instance 0146c1a4 rootTable
+ 5b6c090c 4003077 bfc System.String 0 static 00000000 computerName
+ 56984434 40030e7 14 ...onEntryCollection 0 instance 00000000 innerCollection
+ [18] 0146ceb4
+ Name: System.Net.WebPermission
+ MethodTable: 5696dfc4
+ EEClass: 569e256c
+ Size: 20(0x14) bytes
+ (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
+ Fields:
+ MT Field Offset Type VT Attr Value Name
+ 5b6cd928 400238e c System.Boolean 0 instance 0 m_Unrestricted
+ 5b6cd928 400238f d System.Boolean 0 instance 0 m_UnrestrictedConnect
+ 5b6cd928 4002390 e System.Boolean 0 instance 0 m_UnrestrictedAccept
+ 5b6c639c 4002391 4 ...ections.ArrayList 0 instance 0146cf3c m_connectList
+ 5b6c639c 4002392 8 ...ections.ArrayList 0 instance 0146cf54 m_acceptList
+ 569476f8 4002393 8a4 ...Expressions.Regex 0 static 00000000 s_MatchAllRegex
+ [19] 0146a5fc
+ Name: System.Net.DnsPermission
+ MethodTable: 56966408
+ EEClass: 569d3c08
+ Size: 12(0xc) bytes
+ (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
+ Fields:
+ MT Field Offset Type VT Attr Value Name
+ 5b6cd928 4001d2c 4 System.Boolean 0 instance 1 m_noRestriction
+ [20] 0146d8ec
+ Name: System.Web.AspNetHostingPermission
+ MethodTable: 569831bc
+ EEClass: 56a02ccc
+ Size: 12(0xc) bytes
+ (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
+ Fields:
+ MT Field Offset Type VT Attr Value Name
+ 56983090 4003074 4 System.Int32 0 instance 600 _level
+ [21] 0146e394
+ Name: System.Net.NetworkInformation.NetworkInformationPermission
+ MethodTable: 5697ac70
+ EEClass: 569f7104
+ Size: 16(0x10) bytes
+ (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
+ Fields:
+ MT Field Offset Type VT Attr Value Name
+ 5697ab38 4002c34 4 System.Int32 0 instance 0 access
+ 5b6cd928 4002c35 8 System.Boolean 0 instance 0 unrestricted
+
+
+ The abbreviation !dps can be used for brevity.
+
+ \\
+*/
+DECLARE_API(DumpPermissionSet)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ DWORD_PTR p_Object = NULL;
+
+ CMDValue arg[] =
+ {
+ {&p_Object, COHEX}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+ if (nArg!=1)
+ {
+ ExtOut("Usage: !DumpPermissionSet <PermissionSet object addr>\n");
+ return Status;
+ }
+
+
+ return PrintPermissionSet(p_Object);
+}
+
+#endif // _DEBUG
+
+void GCPrintGenerationInfo(DacpGcHeapDetails &heap);
+void GCPrintSegmentInfo(DacpGcHeapDetails &heap, DWORD_PTR &total_size);
+
+#endif // FEATURE_PAL
+
+void DisplayInvalidStructuresMessage()
+{
+ ExtOut("The garbage collector data structures are not in a valid state for traversal.\n");
+ ExtOut("It is either in the \"plan phase,\" where objects are being moved around, or\n");
+ ExtOut("we are at the initialization or shutdown of the gc heap. Commands related to \n");
+ ExtOut("displaying, finding or traversing objects as well as gc heap segments may not \n");
+ ExtOut("work properly. !dumpheap and !verifyheap may incorrectly complain of heap \n");
+ ExtOut("consistency errors.\n");
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function dumps GC heap size. *
+* *
+\**********************************************************************/
+DECLARE_API(EEHeap)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ BOOL dml = FALSE;
+ BOOL showgc = FALSE;
+ BOOL showloader = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-gc", &showgc, COBOOL, FALSE},
+ {"-loader", &showloader, COBOOL, FALSE},
+ {"/d", &dml, COBOOL, FALSE},
+ };
+
+ if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ if (showloader || !showgc)
+ {
+ // Loader heap.
+ DWORD_PTR allHeapSize = 0;
+ DWORD_PTR wasted = 0;
+ DacpAppDomainStoreData adsData;
+ if ((Status=adsData.Request(g_sos))!=S_OK)
+ {
+ ExtOut("Unable to get AppDomain information\n");
+ return Status;
+ }
+
+ // The first one is the system domain.
+ ExtOut("Loader Heap:\n");
+ IfFailRet(PrintDomainHeapInfo("System Domain", adsData.systemDomain, &allHeapSize, &wasted));
+ IfFailRet(PrintDomainHeapInfo("Shared Domain", adsData.sharedDomain, &allHeapSize, &wasted));
+
+ ArrayHolder<CLRDATA_ADDRESS> pArray = new NOTHROW CLRDATA_ADDRESS[adsData.DomainCount];
+
+ if (pArray==NULL)
+ {
+ ReportOOM();
+ return Status;
+ }
+
+ if ((Status=g_sos->GetAppDomainList(adsData.DomainCount, pArray, NULL))!=S_OK)
+ {
+ ExtOut("Unable to get the array of all AppDomains.\n");
+ return Status;
+ }
+
+ for (int n=0;n<adsData.DomainCount;n++)
+ {
+ if (IsInterrupt())
+ break;
+
+ char domain[16];
+ sprintf_s(domain, _countof(domain), "Domain %d", n+1);
+
+ IfFailRet(PrintDomainHeapInfo(domain, pArray[n], &allHeapSize, &wasted));
+
+ }
+
+ // Jit code heap
+ ExtOut("--------------------------------------\n");
+ ExtOut("Jit code heap:\n");
+
+ if (IsMiniDumpFile())
+ {
+ ExtOut("<no information>\n");
+ }
+ else
+ {
+ allHeapSize += JitHeapInfo();
+ }
+
+
+ // Module Data
+ {
+ int numModule;
+ ArrayHolder<DWORD_PTR> moduleList = ModuleFromName(NULL, &numModule);
+ if (moduleList == NULL)
+ {
+ ExtOut("Failed to request module list.\n");
+ }
+ else
+ {
+ // Module Thunk Heaps
+ ExtOut("--------------------------------------\n");
+ ExtOut("Module Thunk heaps:\n");
+ allHeapSize += PrintModuleHeapInfo(moduleList, numModule, ModuleHeapType_ThunkHeap, &wasted);
+
+ // Module Lookup Table Heaps
+ ExtOut("--------------------------------------\n");
+ ExtOut("Module Lookup Table heaps:\n");
+ allHeapSize += PrintModuleHeapInfo(moduleList, numModule, ModuleHeapType_LookupTableHeap, &wasted);
+ }
+ }
+
+ ExtOut("--------------------------------------\n");
+ ExtOut("Total LoaderHeap size: ");
+ PrintHeapSize(allHeapSize, wasted);
+ ExtOut("=======================================\n");
+ }
+
+ if (showgc || !showloader)
+ {
+ // GC Heap
+ DWORD dwNHeaps = 1;
+
+ if (!GetGcStructuresValid())
+ {
+ DisplayInvalidStructuresMessage();
+ }
+
+ DacpGcHeapData gcheap;
+ if (gcheap.Request(g_sos) != S_OK)
+ {
+ ExtOut("Error requesting GC Heap data\n");
+ return Status;
+ }
+
+ if (gcheap.bServerMode)
+ {
+ dwNHeaps = gcheap.HeapCount;
+ }
+
+ ExtOut("Number of GC Heaps: %d\n", dwNHeaps);
+ DWORD_PTR totalSize = 0;
+ if (!gcheap.bServerMode)
+ {
+ DacpGcHeapDetails heapDetails;
+ if (heapDetails.Request(g_sos) != S_OK)
+ {
+ ExtOut("Error requesting details\n");
+ return Status;
+ }
+
+ GCHeapInfo (heapDetails, totalSize);
+ ExtOut("Total Size: ");
+ PrintHeapSize(totalSize, 0);
+ }
+ else
+ {
+ DWORD dwAllocSize;
+ if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
+ {
+ ExtOut("Failed to get GCHeaps: integer overflow\n");
+ return Status;
+ }
+
+ CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
+ if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
+ {
+ ExtOut("Failed to get GCHeaps\n");
+ return Status;
+ }
+
+ DWORD n;
+ for (n = 0; n < dwNHeaps; n ++)
+ {
+ DacpGcHeapDetails heapDetails;
+ if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
+ {
+ ExtOut("Error requesting details\n");
+ return Status;
+ }
+ ExtOut("------------------------------\n");
+ ExtOut("Heap %d (%p)\n", n, SOS_PTR(heapAddrs[n]));
+ DWORD_PTR heapSize = 0;
+ GCHeapInfo (heapDetails, heapSize);
+ totalSize += heapSize;
+ ExtOut("Heap Size: " WIN86_8SPACES);
+ PrintHeapSize(heapSize, 0);
+ }
+ }
+ ExtOut("------------------------------\n");
+ ExtOut("GC Heap Size: " WIN86_8SPACES);
+ PrintHeapSize(totalSize, 0);
+ }
+ return Status;
+}
+
+void PrintGCStat(HeapStat *inStat, const char* label=NULL)
+{
+ if (inStat)
+ {
+ bool sorted = false;
+ try
+ {
+ inStat->Sort();
+ sorted = true;
+ inStat->Print(label);
+ }
+ catch(...)
+ {
+ ExtOut("Exception occurred while trying to %s the GC stats.\n", sorted ? "print" : "sort");
+ }
+
+ inStat->Delete();
+ }
+}
+
+#ifndef FEATURE_PAL
+
+DECLARE_API(TraverseHeap)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ BOOL bXmlFormat = FALSE;
+ BOOL bVerify = FALSE;
+ StringHolder Filename;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-xml", &bXmlFormat, COBOOL, FALSE},
+ {"-verify", &bVerify, COBOOL, FALSE},
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&Filename.data, COSTRING},
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ if (nArg != 1)
+ {
+ ExtOut("usage: HeapTraverse [-xml] filename\n");
+ return Status;
+ }
+
+ if (!g_snapshot.Build())
+ {
+ ExtOut("Unable to build snapshot of the garbage collector state\n");
+ return Status;
+ }
+
+ FILE* file = NULL;
+ if (fopen_s(&file, Filename.data, "w") != 0) {
+ ExtOut("Unable to open file\n");
+ return Status;
+ }
+
+ if (!bVerify)
+ ExtOut("Assuming a uncorrupted GC heap. If this is a crash dump consider -verify option\n");
+
+ HeapTraverser traverser(bVerify != FALSE);
+
+ ExtOut("Writing %s format to file %s\n", bXmlFormat ? "Xml" : "CLRProfiler", Filename.data);
+ ExtOut("Gathering types...\n");
+
+ // TODO: there may be a canonical list of methodtables in the runtime that we can
+ // traverse instead of exploring the gc heap for that list. We could then simplify the
+ // tree structure to a sorted list of methodtables, and the index is the ID.
+
+ // TODO: "Traversing object members" code should be generalized and shared between
+ // !gcroot and !traverseheap. Also !dumpheap can begin using GCHeapsTraverse.
+
+ if (!traverser.Initialize())
+ {
+ ExtOut("Error initializing heap traversal\n");
+ fclose(file);
+ return Status;
+ }
+
+ if (!traverser.CreateReport (file, bXmlFormat ? FORMAT_XML : FORMAT_CLRPROFILER))
+ {
+ ExtOut("Unable to write heap report\n");
+ fclose(file);
+ return Status;
+ }
+
+ fclose(file);
+ ExtOut("\nfile %s saved\n", Filename.data);
+
+ return Status;
+}
+
+#endif // FEATURE_PAL
+
+struct PrintRuntimeTypeArgs
+{
+ DWORD_PTR mtOfRuntimeType;
+ int handleFieldOffset;
+ DacpAppDomainStoreData adstore;
+};
+
+void PrintRuntimeTypes(DWORD_PTR objAddr,size_t Size,DWORD_PTR methodTable,LPVOID token)
+{
+ PrintRuntimeTypeArgs *pArgs = (PrintRuntimeTypeArgs *)token;
+
+ if (pArgs->mtOfRuntimeType == NULL)
+ {
+ NameForMT_s(methodTable, g_mdName, mdNameLen);
+
+ if (_wcscmp(g_mdName, W("System.RuntimeType")) == 0)
+ {
+ pArgs->mtOfRuntimeType = methodTable;
+ pArgs->handleFieldOffset = GetObjFieldOffset(objAddr, methodTable, W("m_handle"));
+ if (pArgs->handleFieldOffset <= 0)
+ ExtOut("Error getting System.RuntimeType.m_handle offset\n");
+
+ pArgs->adstore.Request(g_sos);
+ }
+ }
+
+ if ((methodTable == pArgs->mtOfRuntimeType) && (pArgs->handleFieldOffset > 0))
+ {
+ // Get the method table and display the information.
+ DWORD_PTR mtPtr;
+ if (MOVE(mtPtr, objAddr + pArgs->handleFieldOffset) == S_OK)
+ {
+ DMLOut(DMLObject(objAddr));
+
+ CLRDATA_ADDRESS appDomain = GetAppDomainForMT(mtPtr);
+ if (appDomain != NULL)
+ {
+ if (appDomain == pArgs->adstore.sharedDomain)
+ ExtOut(" %" POINTERSIZE "s", "Shared");
+
+ else if (appDomain == pArgs->adstore.systemDomain)
+ ExtOut(" %" POINTERSIZE "s", "System");
+ else
+ DMLOut(" %s", DMLDomain(appDomain));
+ }
+ else
+ {
+ ExtOut(" %" POINTERSIZE "s", "?");
+ }
+
+ NameForMT_s(mtPtr, g_mdName, mdNameLen);
+ DMLOut(" %s %S\n", DMLMethodTable(mtPtr), g_mdName);
+ }
+ }
+}
+
+
+DECLARE_API(DumpRuntimeTypes)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"/d", &dml, COBOOL, FALSE},
+ };
+
+ if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ return Status;
+
+ EnableDMLHolder dmlHolder(dml);
+
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s Type Name \n",
+ "Address", "Domain", "MT");
+ ExtOut("------------------------------------------------------------------------------\n");
+
+ PrintRuntimeTypeArgs pargs;
+ ZeroMemory(&pargs, sizeof(PrintRuntimeTypeArgs));
+
+ GCHeapsTraverse(PrintRuntimeTypes, (LPVOID)&pargs);
+ return Status;
+}
+
+#define MIN_FRAGMENTATIONBLOCK_BYTES (1024*512)
+namespace sos
+{
+ class FragmentationBlock
+ {
+ public:
+ FragmentationBlock(TADDR addr, size_t size, TADDR next, TADDR mt)
+ : mAddress(addr), mSize(size), mNext(next), mNextMT(mt)
+ {
+ }
+
+ inline TADDR GetAddress() const
+ {
+ return mAddress;
+ }
+ inline size_t GetSize() const
+ {
+ return mSize;
+ }
+
+ inline TADDR GetNextObject() const
+ {
+ return mNext;
+ }
+
+ inline TADDR GetNextMT() const
+ {
+ return mNextMT;
+ }
+
+ private:
+ TADDR mAddress;
+ size_t mSize;
+ TADDR mNext;
+ TADDR mNextMT;
+ };
+}
+
+class DumpHeapImpl
+{
+public:
+ DumpHeapImpl(PCSTR args)
+ : mStart(0), mStop(0), mMT(0), mMinSize(0), mMaxSize(~0),
+ mStat(FALSE), mStrings(FALSE), mVerify(FALSE),
+ mThinlock(FALSE), mShort(FALSE), mDML(FALSE),
+ mLive(FALSE), mDead(FALSE), mType(NULL)
+ {
+ ArrayHolder<char> type = NULL;
+
+ TADDR minTemp = 0;
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-mt", &mMT, COHEX, TRUE}, // dump objects with a given MethodTable
+ {"-type", &type, COSTRING, TRUE}, // list objects of specified type
+ {"-stat", &mStat, COBOOL, FALSE}, // dump a summary of types and the number of instances of each
+ {"-strings", &mStrings, COBOOL, FALSE}, // dump a summary of string objects
+ {"-verify", &mVerify, COBOOL, FALSE}, // verify heap objects (!heapverify)
+ {"-thinlock", &mThinlock, COBOOL, FALSE},// list only thinlocks
+ {"-short", &mShort, COBOOL, FALSE}, // list only addresses
+ {"-min", &mMinSize, COHEX, TRUE}, // min size of objects to display
+ {"-max", &mMaxSize, COHEX, TRUE}, // max size of objects to display
+ {"-live", &mLive, COHEX, FALSE}, // only print live objects
+ {"-dead", &mDead, COHEX, FALSE}, // only print dead objects
+#ifndef FEATURE_PAL
+ {"/d", &mDML, COBOOL, FALSE}, // Debugger Markup Language
+#endif
+ };
+
+ CMDValue arg[] =
+ { // vptr, type
+ {&mStart, COHEX},
+ {&mStop, COHEX}
+ };
+
+ size_t nArgs = 0;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArgs))
+ sos::Throw<sos::Exception>("Failed to parse command line arguments.");
+
+ if (mStart == 0)
+ mStart = minTemp;
+
+ if (mStop == 0)
+ mStop = sos::GCHeap::HeapEnd;
+
+ if (type && mMT)
+ {
+ sos::Throw<sos::Exception>("Cannot specify both -mt and -type");
+ }
+
+ if (mLive && mDead)
+ {
+ sos::Throw<sos::Exception>("Cannot specify both -live and -dead.");
+ }
+
+ if (mMinSize > mMaxSize)
+ {
+ sos::Throw<sos::Exception>("wrong argument");
+ }
+
+ // If the user gave us a type, convert it to unicode and clean up "type".
+ if (type && !mStrings)
+ {
+ size_t iLen = strlen(type) + 1;
+ mType = new WCHAR[iLen];
+ MultiByteToWideChar(CP_ACP, 0, type, -1, mType, (int)iLen);
+ }
+ }
+
+ ~DumpHeapImpl()
+ {
+ if (mType)
+ delete [] mType;
+ }
+
+ void Run()
+ {
+ // enable Debugger Markup Language
+ EnableDMLHolder dmlholder(mDML);
+ sos::GCHeap gcheap;
+
+ if (!gcheap.AreGCStructuresValid())
+ DisplayInvalidStructuresMessage();
+
+ if (IsMiniDumpFile())
+ {
+ ExtOut("In a minidump without full memory, most gc heap structures will not be valid.\n");
+ ExtOut("If you need this functionality, get a full memory dump with \".dump /ma mydump.dmp\"\n");
+ }
+
+#ifndef FEATURE_PAL
+ if (mLive || mDead)
+ {
+ GCRootImpl gcroot;
+ mLiveness = gcroot.GetLiveObjects();
+ }
+#endif
+
+ // Some of the "specialty" versions of DumpHeap have slightly
+ // different implementations than the standard version of DumpHeap.
+ // We seperate them out to not clutter the standard DumpHeap function.
+ if (mShort)
+ DumpHeapShort(gcheap);
+ else if (mThinlock)
+ DumpHeapThinlock(gcheap);
+ else if (mStrings)
+ DumpHeapStrings(gcheap);
+ else
+ DumpHeap(gcheap);
+
+ if (mVerify)
+ ValidateSyncTable(gcheap);
+ }
+
+ static bool ValidateSyncTable(sos::GCHeap &gcheap)
+ {
+ bool succeeded = true;
+ for (sos::SyncBlkIterator itr; itr; ++itr)
+ {
+ sos::CheckInterrupt();
+
+ if (!itr->IsFree())
+ {
+ if (!sos::IsObject(itr->GetObject(), true))
+ {
+ ExtOut("SyncBlock %d corrupted, points to invalid object %p\n",
+ itr->GetIndex(), SOS_PTR(itr->GetObject()));
+ succeeded = false;
+ }
+ else
+ {
+ // Does the object header point to this syncblock index?
+ sos::Object obj = itr->GetObject();
+ ULONG header = 0;
+
+ if (!obj.TryGetHeader(header))
+ {
+ ExtOut("Failed to get object header for object %p while inspecting syncblock at index %d.\n",
+ SOS_PTR(itr->GetObject()), itr->GetIndex());
+ succeeded = false;
+ }
+ else
+ {
+ bool valid = false;
+ if ((header & BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX) != 0 && (header & BIT_SBLK_IS_HASHCODE) == 0)
+ {
+ ULONG index = header & MASK_SYNCBLOCKINDEX;
+ valid = (ULONG)itr->GetIndex() == index;
+ }
+
+ if (!valid)
+ {
+ ExtOut("Object header for %p should have a SyncBlock index of %d.\n",
+ SOS_PTR(itr->GetObject()), itr->GetIndex());
+ succeeded = false;
+ }
+ }
+ }
+ }
+ }
+
+ return succeeded;
+ }
+private:
+ DumpHeapImpl(const DumpHeapImpl &);
+
+ bool Verify(const sos::ObjectIterator &itr)
+ {
+ if (mVerify)
+ {
+ char buffer[1024];
+ if (!itr.Verify(buffer, _countof(buffer)))
+ {
+ ExtOut(buffer);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ bool IsCorrectType(const sos::Object &obj)
+ {
+ if (mMT != NULL)
+ return mMT == obj.GetMT();
+
+ if (mType != NULL)
+ {
+ WString name = obj.GetTypeName();
+ return _wcsstr(name.c_str(), mType) != NULL;
+ }
+
+ return true;
+ }
+
+ bool IsCorrectSize(const sos::Object &obj)
+ {
+ size_t size = obj.GetSize();
+ return size >= mMinSize && size <= mMaxSize;
+ }
+
+ bool IsCorrectLiveness(const sos::Object &obj)
+ {
+#ifndef FEATURE_PAL
+ if (mLive && mLiveness.find(obj.GetAddress()) == mLiveness.end())
+ return false;
+
+ if (mDead && (mLiveness.find(obj.GetAddress()) != mLiveness.end() || obj.IsFree()))
+ return false;
+#endif
+ return true;
+ }
+
+
+
+ inline void PrintHeader()
+ {
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %8s\n", "Address", "MT", "Size");
+ }
+
+ void DumpHeap(sos::GCHeap &gcheap)
+ {
+ HeapStat stats;
+
+ // For heap fragmentation tracking.
+ TADDR lastFreeObj = NULL;
+ size_t lastFreeSize = 0;
+
+ if (!mStat)
+ PrintHeader();
+
+ for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr)
+ {
+ if (!Verify(itr))
+ return;
+
+ bool onLOH = itr.IsCurrObjectOnLOH();
+
+ // Check for free objects to report fragmentation
+ if (lastFreeObj != NULL)
+ ReportFreeObject(lastFreeObj, lastFreeSize, itr->GetAddress(), itr->GetMT());
+
+ if (!onLOH && itr->IsFree())
+ {
+ lastFreeObj = *itr;
+ lastFreeSize = itr->GetSize();
+ }
+ else
+ {
+ lastFreeObj = NULL;
+ }
+
+ if (IsCorrectType(*itr) && IsCorrectSize(*itr) && IsCorrectLiveness(*itr))
+ {
+ stats.Add((DWORD_PTR)itr->GetMT(), (DWORD)itr->GetSize());
+ if (!mStat)
+ DMLOut("%s %s %8d%s\n", DMLObject(itr->GetAddress()), DMLDumpHeapMT(itr->GetMT()), itr->GetSize(),
+ itr->IsFree() ? " Free":" ");
+ }
+ }
+
+ if (!mStat)
+ ExtOut("\n");
+
+ stats.Sort();
+ stats.Print();
+
+ PrintFragmentationReport();
+ }
+
+ struct StringSetEntry
+ {
+ StringSetEntry() : count(0), size(0)
+ {
+ str[0] = 0;
+ }
+
+ StringSetEntry(__in_ecount(64) WCHAR tmp[64], size_t _size)
+ : count(1), size(_size)
+ {
+ memcpy(str, tmp, sizeof(str));
+ }
+
+ void Add(size_t _size) const
+ {
+ count++;
+ size += _size;
+ }
+
+ mutable size_t count;
+ mutable size_t size;
+ WCHAR str[64];
+
+ bool operator<(const StringSetEntry &rhs) const
+ {
+ return _wcscmp(str, rhs.str) < 0;
+ }
+ };
+
+
+ static bool StringSetCompare(const StringSetEntry &a1, const StringSetEntry &a2)
+ {
+ return a1.size < a2.size;
+ }
+
+ void DumpHeapStrings(sos::GCHeap &gcheap)
+ {
+#ifdef FEATURE_PAL
+ ExtOut("Not implemented.\n");
+#else
+ const int offset = sos::Object::GetStringDataOffset();
+ typedef std::set<StringSetEntry> Set;
+ Set set; // A set keyed off of the string's text
+
+ StringSetEntry tmp; // Temp string used to keep track of the set
+ ULONG fetched = 0;
+
+ TableOutput out(3, POINTERSIZE_HEX, AlignRight);
+ for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr)
+ {
+ if (IsInterrupt())
+ break;
+
+ if (itr->IsString() && IsCorrectSize(*itr) && IsCorrectLiveness(*itr))
+ {
+ CLRDATA_ADDRESS addr = itr->GetAddress();
+ size_t size = itr->GetSize();
+
+ if (!mStat)
+ out.WriteRow(ObjectPtr(addr), Pointer(itr->GetMT()), Decimal(size));
+
+ // Don't bother calculating the size of the string, just read the full 64 characters of the buffer. The null
+ // terminator we read will terminate the string.
+ HRESULT hr = g_ExtData->ReadVirtual(TO_CDADDR(addr+offset), tmp.str, sizeof(WCHAR)*(_countof(tmp.str)-1), &fetched);
+ if (SUCCEEDED(hr))
+ {
+ // Ensure we null terminate the string. Note that this will not overrun the buffer as we only
+ // wrote a max of 63 characters into the 64 character buffer.
+ tmp.str[fetched/sizeof(WCHAR)] = 0;
+ Set::iterator sitr = set.find(tmp);
+ if (sitr == set.end())
+ {
+ tmp.size = size;
+ tmp.count = 1;
+ set.insert(tmp);
+ }
+ else
+ {
+ sitr->Add(size);
+ }
+ }
+ }
+ }
+
+ ExtOut("\n");
+
+ // Now flatten the set into a vector. This is much faster than keeping two sets, or using a multimap.
+ typedef std::vector<StringSetEntry> Vect;
+ Vect v(set.begin(), set.end());
+ std::sort(v.begin(), v.end(), &DumpHeapImpl::StringSetCompare);
+
+ // Now print out the data. The call to Flatten ensures that we don't print newlines to break up the
+ // output in strange ways.
+ for (Vect::iterator vitr = v.begin(); vitr != v.end(); ++vitr)
+ {
+ if (IsInterrupt())
+ break;
+
+ Flatten(vitr->str, (unsigned int)_wcslen(vitr->str));
+ out.WriteRow(Decimal(vitr->size), Decimal(vitr->count), vitr->str);
+ }
+#endif // FEATURE_PAL
+ }
+
+ void DumpHeapShort(sos::GCHeap &gcheap)
+ {
+ for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr)
+ {
+ if (!Verify(itr))
+ return;
+
+ if (IsCorrectType(*itr) && IsCorrectSize(*itr) && IsCorrectLiveness(*itr))
+ DMLOut("%s\n", DMLObject(itr->GetAddress()));
+ }
+ }
+
+ void DumpHeapThinlock(sos::GCHeap &gcheap)
+ {
+ int count = 0;
+
+ PrintHeader();
+ for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr)
+ {
+ if (!Verify(itr))
+ return;
+
+ sos::ThinLockInfo lockInfo;
+ if (IsCorrectType(*itr) && itr->GetThinLock(lockInfo))
+ {
+ DMLOut("%s %s %8d", DMLObject(itr->GetAddress()), DMLDumpHeapMT(itr->GetMT()), itr->GetSize());
+ ExtOut(" ThinLock owner %x (%p) Recursive %x\n", lockInfo.ThreadId,
+ SOS_PTR(lockInfo.ThreadPtr), lockInfo.Recursion);
+
+ count++;
+ }
+ }
+
+ ExtOut("Found %d objects.\n", count);
+ }
+
+private:
+ TADDR mStart,
+ mStop,
+ mMT,
+ mMinSize,
+ mMaxSize;
+
+ BOOL mStat,
+ mStrings,
+ mVerify,
+ mThinlock,
+ mShort,
+ mDML,
+ mLive,
+ mDead;
+
+
+ WCHAR *mType;
+
+private:
+#if !defined(FEATURE_PAL)
+ // Windows only
+ std::unordered_set<TADDR> mLiveness;
+ typedef std::list<sos::FragmentationBlock> FragmentationList;
+ FragmentationList mFrag;
+
+ void InitFragmentationList()
+ {
+ mFrag.clear();
+ }
+
+ void ReportFreeObject(TADDR addr, size_t size, TADDR next, TADDR mt)
+ {
+ if (size >= MIN_FRAGMENTATIONBLOCK_BYTES)
+ mFrag.push_back(sos::FragmentationBlock(addr, size, next, mt));
+ }
+
+ void PrintFragmentationReport()
+ {
+ if (mFrag.size() > 0)
+ {
+ ExtOut("Fragmented blocks larger than 0.5 MB:\n");
+ ExtOut("%" POINTERSIZE "s %8s %16s\n", "Addr", "Size", "Followed by");
+
+ for (FragmentationList::const_iterator itr = mFrag.begin(); itr != mFrag.end(); ++itr)
+ {
+ sos::MethodTable mt = itr->GetNextMT();
+ ExtOut("%p %6.1fMB " WIN64_8SPACES "%p %S\n",
+ SOS_PTR(itr->GetAddress()),
+ ((double)itr->GetSize()) / 1024.0 / 1024.0,
+ SOS_PTR(itr->GetNextObject()),
+ mt.GetName());
+ }
+ }
+ }
+#else
+ void InitFragmentationList() {}
+ void ReportFreeObject(TADDR, TADDR, size_t, TADDR) {}
+ void PrintFragmentationReport() {}
+#endif
+};
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function dumps all objects on GC heap. It also displays *
+* statistics of objects. If GC heap is corrupted, it will stop at
+* the bad place. (May not work if GC is in progress.) *
+* *
+\**********************************************************************/
+DECLARE_API(DumpHeap)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ if (!g_snapshot.Build())
+ {
+ ExtOut("Unable to build snapshot of the garbage collector state\n");
+ return E_FAIL;
+ }
+
+ try
+ {
+ DumpHeapImpl dumpHeap(args);
+ dumpHeap.Run();
+
+ return S_OK;
+ }
+ catch(const sos::Exception &e)
+ {
+ ExtOut("%s\n", e.what());
+ return E_FAIL;
+ }
+}
+
+DECLARE_API(VerifyHeap)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ if (!g_snapshot.Build())
+ {
+ ExtOut("Unable to build snapshot of the garbage collector state\n");
+ return E_FAIL;
+ }
+
+ try
+ {
+ bool succeeded = true;
+ char buffer[1024];
+ sos::GCHeap gcheap;
+ sos::ObjectIterator itr = gcheap.WalkHeap();
+
+ while (itr)
+ {
+ if (itr.Verify(buffer, _countof(buffer)))
+ {
+ ++itr;
+ }
+ else
+ {
+ succeeded = false;
+ ExtOut(buffer);
+ itr.MoveToNextObjectCarefully();
+ }
+ }
+
+ if (!DumpHeapImpl::ValidateSyncTable(gcheap))
+ succeeded = false;
+
+ if (succeeded)
+ ExtOut("No heap corruption detected.\n");
+
+ return S_OK;
+ }
+ catch(const sos::Exception &e)
+ {
+ ExtOut("%s\n", e.what());
+ return E_FAIL;
+ }
+}
+
+#ifndef FEATURE_PAL
+
+enum failure_get_memory
+{
+ fgm_no_failure = 0,
+ fgm_reserve_segment = 1,
+ fgm_commit_segment_beg = 2,
+ fgm_commit_eph_segment = 3,
+ fgm_grow_table = 4,
+ fgm_commit_table = 5
+};
+
+enum oom_reason
+{
+ oom_no_failure = 0,
+ oom_budget = 1,
+ oom_cant_commit = 2,
+ oom_cant_reserve = 3,
+ oom_loh = 4,
+ oom_low_mem = 5,
+ oom_unproductive_full_gc = 6
+};
+
+static const char *const str_oom[] =
+{
+ "There was no managed OOM due to allocations on the GC heap", // oom_no_failure
+ "This is likely to be a bug in GC", // oom_budget
+ "Didn't have enough memory to commit", // oom_cant_commit
+ "This is likely to be a bug in GC", // oom_cant_reserve
+ "Didn't have enough memory to allocate an LOH segment", // oom_loh
+ "Low on memory during GC", // oom_low_mem
+ "Could not do a full GC" // oom_unproductive_full_gc
+};
+
+static const char *const str_fgm[] =
+{
+ "There was no failure to allocate memory", // fgm_no_failure
+ "Failed to reserve memory", // fgm_reserve_segment
+ "Didn't have enough memory to commit beginning of the segment", // fgm_commit_segment_beg
+ "Didn't have enough memory to commit the new ephemeral segment", // fgm_commit_eph_segment
+ "Didn't have enough memory to grow the internal GC datastructures", // fgm_grow_table
+ "Didn't have enough memory to commit the internal GC datastructures", // fgm_commit_table
+};
+
+void PrintOOMInfo(DacpOomData* oomData)
+{
+ ExtOut("Managed OOM occurred after GC #%d (Requested to allocate %d bytes)\n",
+ oomData->gc_index, oomData->alloc_size);
+
+ if ((oomData->reason == oom_budget) ||
+ (oomData->reason == oom_cant_reserve))
+ {
+ // TODO: This message needs to be updated with more precious info.
+ ExtOut("%s, please contact PSS\n", str_oom[oomData->reason]);
+ }
+ else
+ {
+ ExtOut("Reason: %s\n", str_oom[oomData->reason]);
+ }
+
+ // Now print out the more detailed memory info if any.
+ if (oomData->fgm != fgm_no_failure)
+ {
+ ExtOut("Detail: %s: %s (%d bytes)",
+ (oomData->loh_p ? "LOH" : "SOH"),
+ str_fgm[oomData->fgm],
+ oomData->size);
+
+ if ((oomData->fgm == fgm_commit_segment_beg) ||
+ (oomData->fgm == fgm_commit_eph_segment) ||
+ (oomData->fgm == fgm_grow_table) ||
+ (oomData->fgm == fgm_commit_table))
+ {
+ // If it's a commit error (fgm_grow_table can indicate a reserve
+ // or a commit error since we make one VirtualAlloc call to
+ // reserve and commit), we indicate the available commit
+ // space if we recorded it.
+ if (oomData->available_pagefile_mb)
+ {
+ ExtOut(" - on GC entry available commit space was %d MB",
+ oomData->available_pagefile_mb);
+ }
+ }
+
+ ExtOut("\n");
+ }
+}
+
+DECLARE_API(AnalyzeOOM)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+#ifndef FEATURE_PAL
+
+ if (!InitializeHeapData ())
+ {
+ ExtOut("GC Heap not initialized yet.\n");
+ return S_OK;
+ }
+
+ BOOL bHasManagedOOM = FALSE;
+ DacpOomData oomData;
+ memset (&oomData, 0, sizeof(oomData));
+ if (!IsServerBuild())
+ {
+ if (oomData.Request(g_sos) != S_OK)
+ {
+ ExtOut("Error requesting OOM data\n");
+ return E_FAIL;
+ }
+ if (oomData.reason != oom_no_failure)
+ {
+ bHasManagedOOM = TRUE;
+ PrintOOMInfo(&oomData);
+ }
+ }
+ else
+ {
+ DWORD dwNHeaps = GetGcHeapCount();
+ DWORD dwAllocSize;
+ if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
+ {
+ ExtOut("Failed to get GCHeaps: integer overflow\n");
+ return Status;
+ }
+
+ CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
+ if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
+ {
+ ExtOut("Failed to get GCHeaps\n");
+ return Status;
+ }
+
+ for (DWORD n = 0; n < dwNHeaps; n ++)
+ {
+ if (oomData.Request(g_sos, heapAddrs[n]) != S_OK)
+ {
+ ExtOut("Heap %d: Error requesting OOM data\n", n);
+ return E_FAIL;
+ }
+ if (oomData.reason != oom_no_failure)
+ {
+ if (!bHasManagedOOM)
+ {
+ bHasManagedOOM = TRUE;
+ }
+ ExtOut("---------Heap %#-2d---------\n", n);
+ PrintOOMInfo(&oomData);
+ }
+ }
+ }
+
+ if (!bHasManagedOOM)
+ {
+ ExtOut("%s\n", str_oom[oomData.reason]);
+ }
+
+ return S_OK;
+#else
+ _ASSERTE(false);
+ return E_FAIL;
+#endif // FEATURE_PAL
+}
+
+DECLARE_API(VerifyObj)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ TADDR taddrObj = 0;
+ TADDR taddrMT;
+ size_t objSize;
+
+ BOOL bValid = FALSE;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"/d", &dml, COBOOL, FALSE},
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&taddrObj, COHEX}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ BOOL bContainsPointers;
+
+ if (FAILED(GetMTOfObject(taddrObj, &taddrMT)) ||
+ !GetSizeEfficient(taddrObj, taddrMT, FALSE, objSize, bContainsPointers))
+ {
+ ExtOut("object %#p does not have valid method table\n", SOS_PTR(taddrObj));
+ goto Exit;
+ }
+
+ // we need to build g_snapshot as it is later used in GetGeneration
+ if (!g_snapshot.Build())
+ {
+ ExtOut("Unable to build snapshot of the garbage collector state\n");
+ goto Exit;
+ }
+ DacpGcHeapDetails *pheapDetails = g_snapshot.GetHeap(taddrObj);
+ bValid = VerifyObject(*pheapDetails, taddrObj, taddrMT, objSize, TRUE);
+
+Exit:
+ if (bValid)
+ {
+ ExtOut("object %#p is a valid object\n", SOS_PTR(taddrObj));
+ }
+
+ return Status;
+}
+
+void LNODisplayOutput(LPCWSTR tag, TADDR pMT, TADDR currentObj, size_t size)
+{
+ sos::Object obj(currentObj, pMT);
+ DMLOut("%S %s %12d (0x%x)\t%S\n", tag, DMLObject(currentObj), size, size, obj.GetTypeName());
+}
+
+DECLARE_API(ListNearObj)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+#if !defined(FEATURE_PAL)
+
+ TADDR taddrArg = 0;
+ TADDR taddrObj = 0;
+ // we may want to provide a more exact version of searching for the
+ // previous object in the heap, using the brick table, instead of
+ // looking for what may be valid method tables...
+ //BOOL bExact;
+ //CMDOption option[] =
+ //{
+ // // name, vptr, type, hasValue
+ // {"-exact", &bExact, COBOOL, FALSE}
+ //};
+
+ BOOL dml = FALSE;
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"/d", &dml, COBOOL, FALSE},
+ };
+ CMDValue arg[] =
+ {
+ // vptr, type
+ {&taddrArg, COHEX}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || nArg != 1)
+ {
+ ExtOut("Usage: !ListNearObj <obj_address>\n");
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+
+ if (!g_snapshot.Build())
+ {
+ ExtOut("Unable to build snapshot of the garbage collector state\n");
+ return Status;
+ }
+
+ taddrObj = Align(taddrArg);
+
+ DacpGcHeapDetails *heap = g_snapshot.GetHeap(taddrArg);
+ if (heap == NULL)
+ {
+ ExtOut("Address %p does not lie in the managed heap\n", SOS_PTR(taddrObj));
+ return Status;
+ }
+
+ TADDR_SEGINFO trngSeg = {0, 0, 0};
+ TADDR_RANGE allocCtx = {0, 0};
+ BOOL bLarge;
+ int gen;
+ if (!GCObjInHeap(taddrObj, *heap, trngSeg, gen, allocCtx, bLarge))
+ {
+ ExtOut("Failed to find the segment of the managed heap where the object %p resides\n",
+ SOS_PTR(taddrObj));
+ return Status;
+ }
+
+ TADDR objMT = NULL;
+ size_t objSize = 0;
+ BOOL bObj = FALSE;
+ TADDR taddrCur;
+ TADDR curMT = 0;
+ size_t curSize = 0;
+ BOOL bCur = FALSE;
+ TADDR taddrNxt;
+ TADDR nxtMT = 0;
+ size_t nxtSize = 0;
+ BOOL bNxt = FALSE;
+ BOOL bContainsPointers;
+
+ std::vector<TADDR> candidate;
+ candidate.reserve(10);
+
+ // since we'll be reading back I'll prime the read cache to a buffer before the current address
+ MOVE(taddrCur, _max(trngSeg.start, taddrObj-DT_OS_PAGE_SIZE));
+
+ // ===== Look for a good candidate preceeding taddrObj
+
+ for (taddrCur = taddrObj - sizeof(TADDR); taddrCur >= trngSeg.start; taddrCur -= sizeof(TADDR))
+ {
+ // currently we don't pay attention to allocation contexts. if this
+ // proves to be an issue we need to reconsider the code below
+ if (SUCCEEDED(GetMTOfObject(taddrCur, &curMT)) &&
+ GetSizeEfficient(taddrCur, curMT, bLarge, curSize, bContainsPointers))
+ {
+ // remember this as one of the possible "good" objects preceeding taddrObj
+ candidate.push_back(taddrCur);
+
+ std::vector<TADDR>::iterator it =
+ std::find(candidate.begin(), candidate.end(), taddrCur+curSize);
+ if (it != candidate.end())
+ {
+ // We found a chain of two objects preceeding taddrObj. We'll
+ // trust this is a good indication that the two objects are valid.
+ // What is not valid is possibly the object following the second
+ // one...
+ taddrCur = *it;
+ GetMTOfObject(taddrCur, &curMT);
+ GetSizeEfficient(taddrCur, curMT, bLarge, curSize, bContainsPointers);
+ bCur = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (!bCur && !candidate.empty())
+ {
+ // pick the closest object to taddrObj
+ taddrCur = *(candidate.begin());
+ GetMTOfObject(taddrCur, &curMT);
+ GetSizeEfficient(taddrCur, curMT, bLarge, curSize, bContainsPointers);
+ // we have a candidate, even if not confirmed
+ bCur = TRUE;
+ }
+
+ taddrNxt = taddrObj;
+ if (taddrArg == taddrObj)
+ {
+ taddrNxt += sizeof(TADDR);
+ }
+
+ // ===== Now look at taddrObj
+ if (taddrObj == taddrArg)
+ {
+ // only look at taddrObj if it's the same as what user passed in, meaning it's aligned.
+ if (SUCCEEDED(GetMTOfObject(taddrObj, &objMT)) &&
+ GetSizeEfficient(taddrObj, objMT, bLarge, objSize, bContainsPointers))
+ {
+ bObj = TRUE;
+ taddrNxt = taddrObj+objSize;
+ }
+ }
+
+ if ((taddrCur + curSize > taddrArg) && taddrCur + curSize < trngSeg.end)
+ {
+ if (SUCCEEDED(GetMTOfObject(taddrCur + curSize, &nxtMT)) &&
+ GetSizeEfficient(taddrObj, objMT, bLarge, objSize, bContainsPointers))
+ {
+ taddrNxt = taddrCur+curSize;
+ }
+ }
+
+ // ===== And finally move on to elements following taddrObj
+
+ for (; taddrNxt < trngSeg.end; taddrNxt += sizeof(TADDR))
+ {
+ if (SUCCEEDED(GetMTOfObject(taddrNxt, &nxtMT)) &&
+ GetSizeEfficient(taddrNxt, nxtMT, bLarge, nxtSize, bContainsPointers))
+ {
+ bNxt = TRUE;
+ break;
+ }
+ }
+
+ if (bCur)
+ LNODisplayOutput(W("Before: "), curMT, taddrCur, curSize);
+ else
+ ExtOut("Before: couldn't find any object between %#p and %#p\n",
+ SOS_PTR(trngSeg.start), SOS_PTR(taddrArg));
+
+ if (bObj)
+ LNODisplayOutput(W("Current:"), objMT, taddrObj, objSize);
+
+ if (bNxt)
+ LNODisplayOutput(W("After: "), nxtMT, taddrNxt, nxtSize);
+ else
+ ExtOut("After: couldn't find any object between %#p and %#p\n",
+ SOS_PTR(taddrArg), SOS_PTR(trngSeg.end));
+
+ if (bCur && bNxt &&
+ (((taddrCur+curSize == taddrObj) && (taddrObj+objSize == taddrNxt)) || (taddrCur+curSize == taddrNxt)))
+ {
+ ExtOut("Heap local consistency confirmed.\n");
+ }
+ else
+ {
+ ExtOut("Heap local consistency not confirmed.\n");
+ }
+
+ return Status;
+
+#else
+
+ _ASSERTE(false);
+ return E_FAIL;
+
+#endif // FEATURE_PAL
+}
+
+
+DECLARE_API(GCHeapStat)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+
+#ifndef FEATURE_PAL
+
+ BOOL bIncUnreachable = FALSE;
+ BOOL dml = FALSE;
+
+ CMDOption option[] = {
+ // name, vptr, type, hasValue
+ {"-inclUnrooted", &bIncUnreachable, COBOOL, FALSE},
+ {"-iu", &bIncUnreachable, COBOOL, FALSE},
+ {"/d", &dml, COBOOL, FALSE}
+ };
+
+ if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ ExtOut("%-8s %12s %12s %12s %12s\n", "Heap", "Gen0", "Gen1", "Gen2", "LOH");
+
+ if (!IsServerBuild())
+ {
+ float tempf;
+ DacpGcHeapDetails heapDetails;
+ if (heapDetails.Request(g_sos) != S_OK)
+ {
+ ExtErr("Error requesting gc heap details\n");
+ return Status;
+ }
+
+ HeapUsageStat hpUsage;
+ if (GCHeapUsageStats(heapDetails, bIncUnreachable, &hpUsage))
+ {
+ ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n", 0,
+ hpUsage.genUsage[0].allocd, hpUsage.genUsage[1].allocd,
+ hpUsage.genUsage[2].allocd, hpUsage.genUsage[3].allocd);
+ ExtOut("\nFree space: Percentage\n");
+ ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u", 0,
+ hpUsage.genUsage[0].freed, hpUsage.genUsage[1].freed,
+ hpUsage.genUsage[2].freed, hpUsage.genUsage[3].freed);
+ tempf = ((float)(hpUsage.genUsage[0].freed+hpUsage.genUsage[1].freed+hpUsage.genUsage[2].freed)) /
+ (hpUsage.genUsage[0].allocd+hpUsage.genUsage[1].allocd+hpUsage.genUsage[2].allocd);
+ ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf),
+ (int)(100*((float)hpUsage.genUsage[3].freed) / (hpUsage.genUsage[3].allocd)));
+ if (bIncUnreachable)
+ {
+ ExtOut("\nUnrooted objects: Percentage\n");
+ ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u", 0,
+ hpUsage.genUsage[0].unrooted, hpUsage.genUsage[1].unrooted,
+ hpUsage.genUsage[2].unrooted, hpUsage.genUsage[3].unrooted);
+ tempf = ((float)(hpUsage.genUsage[0].unrooted+hpUsage.genUsage[1].unrooted+hpUsage.genUsage[2].unrooted)) /
+ (hpUsage.genUsage[0].allocd+hpUsage.genUsage[1].allocd+hpUsage.genUsage[2].allocd);
+ ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf),
+ (int)(100*((float)hpUsage.genUsage[3].unrooted) / (hpUsage.genUsage[3].allocd)));
+ }
+ }
+ }
+ else
+ {
+ float tempf;
+ DacpGcHeapData gcheap;
+ if (gcheap.Request(g_sos) != S_OK)
+ {
+ ExtErr("Error requesting GC Heap data\n");
+ return Status;
+ }
+
+ DWORD dwAllocSize;
+ DWORD dwNHeaps = gcheap.HeapCount;
+ if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
+ {
+ ExtErr("Failed to get GCHeaps: integer overflow\n");
+ return Status;
+ }
+
+ CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
+ if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
+ {
+ ExtErr("Failed to get GCHeaps\n");
+ return Status;
+ }
+
+ ArrayHolder<HeapUsageStat> hpUsage = new NOTHROW HeapUsageStat[dwNHeaps];
+ if (hpUsage == NULL)
+ {
+ ReportOOM();
+ return Status;
+ }
+
+ // aggregate stats accross heaps / generation
+ GenUsageStat genUsageStat[4] = {0, 0, 0, 0};
+
+ for (DWORD n = 0; n < dwNHeaps; n ++)
+ {
+ DacpGcHeapDetails heapDetails;
+ if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
+ {
+ ExtErr("Error requesting gc heap details\n");
+ return Status;
+ }
+
+ if (GCHeapUsageStats(heapDetails, bIncUnreachable, &hpUsage[n]))
+ {
+ for (int i = 0; i < 4; ++i)
+ {
+ genUsageStat[i].allocd += hpUsage[n].genUsage[i].allocd;
+ genUsageStat[i].freed += hpUsage[n].genUsage[i].freed;
+ if (bIncUnreachable)
+ {
+ genUsageStat[i].unrooted += hpUsage[n].genUsage[i].unrooted;
+ }
+ }
+ }
+ }
+
+ for (DWORD n = 0; n < dwNHeaps; n ++)
+ {
+ ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n", n,
+ hpUsage[n].genUsage[0].allocd, hpUsage[n].genUsage[1].allocd,
+ hpUsage[n].genUsage[2].allocd, hpUsage[n].genUsage[3].allocd);
+ }
+ ExtOut("Total %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n",
+ genUsageStat[0].allocd, genUsageStat[1].allocd,
+ genUsageStat[2].allocd, genUsageStat[3].allocd);
+
+ ExtOut("\nFree space: Percentage\n");
+ for (DWORD n = 0; n < dwNHeaps; n ++)
+ {
+ ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u", n,
+ hpUsage[n].genUsage[0].freed, hpUsage[n].genUsage[1].freed,
+ hpUsage[n].genUsage[2].freed, hpUsage[n].genUsage[3].freed);
+
+ tempf = ((float)(hpUsage[n].genUsage[0].freed+hpUsage[n].genUsage[1].freed+hpUsage[n].genUsage[2].freed)) /
+ (hpUsage[n].genUsage[0].allocd+hpUsage[n].genUsage[1].allocd+hpUsage[n].genUsage[2].allocd);
+ ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf),
+ (int)(100*((float)hpUsage[n].genUsage[3].freed) / (hpUsage[n].genUsage[3].allocd))
+ );
+ }
+ ExtOut("Total %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n",
+ genUsageStat[0].freed, genUsageStat[1].freed,
+ genUsageStat[2].freed, genUsageStat[3].freed);
+
+ if (bIncUnreachable)
+ {
+ ExtOut("\nUnrooted objects: Percentage\n");
+ for (DWORD n = 0; n < dwNHeaps; n ++)
+ {
+ ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u", n,
+ hpUsage[n].genUsage[0].unrooted, hpUsage[n].genUsage[1].unrooted,
+ hpUsage[n].genUsage[2].unrooted, hpUsage[n].genUsage[3].unrooted);
+
+ tempf = ((float)(hpUsage[n].genUsage[0].unrooted+hpUsage[n].genUsage[1].unrooted+hpUsage[n].genUsage[2].unrooted)) /
+ (hpUsage[n].genUsage[0].allocd+hpUsage[n].genUsage[1].allocd+hpUsage[n].genUsage[2].allocd);
+ ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf),
+ (int)(100*((float)hpUsage[n].genUsage[3].unrooted) / (hpUsage[n].genUsage[3].allocd)));
+ }
+ ExtOut("Total %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n",
+ genUsageStat[0].unrooted, genUsageStat[1].unrooted,
+ genUsageStat[2].unrooted, genUsageStat[3].unrooted);
+ }
+
+ }
+
+ return Status;
+
+#else
+
+ _ASSERTE(false);
+ return E_FAIL;
+
+#endif // FEATURE_PAL
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function dumps what is in the syncblock cache. By default *
+* it dumps all active syncblocks. Using -all to dump all syncblocks
+* *
+\**********************************************************************/
+DECLARE_API(SyncBlk)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ BOOL bDumpAll = FALSE;
+ size_t nbAsked = 0;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-all", &bDumpAll, COBOOL, FALSE},
+ {"/d", &dml, COBOOL, FALSE}
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&nbAsked, COSIZE_T}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ DacpSyncBlockData syncBlockData;
+ if (syncBlockData.Request(g_sos,1) != S_OK)
+ {
+ ExtOut("Error requesting SyncBlk data\n");
+ return Status;
+ }
+
+ DWORD dwCount = syncBlockData.SyncBlockCount;
+
+ ExtOut("Index" WIN64_8SPACES " SyncBlock MonitorHeld Recursion Owning Thread Info" WIN64_8SPACES " SyncBlock Owner\n");
+ ULONG freeCount = 0;
+ ULONG CCWCount = 0;
+ ULONG RCWCount = 0;
+ ULONG CFCount = 0;
+ for (DWORD nb = 1; nb <= dwCount; nb++)
+ {
+ if (IsInterrupt())
+ return Status;
+
+ if (nbAsked && nb != nbAsked)
+ {
+ continue;
+ }
+
+ if (syncBlockData.Request(g_sos,nb) != S_OK)
+ {
+ ExtOut("SyncBlock %d is invalid%s\n", nb,
+ (nb != nbAsked) ? ", continuing..." : "");
+ continue;
+ }
+
+ BOOL bPrint = (bDumpAll || nb == nbAsked || (syncBlockData.MonitorHeld > 0 && !syncBlockData.bFree));
+
+ if (bPrint)
+ {
+ ExtOut("%5d ", nb);
+ if (!syncBlockData.bFree || nb != nbAsked)
+ {
+ ExtOut("%p ", syncBlockData.SyncBlockPointer);
+ ExtOut("%11d ", syncBlockData.MonitorHeld);
+ ExtOut("%9d ", syncBlockData.Recursion);
+ ExtOut("%p ", syncBlockData.HoldingThread);
+
+ if (syncBlockData.HoldingThread == ~0ul)
+ {
+ ExtOut(" orphaned ");
+ }
+ else if (syncBlockData.HoldingThread != NULL)
+ {
+ DacpThreadData Thread;
+ if ((Status = Thread.Request(g_sos, syncBlockData.HoldingThread)) != S_OK)
+ {
+ ExtOut("Failed to request Thread at %p\n", syncBlockData.HoldingThread);
+ return Status;
+ }
+
+ DMLOut(DMLThreadID(Thread.osThreadId));
+ ULONG id;
+ if (g_ExtSystem->GetThreadIdBySystemId(Thread.osThreadId, &id) == S_OK)
+ {
+ ExtOut("%4d ", id);
+ }
+ else
+ {
+ ExtOut(" XXX ");
+ }
+ }
+ else
+ {
+ ExtOut(" none ");
+ }
+
+ if (syncBlockData.bFree)
+ {
+ ExtOut(" %8d", 0); // TODO: do we need to print the free synctable list?
+ }
+ else
+ {
+ sos::Object obj = TO_TADDR(syncBlockData.Object);
+ DMLOut(" %s %S", DMLObject(syncBlockData.Object), obj.GetTypeName());
+ }
+ }
+ }
+
+ if (syncBlockData.bFree)
+ {
+ freeCount ++;
+ if (bPrint) {
+ ExtOut(" Free");
+ }
+ }
+ else
+ {
+#ifdef FEATURE_COMINTEROP
+ if (syncBlockData.COMFlags) {
+ switch (syncBlockData.COMFlags) {
+ case SYNCBLOCKDATA_COMFLAGS_CCW:
+ CCWCount ++;
+ break;
+ case SYNCBLOCKDATA_COMFLAGS_RCW:
+ RCWCount ++;
+ break;
+ case SYNCBLOCKDATA_COMFLAGS_CF:
+ CFCount ++;
+ break;
+ }
+ }
+#endif // FEATURE_COMINTEROP
+ }
+
+ if (syncBlockData.MonitorHeld > 1)
+ {
+ // TODO: implement this
+ /*
+ ExtOut(" ");
+ DWORD_PTR pHead = (DWORD_PTR)vSyncBlock.m_Link.m_pNext;
+ DWORD_PTR pNext = pHead;
+ Thread vThread;
+
+ while (1)
+ {
+ if (IsInterrupt())
+ return Status;
+ DWORD_PTR pWaitEventLink = pNext - offsetLinkSB;
+ WaitEventLink vWaitEventLink;
+ vWaitEventLink.Fill(pWaitEventLink);
+ if (!CallStatus) {
+ break;
+ }
+ DWORD_PTR dwAddr = (DWORD_PTR)vWaitEventLink.m_Thread;
+ ExtOut("%x ", dwAddr);
+ vThread.Fill (dwAddr);
+ if (!CallStatus) {
+ break;
+ }
+ if (bPrint)
+ DMLOut("%s,", DMLThreadID(vThread.m_OSThreadId));
+ pNext = (DWORD_PTR)vWaitEventLink.m_LinkSB.m_pNext;
+ if (pNext == 0)
+ break;
+ }
+ */
+ }
+
+ if (bPrint)
+ ExtOut("\n");
+ }
+
+ ExtOut("-----------------------------\n");
+ ExtOut("Total %d\n", dwCount);
+ ExtOut("CCW %d\n", CCWCount);
+ ExtOut("RCW %d\n", RCWCount);
+ ExtOut("ComClassFactory %d\n", CFCount);
+ ExtOut("Free %d\n", freeCount);
+
+ return Status;
+}
+
+#ifdef FEATURE_COMINTEROP
+struct VisitRcwArgs
+{
+ BOOL bDetail;
+ UINT MTACount;
+ UINT STACount;
+ ULONG FTMCount;
+};
+
+void VisitRcw(CLRDATA_ADDRESS RCW,CLRDATA_ADDRESS Context,CLRDATA_ADDRESS Thread, BOOL bIsFreeThreaded, LPVOID token)
+{
+ VisitRcwArgs *pArgs = (VisitRcwArgs *) token;
+
+ if (pArgs->bDetail)
+ {
+ if (pArgs->MTACount == 0 && pArgs->STACount == 0 && pArgs->FTMCount == 0)
+ {
+ // First time, print a header
+ ExtOut("RuntimeCallableWrappers (RCW) to be cleaned:\n");
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s Apartment\n",
+ "RCW", "CONTEXT", "THREAD");
+ }
+ LPCSTR szThreadApartment;
+ if (bIsFreeThreaded)
+ {
+ szThreadApartment = "(FreeThreaded)";
+ pArgs->FTMCount++;
+ }
+ else if (Thread == NULL)
+ {
+ szThreadApartment = "(MTA)";
+ pArgs->MTACount++;
+ }
+ else
+ {
+ szThreadApartment = "(STA)";
+ pArgs->STACount++;
+ }
+
+ ExtOut("%" POINTERSIZE "p %" POINTERSIZE "p %" POINTERSIZE "p %9s\n",
+ SOS_PTR(RCW),
+ SOS_PTR(Context),
+ SOS_PTR(Thread),
+ szThreadApartment);
+ }
+}
+
+DECLARE_API(RCWCleanupList)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ DWORD_PTR p_CleanupList = GetExpression(args);
+
+ VisitRcwArgs travArgs;
+ ZeroMemory(&travArgs,sizeof(VisitRcwArgs));
+ travArgs.bDetail = TRUE;
+
+ // We need to detect when !RCWCleanupList is called with an expression which evaluates to 0
+ // (to print out an Invalid parameter message), but at the same time we need to allow an
+ // empty argument list which would result in p_CleanupList equaling 0.
+ if (p_CleanupList || strlen(args) == 0)
+ {
+ HRESULT hr = g_sos->TraverseRCWCleanupList(p_CleanupList, (VISITRCWFORCLEANUP)VisitRcw, &travArgs);
+
+ if (SUCCEEDED(hr))
+ {
+ ExtOut("Free-Threaded Interfaces to be released: %d\n", travArgs.FTMCount);
+ ExtOut("MTA Interfaces to be released: %d\n", travArgs.MTACount);
+ ExtOut("STA Interfaces to be released: %d\n", travArgs.STACount);
+ }
+ else
+ {
+ ExtOut("An error occurred while traversing the cleanup list.\n");
+ }
+ }
+ else
+ {
+ ExtOut("Invalid parameter %s\n", args);
+ }
+
+ return Status;
+}
+#endif // FEATURE_COMINTEROP
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the contents of the finalizer *
+* queue. *
+* *
+\**********************************************************************/
+DECLARE_API(FinalizeQueue)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ BOOL bDetail = FALSE;
+ BOOL bAllReady = FALSE;
+ BOOL bShort = FALSE;
+ BOOL dml = FALSE;
+ TADDR taddrMT = 0;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-detail", &bDetail, COBOOL, FALSE},
+ {"-allReady", &bAllReady, COBOOL, FALSE},
+ {"-short", &bShort, COBOOL, FALSE},
+ {"/d", &dml, COBOOL, FALSE},
+ {"-mt", &taddrMT, COHEX, TRUE},
+ };
+
+ if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ if (!bShort)
+ {
+ DacpSyncBlockCleanupData dsbcd;
+ CLRDATA_ADDRESS sbCurrent = NULL;
+ ULONG cleanCount = 0;
+ while ((dsbcd.Request(g_sos,sbCurrent) == S_OK) && dsbcd.SyncBlockPointer)
+ {
+ if (bDetail)
+ {
+ if (cleanCount == 0) // print first time only
+ {
+ ExtOut("SyncBlocks to be cleaned by the finalizer thread:\n");
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
+ "SyncBlock", "RCW", "CCW", "ComClassFactory");
+ }
+
+ ExtOut("%" POINTERSIZE "p %" POINTERSIZE "p %" POINTERSIZE "p %" POINTERSIZE "p\n",
+ (ULONG64) dsbcd.SyncBlockPointer,
+ (ULONG64) dsbcd.blockRCW,
+ (ULONG64) dsbcd.blockCCW,
+ (ULONG64) dsbcd.blockClassFactory);
+ }
+
+ cleanCount++;
+ sbCurrent = dsbcd.nextSyncBlock;
+ if (sbCurrent == NULL)
+ {
+ break;
+ }
+ }
+
+ ExtOut("SyncBlocks to be cleaned up: %d\n", cleanCount);
+
+#ifdef FEATURE_COMINTEROP
+ VisitRcwArgs travArgs;
+ ZeroMemory(&travArgs,sizeof(VisitRcwArgs));
+ travArgs.bDetail = bDetail;
+ g_sos->TraverseRCWCleanupList(0, (VISITRCWFORCLEANUP) VisitRcw, &travArgs);
+ ExtOut("Free-Threaded Interfaces to be released: %d\n", travArgs.FTMCount);
+ ExtOut("MTA Interfaces to be released: %d\n", travArgs.MTACount);
+ ExtOut("STA Interfaces to be released: %d\n", travArgs.STACount);
+#endif // FEATURE_COMINTEROP
+
+// noRCW:
+ ExtOut("----------------------------------\n");
+ }
+
+ // GC Heap
+ DWORD dwNHeaps = GetGcHeapCount();
+
+ HeapStat hpStat;
+
+ if (!IsServerBuild())
+ {
+ DacpGcHeapDetails heapDetails;
+ if (heapDetails.Request(g_sos) != S_OK)
+ {
+ ExtOut("Error requesting details\n");
+ return Status;
+ }
+
+ GatherOneHeapFinalization(heapDetails, &hpStat, bAllReady, bShort);
+ }
+ else
+ {
+ DWORD dwAllocSize;
+ if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
+ {
+ ExtOut("Failed to get GCHeaps: integer overflow\n");
+ return Status;
+ }
+
+ CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
+ if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
+ {
+ ExtOut("Failed to get GCHeaps\n");
+ return Status;
+ }
+
+ for (DWORD n = 0; n < dwNHeaps; n ++)
+ {
+ DacpGcHeapDetails heapDetails;
+ if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
+ {
+ ExtOut("Error requesting details\n");
+ return Status;
+ }
+
+ ExtOut("------------------------------\n");
+ ExtOut("Heap %d\n", n);
+ GatherOneHeapFinalization(heapDetails, &hpStat, bAllReady, bShort);
+ }
+ }
+
+ if (!bShort)
+ {
+ if (bAllReady)
+ {
+ PrintGCStat(&hpStat, "Statistics for all finalizable objects that are no longer rooted:\n");
+ }
+ else
+ {
+ PrintGCStat(&hpStat, "Statistics for all finalizable objects (including all objects ready for finalization):\n");
+ }
+ }
+
+ return Status;
+}
+
+#endif // FEATURE_PAL
+
+enum {
+ // These are the values set in m_dwTransientFlags.
+ // Note that none of these flags survive a prejit save/restore.
+
+ M_CRST_NOTINITIALIZED = 0x00000001, // Used to prevent destruction of garbage m_crst
+ M_LOOKUPCRST_NOTINITIALIZED = 0x00000002,
+
+ SUPPORTS_UPDATEABLE_METHODS = 0x00000020,
+ CLASSES_FREED = 0x00000040,
+ HAS_PHONY_IL_RVAS = 0x00000080,
+ IS_EDIT_AND_CONTINUE = 0x00000200,
+};
+
+void ModuleMapTraverse(UINT index, CLRDATA_ADDRESS methodTable, LPVOID token)
+{
+ ULONG32 rid = (ULONG32)(size_t)token;
+ NameForMT_s(TO_TADDR(methodTable), g_mdName, mdNameLen);
+
+ DMLOut("%s 0x%08x %S\n", DMLMethodTable(methodTable), (ULONG32)TokenFromRid(rid, index), g_mdName);
+}
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the contents of a Module *
+* for a given address *
+* *
+\**********************************************************************/
+DECLARE_API(DumpModule)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+
+ DWORD_PTR p_ModuleAddr = NULL;
+ BOOL bMethodTables = FALSE;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-mt", &bMethodTables, COBOOL, FALSE},
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE}
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&p_ModuleAddr, COHEX}
+ };
+
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+ if (nArg != 1)
+ {
+ ExtOut("Usage: DumpModule [-mt] <Module Address>\n");
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ DacpModuleData module;
+ if ((Status=module.Request(g_sos, TO_CDADDR(p_ModuleAddr)))!=S_OK)
+ {
+ ExtOut("Fail to fill Module %p\n", SOS_PTR(p_ModuleAddr));
+ return Status;
+ }
+
+ WCHAR FileName[MAX_LONGPATH];
+ FileNameForModule (&module, FileName);
+ ExtOut("Name: %S\n", FileName[0] ? FileName : W("Unknown Module"));
+
+ ExtOut("Attributes: ");
+ if (module.bIsPEFile)
+ ExtOut("PEFile ");
+ if (module.bIsReflection)
+ ExtOut("Reflection ");
+ if (module.dwTransientFlags & SUPPORTS_UPDATEABLE_METHODS)
+ ExtOut("SupportsUpdateableMethods");
+ ExtOut("\n");
+
+ DMLOut("Assembly: %s\n", DMLAssembly(module.Assembly));
+
+ ExtOut("LoaderHeap: %p\n", SOS_PTR(module.pLookupTableHeap));
+ ExtOut("TypeDefToMethodTableMap: %p\n", SOS_PTR(module.TypeDefToMethodTableMap));
+ ExtOut("TypeRefToMethodTableMap: %p\n", SOS_PTR(module.TypeRefToMethodTableMap));
+ ExtOut("MethodDefToDescMap: %p\n", SOS_PTR(module.MethodDefToDescMap));
+ ExtOut("FieldDefToDescMap: %p\n", SOS_PTR(module.FieldDefToDescMap));
+ ExtOut("MemberRefToDescMap: %p\n", SOS_PTR(module.MemberRefToDescMap));
+ ExtOut("FileReferencesMap: %p\n", SOS_PTR(module.FileReferencesMap));
+ ExtOut("AssemblyReferencesMap: %p\n", SOS_PTR(module.ManifestModuleReferencesMap));
+
+ if (module.ilBase && module.metadataStart)
+ ExtOut("MetaData start address: %p (%d bytes)\n", SOS_PTR(module.metadataStart), module.metadataSize);
+
+ if (bMethodTables)
+ {
+ ExtOut("\nTypes defined in this module\n\n");
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %s\n", "MT", "TypeDef", "Name");
+
+ ExtOut("------------------------------------------------------------------------------\n");
+ g_sos->TraverseModuleMap(TYPEDEFTOMETHODTABLE, TO_CDADDR(p_ModuleAddr), ModuleMapTraverse, (LPVOID)mdTypeDefNil);
+
+ ExtOut("\nTypes referenced in this module\n\n");
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %s\n", "MT", "TypeRef", "Name");
+
+ ExtOut("------------------------------------------------------------------------------\n");
+ g_sos->TraverseModuleMap(TYPEREFTOMETHODTABLE, TO_CDADDR(p_ModuleAddr), ModuleMapTraverse, (LPVOID)mdTypeDefNil);
+ }
+
+ return Status;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the contents of a Domain *
+* for a given address *
+* *
+\**********************************************************************/
+DECLARE_API(DumpDomain)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ DWORD_PTR p_DomainAddr = 0;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE},
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&p_DomainAddr, COHEX},
+ };
+ size_t nArg;
+
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+
+ DacpAppDomainStoreData adsData;
+ if ((Status=adsData.Request(g_sos))!=S_OK)
+ {
+ ExtOut("Unable to get AppDomain information\n");
+ return Status;
+ }
+
+ if (p_DomainAddr)
+ {
+ DacpAppDomainData appDomain1;
+ if ((Status=appDomain1.Request(g_sos, TO_CDADDR(p_DomainAddr)))!=S_OK)
+ {
+ ExtOut("Fail to fill AppDomain\n");
+ return Status;
+ }
+
+ ExtOut("--------------------------------------\n");
+
+ if (p_DomainAddr == adsData.sharedDomain)
+ {
+ DMLOut("Shared Domain: %s\n", DMLDomain(adsData.sharedDomain));
+ }
+ else if (p_DomainAddr == adsData.systemDomain)
+ {
+ DMLOut("System Domain: %s\n", DMLDomain(adsData.systemDomain));
+ }
+ else
+ {
+ DMLOut("Domain %d:%s %s\n", appDomain1.dwId, (appDomain1.dwId >= 10) ? "" : " ", DMLDomain(p_DomainAddr));
+ }
+
+ DomainInfo(&appDomain1);
+ return Status;
+ }
+
+ ExtOut("--------------------------------------\n");
+ DMLOut("System Domain: %s\n", DMLDomain(adsData.systemDomain));
+ DacpAppDomainData appDomain;
+ if ((Status=appDomain.Request(g_sos,adsData.systemDomain))!=S_OK)
+ {
+ ExtOut("Unable to get system domain info.\n");
+ return Status;
+ }
+ DomainInfo(&appDomain);
+
+ ExtOut("--------------------------------------\n");
+ DMLOut("Shared Domain: %s\n", DMLDomain(adsData.sharedDomain));
+ if ((Status=appDomain.Request(g_sos, adsData.sharedDomain))!=S_OK)
+ {
+ ExtOut("Unable to get shared domain info\n");
+ return Status;
+ }
+ DomainInfo(&appDomain);
+
+ ArrayHolder<CLRDATA_ADDRESS> pArray = new NOTHROW CLRDATA_ADDRESS[adsData.DomainCount];
+ if (pArray==NULL)
+ {
+ ReportOOM();
+ return Status;
+ }
+
+ if ((Status=g_sos->GetAppDomainList(adsData.DomainCount, pArray, NULL))!=S_OK)
+ {
+ ExtOut("Unable to get array of AppDomains\n");
+ return Status;
+ }
+
+ for (int n=0;n<adsData.DomainCount;n++)
+ {
+ if (IsInterrupt())
+ break;
+
+ if ((Status=appDomain.Request(g_sos, pArray[n])) != S_OK)
+ {
+ ExtOut("Failed to get appdomain %p, error %lx\n", SOS_PTR(pArray[n]), Status);
+ return Status;
+ }
+
+ ExtOut("--------------------------------------\n");
+ DMLOut("Domain %d:%s %s\n", appDomain.dwId, (appDomain.dwId >= 10) ? "" : " ", DMLDomain(pArray[n]));
+ DomainInfo(&appDomain);
+ }
+
+ return Status;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the contents of a Assembly *
+* for a given address *
+* *
+\**********************************************************************/
+DECLARE_API(DumpAssembly)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ DWORD_PTR p_AssemblyAddr = 0;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE},
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&p_AssemblyAddr, COHEX},
+ };
+ size_t nArg;
+
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+
+ if (p_AssemblyAddr == 0)
+ {
+ ExtOut("Invalid Assembly %s\n", args);
+ return Status;
+ }
+
+ DacpAssemblyData Assembly;
+ if ((Status=Assembly.Request(g_sos, TO_CDADDR(p_AssemblyAddr)))!=S_OK)
+ {
+ ExtOut("Fail to fill Assembly\n");
+ return Status;
+ }
+ DMLOut("Parent Domain: %s\n", DMLDomain(Assembly.ParentDomain));
+ if (g_sos->GetAssemblyName(TO_CDADDR(p_AssemblyAddr), mdNameLen, g_mdName, NULL)==S_OK)
+ ExtOut("Name: %S\n", g_mdName);
+ else
+ ExtOut("Name: Unknown\n");
+
+ AssemblyInfo(&Assembly);
+ return Status;
+}
+
+
+String GetHostingCapabilities(DWORD hostConfig)
+{
+ String result;
+
+ bool bAnythingPrinted = false;
+
+#define CHK_AND_PRINT(hType,hStr) \
+ if (hostConfig & (hType)) { \
+ if (bAnythingPrinted) result += ", "; \
+ result += hStr; \
+ bAnythingPrinted = true; \
+ }
+
+ CHK_AND_PRINT(CLRMEMORYHOSTED, "Memory");
+ CHK_AND_PRINT(CLRTASKHOSTED, "Task");
+ CHK_AND_PRINT(CLRSYNCHOSTED, "Sync");
+ CHK_AND_PRINT(CLRTHREADPOOLHOSTED, "Threadpool");
+ CHK_AND_PRINT(CLRIOCOMPLETIONHOSTED, "IOCompletion");
+ CHK_AND_PRINT(CLRASSEMBLYHOSTED, "Assembly");
+ CHK_AND_PRINT(CLRGCHOSTED, "GC");
+ CHK_AND_PRINT(CLRSECURITYHOSTED, "Security");
+
+#undef CHK_AND_PRINT
+
+ return result;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the managed threads *
+* *
+\**********************************************************************/
+HRESULT PrintThreadsFromThreadStore(BOOL bMiniDump, BOOL bPrintLiveThreadsOnly)
+{
+ HRESULT Status;
+
+ DacpThreadStoreData ThreadStore;
+ if ((Status = ThreadStore.Request(g_sos)) != S_OK)
+ {
+ Print("Failed to request ThreadStore\n");
+ return Status;
+ }
+
+ TableOutput table(2, 17);
+
+ table.WriteRow("ThreadCount:", Decimal(ThreadStore.threadCount));
+ table.WriteRow("UnstartedThread:", Decimal(ThreadStore.unstartedThreadCount));
+ table.WriteRow("BackgroundThread:", Decimal(ThreadStore.backgroundThreadCount));
+ table.WriteRow("PendingThread:", Decimal(ThreadStore.pendingThreadCount));
+ table.WriteRow("DeadThread:", Decimal(ThreadStore.deadThreadCount));
+
+ if (ThreadStore.fHostConfig & ~CLRHOSTED)
+ {
+ String hosting = "yes";
+
+ hosting += " (";
+ hosting += GetHostingCapabilities(ThreadStore.fHostConfig);
+ hosting += ")";
+
+ table.WriteRow("Hosted Runtime:", hosting);
+ }
+ else
+ {
+ table.WriteRow("Hosted Runtime:", "no");
+ }
+
+ const bool hosted = (ThreadStore.fHostConfig & CLRTASKHOSTED) != 0;
+ table.ReInit(hosted ? 12 : 11, POINTERSIZE_HEX);
+ table.SetWidths(10, 4, 4, 4, _max(9, POINTERSIZE_HEX),
+ 8, 11, 1+POINTERSIZE_HEX*2, POINTERSIZE_HEX,
+ 5, 3, POINTERSIZE_HEX);
+
+ table.SetColAlignment(0, AlignRight);
+ table.SetColAlignment(1, AlignRight);
+ table.SetColAlignment(2, AlignRight);
+ table.SetColAlignment(4, AlignRight);
+
+ table.WriteColumn(8, "Lock");
+ table.WriteRow("", "ID", "OSID", "ThreadOBJ", "State", "GC Mode", "GC Alloc Context", "Domain", "Count", "Apt");
+
+ if (hosted)
+ table.WriteColumn("Fiber");
+
+ table.WriteColumn("Exception");
+
+ DacpThreadData Thread;
+ CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
+ while (CurThread)
+ {
+ if (IsInterrupt())
+ break;
+
+ if ((Status = Thread.Request(g_sos, CurThread)) != S_OK)
+ {
+ PrintLn("Failed to request Thread at ", Pointer(CurThread));
+ return Status;
+ }
+
+ BOOL bSwitchedOutFiber = Thread.osThreadId == SWITCHED_OUT_FIBER_OSID;
+ if (!IsKernelDebugger())
+ {
+ ULONG id = 0;
+
+ if (bSwitchedOutFiber)
+ {
+ table.WriteColumn(0, "<<<< ");
+ }
+ else if (g_ExtSystem->GetThreadIdBySystemId(Thread.osThreadId, &id) == S_OK)
+ {
+ table.WriteColumn(0, Decimal(id));
+ }
+ else if (bPrintLiveThreadsOnly)
+ {
+ CurThread = Thread.nextThread;
+ continue;
+ }
+ else
+ {
+ table.WriteColumn(0, "XXXX ");
+ }
+ }
+
+ table.WriteColumn(1, Decimal(Thread.corThreadId));
+ table.WriteColumn(2, ThreadID(bSwitchedOutFiber ? 0 : Thread.osThreadId));
+ table.WriteColumn(3, Pointer(CurThread));
+ table.WriteColumn(4, ThreadState(Thread.state));
+ table.WriteColumn(5, Thread.preemptiveGCDisabled == 1 ? "Cooperative" : "Preemptive");
+ table.WriteColumnFormat(6, "%p:%p", Thread.allocContextPtr, Thread.allocContextLimit);
+
+ if (Thread.domain)
+ {
+ table.WriteColumn(7, AppDomainPtr(Thread.domain));
+ }
+ else
+ {
+ CLRDATA_ADDRESS domain = 0;
+ if (FAILED(g_sos->GetDomainFromContext(Thread.context, &domain)))
+ table.WriteColumn(7, "<error>");
+ else
+ table.WriteColumn(7, AppDomainPtr(domain));
+ }
+
+ table.WriteColumn(8, Decimal(Thread.lockCount));
+
+ // Apartment state
+#ifndef FEATURE_PAL
+ DWORD_PTR OleTlsDataAddr;
+ if (!bSwitchedOutFiber
+ && SafeReadMemory(Thread.teb + offsetof(TEB, ReservedForOle),
+ &OleTlsDataAddr,
+ sizeof(OleTlsDataAddr), NULL) && OleTlsDataAddr != 0)
+ {
+ DWORD AptState;
+ if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwFlags),
+ &AptState,
+ sizeof(AptState), NULL))
+ {
+ if (AptState & OLETLS_APARTMENTTHREADED)
+ table.WriteColumn(9, "STA");
+ else if (AptState & OLETLS_MULTITHREADED)
+ table.WriteColumn(9, "MTA");
+ else if (AptState & OLETLS_INNEUTRALAPT)
+ table.WriteColumn(9, "NTA");
+ else
+ table.WriteColumn(9, "Ukn");
+ }
+ else
+ {
+ table.WriteColumn(9, "Ukn");
+ }
+ }
+ else
+#endif // FEATURE_PAL
+ table.WriteColumn(9, "Ukn");
+
+ if (hosted)
+ table.WriteColumn(10, Thread.fiberData);
+
+ WString lastCol;
+ if (CurThread == ThreadStore.finalizerThread)
+ lastCol += W("(Finalizer) ");
+ if (CurThread == ThreadStore.gcThread)
+ lastCol += W("(GC) ");
+
+ const int TS_TPWorkerThread = 0x01000000; // is this a threadpool worker thread?
+ const int TS_CompletionPortThread = 0x08000000; // is this is a completion port thread?
+
+ if (Thread.state & TS_TPWorkerThread)
+ lastCol += W("(Threadpool Worker) ");
+ else if (Thread.state & TS_CompletionPortThread)
+ lastCol += W("(Threadpool Completion Port) ");
+
+
+ TADDR taLTOH;
+ if (Thread.lastThrownObjectHandle && SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle),
+ &taLTOH, sizeof(taLTOH), NULL) && taLTOH)
+ {
+ TADDR taMT;
+ if (SafeReadMemory(taLTOH, &taMT, sizeof(taMT), NULL))
+ {
+ if (NameForMT_s(taMT, g_mdName, mdNameLen))
+ lastCol += WString(g_mdName) + W(" ") + ExceptionPtr(taLTOH);
+ else
+ lastCol += WString(W("<Invalid Object> (")) + Pointer(taLTOH) + W(")");
+
+ // Print something if there are nested exceptions on the thread
+ if (Thread.firstNestedException)
+ lastCol += W(" (nested exceptions)");
+ }
+ }
+
+ table.WriteColumn(lastCol);
+ CurThread = Thread.nextThread;
+ }
+
+ return Status;
+}
+
+#ifndef FEATURE_PAL
+HRESULT PrintSpecialThreads()
+{
+ Print("\n");
+
+ DWORD dwCLRTLSDataIndex = 0;
+ HRESULT Status = g_sos->GetTLSIndex(&dwCLRTLSDataIndex);
+
+ if (!SUCCEEDED (Status))
+ {
+ Print("Failed to retrieve Tls Data index\n");
+ return Status;
+ }
+
+
+ ULONG ulOriginalThreadID = 0;
+ Status = g_ExtSystem->GetCurrentThreadId (&ulOriginalThreadID);
+ if (!SUCCEEDED (Status))
+ {
+ Print("Failed to require current Thread ID\n");
+ return Status;
+ }
+
+ ULONG ulTotalThreads = 0;
+ Status = g_ExtSystem->GetNumberThreads (&ulTotalThreads);
+ if (!SUCCEEDED (Status))
+ {
+ Print("Failed to require total thread number\n");
+ return Status;
+ }
+
+ TableOutput table(3, 4, AlignRight, 5);
+ table.WriteRow("", "OSID", "Special thread type");
+
+ for (ULONG ulThread = 0; ulThread < ulTotalThreads; ulThread++)
+ {
+ ULONG Id = 0;
+ ULONG SysId = 0;
+ HRESULT threadStatus = g_ExtSystem->GetThreadIdsByIndex(ulThread, 1, &Id, &SysId);
+ if (!SUCCEEDED (threadStatus))
+ {
+ PrintLn("Failed to get thread ID for thread ", Decimal(ulThread));
+ continue;
+ }
+
+ threadStatus = g_ExtSystem->SetCurrentThreadId(Id);
+ if (!SUCCEEDED (threadStatus))
+ {
+ PrintLn("Failed to switch to thread ", ThreadID(SysId));
+ continue;
+ }
+
+ CLRDATA_ADDRESS cdaTeb = 0;
+ threadStatus = g_ExtSystem->GetCurrentThreadTeb(&cdaTeb);
+ if (!SUCCEEDED (threadStatus))
+ {
+ PrintLn("Failed to get Teb for Thread ", ThreadID(SysId));
+ continue;
+ }
+
+ TADDR CLRTLSDataAddr = 0;
+
+#ifdef FEATURE_IMPLICIT_TLS
+ TADDR tlsArrayAddr = NULL;
+ if (!SafeReadMemory (TO_TADDR(cdaTeb) + WINNT_OFFSETOF__TEB__ThreadLocalStoragePointer , &tlsArrayAddr, sizeof (void**), NULL))
+ {
+ PrintLn("Failed to get Tls expansion slots for thread ", ThreadID(SysId));
+ continue;
+ }
+
+ TADDR moduleTlsDataAddr = 0;
+
+ if (!SafeReadMemory (tlsArrayAddr + sizeof (void*) * dwCLRTLSDataIndex, &moduleTlsDataAddr, sizeof (void**), NULL))
+ {
+ PrintLn("Failed to get Tls expansion slots for thread ", ThreadID(SysId));
+ continue;
+ }
+
+ CLRTLSDataAddr = moduleTlsDataAddr + OFFSETOF__TLS__tls_EETlsData;
+#else
+ if (dwCLRTLSDataIndex < TLS_MINIMUM_AVAILABLE)
+ {
+ CLRTLSDataAddr = TO_TADDR(cdaTeb) + offsetof(TEB, TlsSlots) + sizeof (void*) * dwCLRTLSDataIndex;
+ }
+ else
+ {
+ //if TLS index is bigger than TLS_MINIMUM_AVAILABLE, the TLS slot lives in ExpansionSlots
+ TADDR TebExpsionAddr = NULL;
+ if (!SafeReadMemory (TO_TADDR(cdaTeb) + offsetof(TEB, TlsExpansionSlots) , &TebExpsionAddr, sizeof (void**), NULL))
+ {
+ PrintLn("Failed to get Tls expansion slots for thread ", ThreadID(SysId));
+ continue;
+ }
+
+ if (TebExpsionAddr == NULL)
+ {
+ continue;
+ }
+
+ CLRTLSDataAddr = TebExpsionAddr + sizeof (void*) * (dwCLRTLSDataIndex - TLS_MINIMUM_AVAILABLE);
+ }
+#endif // FEATURE_IMPLICIT_TLS
+
+ TADDR CLRTLSData = NULL;
+ if (!SafeReadMemory (CLRTLSDataAddr, &CLRTLSData, sizeof (TADDR), NULL))
+ {
+ PrintLn("Failed to get CLR Tls data for thread ", ThreadID(SysId));
+ continue;
+ }
+
+ if (CLRTLSData == NULL)
+ {
+ continue;
+ }
+
+ size_t ThreadType = 0;
+ if (!SafeReadMemory (CLRTLSData + sizeof (TADDR) * TlsIdx_ThreadType, &ThreadType, sizeof (size_t), NULL))
+ {
+ PrintLn("Failed to get thread type info not found for thread ", ThreadID(SysId));
+ continue;
+ }
+
+ if (ThreadType == 0)
+ {
+ continue;
+ }
+
+ table.WriteColumn(0, Decimal(Id));
+ table.WriteColumn(1, ThreadID(SysId));
+
+ String type;
+ if (ThreadType & ThreadType_GC)
+ {
+ type += "GC ";
+ }
+ if (ThreadType & ThreadType_Timer)
+ {
+ type += "Timer ";
+ }
+ if (ThreadType & ThreadType_Gate)
+ {
+ type += "Gate ";
+ }
+ if (ThreadType & ThreadType_DbgHelper)
+ {
+ type += "DbgHelper ";
+ }
+ if (ThreadType & ThreadType_Shutdown)
+ {
+ type += "Shutdown ";
+ }
+ if (ThreadType & ThreadType_DynamicSuspendEE)
+ {
+ type += "SuspendEE ";
+ }
+ if (ThreadType & ThreadType_Finalizer)
+ {
+ type += "Finalizer ";
+ }
+ if (ThreadType & ThreadType_ADUnloadHelper)
+ {
+ type += "ADUnloadHelper ";
+ }
+ if (ThreadType & ThreadType_ShutdownHelper)
+ {
+ type += "ShutdownHelper ";
+ }
+ if (ThreadType & ThreadType_Threadpool_IOCompletion)
+ {
+ type += "IOCompletion ";
+ }
+ if (ThreadType & ThreadType_Threadpool_Worker)
+ {
+ type += "ThreadpoolWorker ";
+ }
+ if (ThreadType & ThreadType_Wait)
+ {
+ type += "Wait ";
+ }
+ if (ThreadType & ThreadType_ProfAPI_Attach)
+ {
+ type += "ProfilingAPIAttach ";
+ }
+ if (ThreadType & ThreadType_ProfAPI_Detach)
+ {
+ type += "ProfilingAPIDetach ";
+ }
+
+ table.WriteColumn(2, type);
+ }
+
+ Status = g_ExtSystem->SetCurrentThreadId (ulOriginalThreadID);
+ if (!SUCCEEDED (Status))
+ {
+ ExtOut("Failed to switch to original thread\n");
+ return Status;
+ }
+
+ return Status;
+}
+#endif //FEATURE_PAL
+
+struct ThreadStateTable
+{
+ unsigned int State;
+ const char * Name;
+};
+static const struct ThreadStateTable ThreadStates[] =
+{
+ {0x1, "Thread Abort Requested"},
+ {0x2, "GC Suspend Pending"},
+ {0x4, "User Suspend Pending"},
+ {0x8, "Debug Suspend Pending"},
+ {0x10, "GC On Transitions"},
+ {0x20, "Legal to Join"},
+ {0x40, "Yield Requested"},
+ {0x80, "Hijacked by the GC"},
+ {0x100, "Blocking GC for Stack Overflow"},
+ {0x200, "Background"},
+ {0x400, "Unstarted"},
+ {0x800, "Dead"},
+ {0x1000, "CLR Owns"},
+ {0x2000, "CoInitialized"},
+ {0x4000, "In Single Threaded Apartment"},
+ {0x8000, "In Multi Threaded Apartment"},
+ {0x10000, "Reported Dead"},
+ {0x20000, "Fully initialized"},
+ {0x40000, "Task Reset"},
+ {0x80000, "Sync Suspended"},
+ {0x100000, "Debug Will Sync"},
+ {0x200000, "Stack Crawl Needed"},
+ {0x400000, "Suspend Unstarted"},
+ {0x800000, "Aborted"},
+ {0x1000000, "Thread Pool Worker Thread"},
+ {0x2000000, "Interruptible"},
+ {0x4000000, "Interrupted"},
+ {0x8000000, "Completion Port Thread"},
+ {0x10000000, "Abort Initiated"},
+ {0x20000000, "Finalized"},
+ {0x40000000, "Failed to Start"},
+ {0x80000000, "Detached"},
+};
+
+DECLARE_API(ThreadState)
+{
+ INIT_API_NODAC();
+
+ size_t state = GetExpression(args);
+ int count = 0;
+
+ if (state)
+ {
+
+ for (unsigned int i = 0; i < _countof(ThreadStates); ++i)
+ if (state & ThreadStates[i].State)
+ {
+ ExtOut(" %s\n", ThreadStates[i].Name);
+ count++;
+ }
+ }
+
+ // If we did not find any thread states, print out a message to let the user
+ // know that the function is working correctly.
+ if (count == 0)
+ ExtOut(" No thread states for '%s'\n", args);
+
+ return Status;
+}
+
+DECLARE_API(Threads)
+{
+ INIT_API();
+
+ BOOL bPrintSpecialThreads = FALSE;
+ BOOL bPrintLiveThreadsOnly = FALSE;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-special", &bPrintSpecialThreads, COBOOL, FALSE},
+ {"-live", &bPrintLiveThreadsOnly, COBOOL, FALSE},
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE},
+#endif
+ };
+ if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ {
+ return Status;
+ }
+
+ // We need to support minidumps for this command.
+ BOOL bMiniDump = IsMiniDumpFile();
+
+ if (bMiniDump && bPrintSpecialThreads)
+ {
+ Print("Special thread information is not available in mini dumps.\n");
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+
+ try
+ {
+ Status = PrintThreadsFromThreadStore(bMiniDump, bPrintLiveThreadsOnly);
+ if (!bMiniDump && bPrintSpecialThreads)
+ {
+#ifdef FEATURE_PAL
+ Print("\n-special not supported.\n");
+#else //FEATURE_PAL
+ HRESULT Status2 = PrintSpecialThreads();
+ if (!SUCCEEDED(Status2))
+ Status = Status2;
+#endif //FEATURE_PAL
+ }
+ }
+ catch (sos::Exception &e)
+ {
+ ExtOut("%s\n", e.what());
+ }
+
+ return Status;
+}
+
+#ifndef FEATURE_PAL
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the Watson Buckets. *
+* *
+\**********************************************************************/
+DECLARE_API(WatsonBuckets)
+{
+ INIT_API();
+
+ // We don't need to support minidumps for this command.
+ if (IsMiniDumpFile())
+ {
+ ExtOut("Not supported on mini dumps.\n");
+ }
+
+ // Get the current managed thread.
+ CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
+ DacpThreadData Thread;
+
+ if ((threadAddr == NULL) || ((Status = Thread.Request(g_sos, threadAddr)) != S_OK))
+ {
+ ExtOut("The current thread is unmanaged\n");
+ return Status;
+ }
+
+ // Get the definition of GenericModeBlock.
+#include <msodw.h>
+ GenericModeBlock gmb;
+
+ if ((Status = g_sos->GetClrWatsonBuckets(threadAddr, &gmb)) != S_OK)
+ {
+ ExtOut("Can't get Watson Buckets\n");
+ return Status;
+ }
+
+ ExtOut("Watson Bucket parameters:\n");
+ ExtOut("b1: %S\n", gmb.wzP1);
+ ExtOut("b2: %S\n", gmb.wzP2);
+ ExtOut("b3: %S\n", gmb.wzP3);
+ ExtOut("b4: %S\n", gmb.wzP4);
+ ExtOut("b5: %S\n", gmb.wzP5);
+ ExtOut("b6: %S\n", gmb.wzP6);
+ ExtOut("b7: %S\n", gmb.wzP7);
+ ExtOut("b8: %S\n", gmb.wzP8);
+ ExtOut("b9: %S\n", gmb.wzP9);
+
+ return Status;
+} // WatsonBuckets()
+#endif // FEATURE_PAL
+
+struct PendingBreakpoint
+{
+ WCHAR szModuleName[MAX_LONGPATH];
+ WCHAR szFunctionName[mdNameLen];
+ WCHAR szFilename[MAX_LONGPATH];
+ DWORD lineNumber;
+ TADDR pModule;
+ DWORD ilOffset;
+ mdMethodDef methodToken;
+ void SetModule(TADDR module)
+ {
+ pModule = module;
+ }
+
+ bool ModuleMatches(TADDR compare)
+ {
+ return (compare == pModule);
+ }
+
+ PendingBreakpoint *pNext;
+ PendingBreakpoint() : lineNumber(0), ilOffset(0), methodToken(0), pNext(NULL)
+ {
+ szModuleName[0] = L'\0';
+ szFunctionName[0] = L'\0';
+ szFilename[0] = L'\0';
+ }
+};
+
+void IssueDebuggerBPCommand ( CLRDATA_ADDRESS addr )
+{
+ const int MaxBPsCached = 1024;
+ static CLRDATA_ADDRESS alreadyPlacedBPs[MaxBPsCached];
+ static int curLimit = 0;
+
+ // on ARM the debugger requires breakpoint addresses to be sanitized
+ if (IsDbgTargetArm())
+#ifndef FEATURE_PAL
+ addr &= ~THUMB_CODE;
+#else
+ addr |= THUMB_CODE; // lldb expects thumb code bit set
+#endif
+
+ // if we overflowed our cache consider all new BPs unique...
+ BOOL bUnique = curLimit >= MaxBPsCached;
+ if (!bUnique)
+ {
+ bUnique = TRUE;
+ for (int i = 0; i < curLimit; ++i)
+ {
+ if (alreadyPlacedBPs[i] == addr)
+ {
+ bUnique = FALSE;
+ break;
+ }
+ }
+ }
+ if (bUnique)
+ {
+ char buffer[64]; // sufficient for "bp <pointersize>"
+ static WCHAR wszNameBuffer[1024]; // should be large enough
+
+ // get the MethodDesc name
+ CLRDATA_ADDRESS pMD;
+ if (g_sos->GetMethodDescPtrFromIP(addr, &pMD) != S_OK
+ || g_sos->GetMethodDescName(pMD, 1024, wszNameBuffer, NULL) != S_OK)
+ {
+ wcscpy_s(wszNameBuffer, _countof(wszNameBuffer), W("UNKNOWN"));
+ }
+
+#ifndef FEATURE_PAL
+ sprintf_s(buffer, _countof(buffer), "bp %p", (void*) (size_t) addr);
+#else
+ sprintf_s(buffer, _countof(buffer), "breakpoint set --address 0x%p", (void*) (size_t) addr);
+#endif
+ ExtOut("Setting breakpoint: %s [%S]\n", buffer, wszNameBuffer);
+ g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
+
+ if (curLimit < MaxBPsCached)
+ {
+ alreadyPlacedBPs[curLimit++] = addr;
+ }
+ }
+}
+
+class Breakpoints
+{
+ PendingBreakpoint* m_breakpoints;
+public:
+ Breakpoints()
+ {
+ m_breakpoints = NULL;
+ }
+ ~Breakpoints()
+ {
+ PendingBreakpoint *pCur = m_breakpoints;
+ while(pCur)
+ {
+ PendingBreakpoint *pNext = pCur->pNext;
+ delete pCur;
+ pCur = pNext;
+ }
+ m_breakpoints = NULL;
+ }
+
+ void Add(__in_z LPWSTR szModule, __in_z LPWSTR szName, TADDR mod, DWORD ilOffset)
+ {
+ if (!IsIn(szModule, szName, mod))
+ {
+ PendingBreakpoint *pNew = new PendingBreakpoint();
+ wcscpy_s(pNew->szModuleName, MAX_LONGPATH, szModule);
+ wcscpy_s(pNew->szFunctionName, mdNameLen, szName);
+ pNew->SetModule(mod);
+ pNew->ilOffset = ilOffset;
+ pNew->pNext = m_breakpoints;
+ m_breakpoints = pNew;
+ }
+ }
+
+ void Add(__in_z LPWSTR szModule, __in_z LPWSTR szName, mdMethodDef methodToken, TADDR mod, DWORD ilOffset)
+ {
+ if (!IsIn(methodToken, mod, ilOffset))
+ {
+ PendingBreakpoint *pNew = new PendingBreakpoint();
+ wcscpy_s(pNew->szModuleName, MAX_LONGPATH, szModule);
+ wcscpy_s(pNew->szFunctionName, mdNameLen, szName);
+ pNew->methodToken = methodToken;
+ pNew->SetModule(mod);
+ pNew->ilOffset = ilOffset;
+ pNew->pNext = m_breakpoints;
+ m_breakpoints = pNew;
+ }
+ }
+
+ void Add(__in_z LPWSTR szFilename, DWORD lineNumber, TADDR mod)
+ {
+ if (!IsIn(szFilename, lineNumber, mod))
+ {
+ PendingBreakpoint *pNew = new PendingBreakpoint();
+ wcscpy_s(pNew->szFilename, MAX_LONGPATH, szFilename);
+ pNew->lineNumber = lineNumber;
+ pNew->SetModule(mod);
+ pNew->pNext = m_breakpoints;
+ m_breakpoints = pNew;
+ }
+ }
+
+ void Add(__in_z LPWSTR szFilename, DWORD lineNumber, mdMethodDef methodToken, TADDR mod, DWORD ilOffset)
+ {
+ if (!IsIn(methodToken, mod, ilOffset))
+ {
+ PendingBreakpoint *pNew = new PendingBreakpoint();
+ wcscpy_s(pNew->szFilename, MAX_LONGPATH, szFilename);
+ pNew->lineNumber = lineNumber;
+ pNew->methodToken = methodToken;
+ pNew->SetModule(mod);
+ pNew->ilOffset = ilOffset;
+ pNew->pNext = m_breakpoints;
+ m_breakpoints = pNew;
+ }
+ }
+
+ //returns true if updates are still needed for this module, FALSE if all BPs are now bound
+ BOOL Update(TADDR mod, BOOL isNewModule)
+ {
+ BOOL bNeedUpdates = FALSE;
+ PendingBreakpoint *pCur = NULL;
+
+ if(isNewModule)
+ {
+ SymbolReader symbolReader;
+ SymbolReader* pSymReader = &symbolReader;
+ if(LoadSymbolsForModule(mod, &symbolReader) != S_OK)
+ pSymReader = NULL;
+
+ // Get tokens for any modules that match. If there was a change,
+ // update notifications.
+ pCur = m_breakpoints;
+ while(pCur)
+ {
+ PendingBreakpoint *pNext = pCur->pNext;
+ ResolvePendingNonModuleBoundBreakpoint(mod, pCur, pSymReader);
+ pCur = pNext;
+ }
+ }
+
+ pCur = m_breakpoints;
+ while(pCur)
+ {
+ PendingBreakpoint *pNext = pCur->pNext;
+ if (ResolvePendingBreakpoint(mod, pCur))
+ {
+ bNeedUpdates = TRUE;
+ }
+ pCur = pNext;
+ }
+ return bNeedUpdates;
+ }
+
+ void RemovePendingForModule(TADDR mod)
+ {
+ PendingBreakpoint *pCur = m_breakpoints;
+ while(pCur)
+ {
+ PendingBreakpoint *pNext = pCur->pNext;
+ if (pCur->ModuleMatches(mod))
+ {
+ // Delete the current node, and keep going
+ Delete(pCur);
+ }
+
+ pCur = pNext;
+ }
+ }
+
+ void ListBreakpoints()
+ {
+ PendingBreakpoint *pCur = m_breakpoints;
+ size_t iBreakpointIndex = 1;
+ ExtOut(SOSPrefix "bpmd pending breakpoint list\n Breakpoint index - Location, ModuleID, Method Token\n");
+ while(pCur)
+ {
+ //windbg likes to format %p as always being 64 bits
+ ULONG64 modulePtr = (ULONG64)pCur->pModule;
+
+ if(pCur->szModuleName[0] != L'\0')
+ ExtOut("%d - %ws!%ws+%d, 0x%p, 0x%08x\n", iBreakpointIndex, pCur->szModuleName, pCur->szFunctionName, pCur->ilOffset, modulePtr, pCur->methodToken);
+ else
+ ExtOut("%d - %ws:%d, 0x%p, 0x%08x\n", iBreakpointIndex, pCur->szFilename, pCur->lineNumber, modulePtr, pCur->methodToken);
+ iBreakpointIndex++;
+ pCur = pCur->pNext;
+ }
+ }
+
+#ifndef FEATURE_PAL
+ void SaveBreakpoints(FILE* pFile)
+ {
+ PendingBreakpoint *pCur = m_breakpoints;
+ while(pCur)
+ {
+ if(pCur->szModuleName[0] != L'\0')
+ fprintf_s(pFile, "!bpmd %ws %ws %d\n", pCur->szModuleName, pCur->szFunctionName, pCur->ilOffset);
+ else
+ fprintf_s(pFile, "!bpmd %ws:%d\n", pCur->szFilename, pCur->lineNumber);
+ pCur = pCur->pNext;
+ }
+ }
+#endif
+
+ void CleanupNotifications()
+ {
+#ifdef FEATURE_PAL
+ if (m_breakpoints == NULL)
+ {
+ g_ExtServices->ClearExceptionCallback();
+ }
+#endif
+ }
+
+ void ClearBreakpoint(size_t breakPointToClear)
+ {
+ PendingBreakpoint *pCur = m_breakpoints;
+ size_t iBreakpointIndex = 1;
+ while(pCur)
+ {
+ if (breakPointToClear == iBreakpointIndex)
+ {
+ ExtOut("%d - %ws, %ws, %p\n", iBreakpointIndex, pCur->szModuleName, pCur->szFunctionName, pCur->pModule);
+ ExtOut("Cleared\n");
+ Delete(pCur);
+ break;
+ }
+ iBreakpointIndex++;
+ pCur = pCur->pNext;
+ }
+
+ if (pCur == NULL)
+ {
+ ExtOut("Invalid pending breakpoint index.\n");
+ }
+ CleanupNotifications();
+ }
+
+ void ClearAllBreakpoints()
+ {
+ size_t iBreakpointIndex = 1;
+ for (PendingBreakpoint *pCur = m_breakpoints; pCur != NULL; )
+ {
+ PendingBreakpoint* pNext = pCur->pNext;
+ Delete(pCur);
+ iBreakpointIndex++;
+ pCur = pNext;
+ }
+ CleanupNotifications();
+
+ ExtOut("All pending breakpoints cleared.\n");
+ }
+
+ HRESULT LoadSymbolsForModule(TADDR mod, SymbolReader* pSymbolReader)
+ {
+ HRESULT Status = S_OK;
+ ToRelease<IXCLRDataModule> pModule;
+ IfFailRet(g_sos->GetModule(mod, &pModule));
+
+ ToRelease<IMetaDataImport> pMDImport = NULL;
+ IfFailRet(pModule->QueryInterface(IID_IMetaDataImport, (LPVOID *) &pMDImport));
+
+ IfFailRet(pSymbolReader->LoadSymbols(pMDImport, pModule));
+
+ return S_OK;
+ }
+
+ HRESULT ResolvePendingNonModuleBoundBreakpoint(__in_z WCHAR* pFilename, DWORD lineNumber, TADDR mod, SymbolReader* pSymbolReader)
+ {
+ HRESULT Status = S_OK;
+ if(pSymbolReader == NULL)
+ return S_FALSE; // no symbols, can't bind here
+
+ mdMethodDef methodDef;
+ ULONG32 ilOffset;
+ if(FAILED(Status = pSymbolReader->ResolveSequencePoint(pFilename, lineNumber, mod, &methodDef, &ilOffset)))
+ {
+ return S_FALSE; // not binding in a module is typical
+ }
+
+ Add(pFilename, lineNumber, methodDef, mod, ilOffset);
+ return Status;
+ }
+
+ HRESULT ResolvePendingNonModuleBoundBreakpoint(__in_z WCHAR* pModuleName, __in_z WCHAR* pMethodName, TADDR mod, DWORD ilOffset)
+ {
+ HRESULT Status = S_OK;
+ char szName[mdNameLen];
+ int numModule;
+
+ ToRelease<IXCLRDataModule> module;
+ IfFailRet(g_sos->GetModule(mod, &module));
+
+ WideCharToMultiByte(CP_ACP, 0, pModuleName, (int)(_wcslen(pModuleName) + 1), szName, mdNameLen, NULL, NULL);
+
+ ArrayHolder<DWORD_PTR> moduleList = ModuleFromName(szName, &numModule);
+ if (moduleList == NULL)
+ {
+ ExtOut("Failed to request module list.\n");
+ return E_FAIL;
+ }
+
+ for (int i = 0; i < numModule; i++)
+ {
+ // If any one entry in moduleList matches, then the current PendingBreakpoint
+ // is the right one.
+ if(moduleList[i] != TO_TADDR(mod))
+ continue;
+
+ CLRDATA_ENUM h;
+ if (module->StartEnumMethodDefinitionsByName(pMethodName, 0, &h) == S_OK)
+ {
+ IXCLRDataMethodDefinition *pMeth = NULL;
+ while (module->EnumMethodDefinitionByName(&h, &pMeth) == S_OK)
+ {
+ mdMethodDef methodToken;
+ ToRelease<IXCLRDataModule> pUnusedModule;
+ IfFailRet(pMeth->GetTokenAndScope(&methodToken, &pUnusedModule));
+
+ Add(pModuleName, pMethodName, methodToken, mod, ilOffset);
+ pMeth->Release();
+ }
+ module->EndEnumMethodDefinitionsByName(h);
+ }
+ }
+ return S_OK;
+ }
+
+ // Return TRUE if there might be more instances that will be JITTED later
+ static BOOL ResolveMethodInstances(IXCLRDataMethodDefinition *pMeth, DWORD ilOffset)
+ {
+ BOOL bFoundCode = FALSE;
+ BOOL bNeedDefer = FALSE;
+ CLRDATA_ENUM h1;
+
+ if (pMeth->StartEnumInstances (NULL, &h1) == S_OK)
+ {
+ IXCLRDataMethodInstance *inst = NULL;
+ while (pMeth->EnumInstance (&h1, &inst) == S_OK)
+ {
+ BOOL foundByIlOffset = FALSE;
+ ULONG32 rangesNeeded = 0;
+ if(inst->GetAddressRangesByILOffset(ilOffset, 0, &rangesNeeded, NULL) == S_OK)
+ {
+ ArrayHolder<CLRDATA_ADDRESS_RANGE> ranges = new NOTHROW CLRDATA_ADDRESS_RANGE[rangesNeeded];
+ if (ranges != NULL)
+ {
+ if (inst->GetAddressRangesByILOffset(ilOffset, rangesNeeded, NULL, ranges) == S_OK)
+ {
+ for (DWORD i = 0; i < rangesNeeded; i++)
+ {
+ IssueDebuggerBPCommand(ranges[i].startAddress);
+ bFoundCode = TRUE;
+ foundByIlOffset = TRUE;
+ }
+ }
+ }
+ }
+
+ if (!foundByIlOffset && ilOffset == 0)
+ {
+ CLRDATA_ADDRESS addr = 0;
+ if (inst->GetRepresentativeEntryAddress(&addr) == S_OK)
+ {
+ IssueDebuggerBPCommand(addr);
+ bFoundCode = TRUE;
+ }
+ }
+ }
+ pMeth->EndEnumInstances (h1);
+ }
+
+ // if this is a generic method we need to add a defered bp
+ BOOL bGeneric = FALSE;
+ pMeth->HasClassOrMethodInstantiation(&bGeneric);
+
+ bNeedDefer = !bFoundCode || bGeneric;
+ // This is down here because we only need to call SetCodeNofiication once.
+ if (bNeedDefer)
+ {
+ if (pMeth->SetCodeNotification (CLRDATA_METHNOTIFY_GENERATED) != S_OK)
+ {
+ bNeedDefer = FALSE;
+ ExtOut("Failed to set code notification\n");
+ }
+ }
+ return bNeedDefer;
+ }
+
+private:
+ BOOL IsIn(__in_z LPWSTR szModule, __in_z LPWSTR szName, TADDR mod)
+ {
+ PendingBreakpoint *pCur = m_breakpoints;
+ while(pCur)
+ {
+ if (pCur->ModuleMatches(mod) &&
+ _wcsicmp(pCur->szModuleName, szModule) == 0 &&
+ _wcscmp(pCur->szFunctionName, szName) == 0)
+ {
+ return TRUE;
+ }
+ pCur = pCur->pNext;
+ }
+ return FALSE;
+ }
+
+ BOOL IsIn(__in_z LPWSTR szFilename, DWORD lineNumber, TADDR mod)
+ {
+ PendingBreakpoint *pCur = m_breakpoints;
+ while(pCur)
+ {
+ if (pCur->ModuleMatches(mod) &&
+ _wcsicmp(pCur->szFilename, szFilename) == 0 &&
+ pCur->lineNumber == lineNumber)
+ {
+ return TRUE;
+ }
+ pCur = pCur->pNext;
+ }
+ return FALSE;
+ }
+
+ BOOL IsIn(mdMethodDef token, TADDR mod, DWORD ilOffset)
+ {
+ PendingBreakpoint *pCur = m_breakpoints;
+ while(pCur)
+ {
+ if (pCur->ModuleMatches(mod) &&
+ pCur->methodToken == token &&
+ pCur->ilOffset == ilOffset)
+ {
+ return TRUE;
+ }
+ pCur = pCur->pNext;
+ }
+ return FALSE;
+ }
+
+ void Delete(PendingBreakpoint *pDelete)
+ {
+ PendingBreakpoint *pCur = m_breakpoints;
+ PendingBreakpoint *pPrev = NULL;
+ while(pCur)
+ {
+ if (pCur == pDelete)
+ {
+ if (pPrev == NULL)
+ {
+ m_breakpoints = pCur->pNext;
+ }
+ else
+ {
+ pPrev->pNext = pCur->pNext;
+ }
+ delete pCur;
+ return;
+ }
+ pPrev = pCur;
+ pCur = pCur->pNext;
+ }
+ }
+
+
+
+ HRESULT ResolvePendingNonModuleBoundBreakpoint(TADDR mod, PendingBreakpoint *pCur, SymbolReader* pSymbolReader)
+ {
+ // This function only works with pending breakpoints that are not module bound.
+ if (pCur->pModule == NULL)
+ {
+ if(pCur->szModuleName[0] != L'\0')
+ {
+ return ResolvePendingNonModuleBoundBreakpoint(pCur->szModuleName, pCur->szFunctionName, mod, pCur->ilOffset);
+ }
+ else
+ {
+ return ResolvePendingNonModuleBoundBreakpoint(pCur->szFilename, pCur->lineNumber, mod, pSymbolReader);
+ }
+ }
+ else
+ {
+ return S_OK;
+ }
+ }
+
+ // Returns TRUE if further instances may be jitted, FALSE if all instances are now resolved
+ BOOL ResolvePendingBreakpoint(TADDR addr, PendingBreakpoint *pCur)
+ {
+ // Only go forward if the module matches the current PendingBreakpoint
+ if (!pCur->ModuleMatches(addr))
+ {
+ return FALSE;
+ }
+
+ ToRelease<IXCLRDataModule> mod;
+ if (FAILED(g_sos->GetModule(addr, &mod)))
+ {
+ return FALSE;
+ }
+
+ if(pCur->methodToken == 0)
+ {
+ return FALSE;
+ }
+
+ ToRelease<IXCLRDataMethodDefinition> pMeth = NULL;
+ mod->GetMethodDefinitionByToken(pCur->methodToken, &pMeth);
+
+ // We may not need the code notification. Maybe it was ngen'd and we
+ // already have the method?
+ // We can delete the current entry if ResolveMethodInstances() set all BPs
+ return ResolveMethodInstances(pMeth, pCur->ilOffset);
+ }
+};
+
+Breakpoints g_bpoints;
+
+// Controls whether optimizations are disabled on module load and whether NGEN can be used
+BOOL g_fAllowJitOptimization = TRUE;
+
+// Controls whether a one-shot breakpoint should be inserted the next time
+// execution is about to enter a catch clause
+BOOL g_stopOnNextCatch = FALSE;
+
+// According to the latest debuggers these callbacks will not get called
+// unless the user (or an extension, like SOS :-)) had previously enabled
+// clrn with "sxe clrn".
+class CNotification : public IXCLRDataExceptionNotification4
+{
+ static int s_condemnedGen;
+
+ int m_count;
+ int m_dbgStatus;
+public:
+ CNotification()
+ : m_count(0)
+ , m_dbgStatus(DEBUG_STATUS_NO_CHANGE)
+ {}
+
+ int GetDebugStatus()
+ {
+ return m_dbgStatus;
+ }
+
+ STDMETHODIMP QueryInterface (REFIID iid, void **ppvObject)
+ {
+ if (ppvObject == NULL)
+ return E_INVALIDARG;
+
+ if (IsEqualIID(iid, IID_IUnknown)
+ || IsEqualIID(iid, IID_IXCLRDataExceptionNotification)
+ || IsEqualIID(iid, IID_IXCLRDataExceptionNotification2)
+ || IsEqualIID(iid, IID_IXCLRDataExceptionNotification3)
+ || IsEqualIID(iid, IID_IXCLRDataExceptionNotification4))
+ {
+ *ppvObject = static_cast<IXCLRDataExceptionNotification4*>(this);
+ AddRef();
+ return S_OK;
+ }
+ else
+ return E_NOINTERFACE;
+
+ }
+
+ STDMETHODIMP_(ULONG) AddRef(void) { return ++m_count; }
+ STDMETHODIMP_(ULONG) Release(void)
+ {
+ m_count--;
+ if (m_count < 0)
+ {
+ m_count = 0;
+ }
+ return m_count;
+ }
+
+
+ /*
+ * New code was generated or discarded for a method.:
+ */
+ STDMETHODIMP OnCodeGenerated(IXCLRDataMethodInstance* method)
+ {
+ // Some method has been generated, make a breakpoint and remove it.
+ ULONG32 len = mdNameLen;
+ LPWSTR szModuleName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
+ if (method->GetName(0, mdNameLen, &len, g_mdName) == S_OK)
+ {
+ ToRelease<IXCLRDataModule> pMod;
+ HRESULT hr = method->GetTokenAndScope(NULL, &pMod);
+ if (SUCCEEDED(hr))
+ {
+ len = mdNameLen;
+ if (pMod->GetName(mdNameLen, &len, szModuleName) == S_OK)
+ {
+ ExtOut("JITTED %S!%S\n", szModuleName, g_mdName);
+
+ // Add breakpoint, perhaps delete pending breakpoint
+ DacpGetModuleAddress dgma;
+ if (SUCCEEDED(dgma.Request(pMod)))
+ {
+ g_bpoints.Update(TO_TADDR(dgma.ModulePtr), FALSE);
+ }
+ else
+ {
+ ExtOut("Failed to request module address.\n");
+ }
+ }
+ }
+ }
+
+ m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
+ return S_OK;
+ }
+
+ STDMETHODIMP OnCodeDiscarded(IXCLRDataMethodInstance* method)
+ {
+ return E_NOTIMPL;
+ }
+
+ /*
+ * The process or task reached the desired execution state.
+ */
+ STDMETHODIMP OnProcessExecution(ULONG32 state) { return E_NOTIMPL; }
+ STDMETHODIMP OnTaskExecution(IXCLRDataTask* task,
+ ULONG32 state) { return E_NOTIMPL; }
+
+ /*
+ * The given module was loaded or unloaded.
+ */
+ STDMETHODIMP OnModuleLoaded(IXCLRDataModule* mod)
+ {
+ DacpGetModuleAddress dgma;
+ if (SUCCEEDED(dgma.Request(mod)))
+ {
+ g_bpoints.Update(TO_TADDR(dgma.ModulePtr), TRUE);
+ }
+
+ if(!g_fAllowJitOptimization)
+ {
+ HRESULT hr;
+ ToRelease<IXCLRDataModule2> mod2;
+ if(FAILED(mod->QueryInterface(__uuidof(IXCLRDataModule2), (void**) &mod2)))
+ {
+ ExtOut("SOS: warning, optimizations for this module could not be suppressed because this CLR version doesn't support the functionality\n");
+ }
+ else if(FAILED(hr = mod2->SetJITCompilerFlags(CORDEBUG_JIT_DISABLE_OPTIMIZATION)))
+ {
+ if(hr == CORDBG_E_CANT_CHANGE_JIT_SETTING_FOR_ZAP_MODULE)
+ ExtOut("SOS: warning, optimizations for this module could not be surpressed because an optimized prejitted image was loaded\n");
+ else
+ ExtOut("SOS: warning, optimizations for this module could not be surpressed hr=0x%x\n", hr);
+ }
+ }
+
+ m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
+ return S_OK;
+ }
+
+ STDMETHODIMP OnModuleUnloaded(IXCLRDataModule* mod)
+ {
+ DacpGetModuleAddress dgma;
+ if (SUCCEEDED(dgma.Request(mod)))
+ {
+ g_bpoints.RemovePendingForModule(TO_TADDR(dgma.ModulePtr));
+ }
+
+ m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
+ return S_OK;
+ }
+
+ /*
+ * The given type was loaded or unloaded.
+ */
+ STDMETHODIMP OnTypeLoaded(IXCLRDataTypeInstance* typeInst)
+ { return E_NOTIMPL; }
+ STDMETHODIMP OnTypeUnloaded(IXCLRDataTypeInstance* typeInst)
+ { return E_NOTIMPL; }
+
+ STDMETHODIMP OnAppDomainLoaded(IXCLRDataAppDomain* domain)
+ { return E_NOTIMPL; }
+ STDMETHODIMP OnAppDomainUnloaded(IXCLRDataAppDomain* domain)
+ { return E_NOTIMPL; }
+ STDMETHODIMP OnException(IXCLRDataExceptionState* exception)
+ { return E_NOTIMPL; }
+
+ STDMETHODIMP OnGcEvent(GcEvtArgs gcEvtArgs)
+{
+ // by default don't stop on these notifications...
+ m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
+
+ IXCLRDataProcess2* idp2 = NULL;
+ if (SUCCEEDED(g_clrData->QueryInterface(IID_IXCLRDataProcess2, (void**) &idp2)))
+ {
+ if (gcEvtArgs.typ == GC_MARK_END)
+ {
+ // erase notification request
+ GcEvtArgs gea = { GC_MARK_END, { 0 } };
+ idp2->SetGcNotification(gea);
+
+ s_condemnedGen = bitidx(gcEvtArgs.condemnedGeneration);
+
+ ExtOut("CLR notification: GC - Performing a gen %d collection. Determined surviving objects...\n", s_condemnedGen);
+
+ // GC_MARK_END notification means: give the user a chance to examine the debuggee
+ m_dbgStatus = DEBUG_STATUS_BREAK;
+ }
+ }
+
+ return S_OK;
+ }
+
+ /*
+ * Catch is about to be entered
+ */
+ STDMETHODIMP ExceptionCatcherEnter(IXCLRDataMethodInstance* method, DWORD catcherNativeOffset)
+ {
+ if(g_stopOnNextCatch)
+ {
+ CLRDATA_ADDRESS startAddr;
+ if(method->GetRepresentativeEntryAddress(&startAddr) == S_OK)
+ {
+ CHAR buffer[100];
+#ifndef FEATURE_PAL
+ sprintf_s(buffer, _countof(buffer), "bp /1 %p", (void*) (size_t) (startAddr+catcherNativeOffset));
+#else
+ sprintf_s(buffer, _countof(buffer), "breakpoint set --one-shot --address 0x%p", (void*) (size_t) (startAddr+catcherNativeOffset));
+#endif
+ g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
+ }
+ g_stopOnNextCatch = FALSE;
+ }
+
+ m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
+ return S_OK;
+ }
+
+ static int GetCondemnedGen()
+ {
+ return s_condemnedGen;
+ }
+
+};
+
+int CNotification::s_condemnedGen = -1;
+
+BOOL CheckCLRNotificationEvent(DEBUG_LAST_EVENT_INFO_EXCEPTION* pdle)
+{
+ ISOSDacInterface4 *psos4 = NULL;
+ CLRDATA_ADDRESS arguments[3];
+ HRESULT Status;
+
+ if (SUCCEEDED(Status = g_sos->QueryInterface(__uuidof(ISOSDacInterface4), (void**) &psos4)))
+ {
+ int count = _countof(arguments);
+ int countNeeded = 0;
+
+ Status = psos4->GetClrNotification(arguments, count, &countNeeded);
+ psos4->Release();
+
+ if (SUCCEEDED(Status))
+ {
+ memset(&pdle->ExceptionRecord, 0, sizeof(pdle->ExceptionRecord));
+ pdle->FirstChance = TRUE;
+ pdle->ExceptionRecord.ExceptionCode = CLRDATA_NOTIFY_EXCEPTION;
+
+ _ASSERTE(count <= EXCEPTION_MAXIMUM_PARAMETERS);
+ for (int i = 0; i < count; i++)
+ {
+ pdle->ExceptionRecord.ExceptionInformation[i] = arguments[i];
+ }
+ // The rest of the ExceptionRecord isn't used by TranslateExceptionRecordToNotification
+ return TRUE;
+ }
+ // No pending exception notification
+ return FALSE;
+ }
+
+ // The new DAC based interface doesn't exists so ask the debugger for the last exception
+ // information. NOTE: this function doesn't work on xplat version when the coreclr symbols
+ // have been stripped.
+
+ ULONG Type, ProcessId, ThreadId;
+ ULONG ExtraInformationUsed;
+ Status = g_ExtControl->GetLastEventInformation(
+ &Type,
+ &ProcessId,
+ &ThreadId,
+ pdle,
+ sizeof(DEBUG_LAST_EVENT_INFO_EXCEPTION),
+ &ExtraInformationUsed,
+ NULL,
+ 0,
+ NULL);
+
+ if (Status != S_OK || Type != DEBUG_EVENT_EXCEPTION)
+ {
+ return FALSE;
+ }
+
+ if (!pdle->FirstChance || pdle->ExceptionRecord.ExceptionCode != CLRDATA_NOTIFY_EXCEPTION)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+HRESULT HandleCLRNotificationEvent()
+{
+ /*
+ * Did we get module load notification? If so, check if any in our pending list
+ * need to be registered for jit notification.
+ *
+ * Did we get a jit notification? If so, check if any can be removed and
+ * real breakpoints be set.
+ */
+ DEBUG_LAST_EVENT_INFO_EXCEPTION dle;
+ CNotification Notification;
+
+ if (!CheckCLRNotificationEvent(&dle))
+ {
+#ifndef FEATURE_PAL
+ ExtOut("Expecting first chance CLRN exception\n");
+ return E_FAIL;
+#else
+ g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "process continue", 0);
+ return S_OK;
+#endif
+ }
+
+ // Notification only needs to live for the lifetime of the call below, so it's a non-static
+ // local.
+ HRESULT Status = g_clrData->TranslateExceptionRecordToNotification(&dle.ExceptionRecord, &Notification);
+ if (Status != S_OK)
+ {
+ ExtErr("Error processing exception notification\n");
+ return Status;
+ }
+ else
+ {
+ switch (Notification.GetDebugStatus())
+ {
+ case DEBUG_STATUS_GO:
+ case DEBUG_STATUS_GO_HANDLED:
+ case DEBUG_STATUS_GO_NOT_HANDLED:
+#ifndef FEATURE_PAL
+ g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "g", 0);
+#else
+ g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "process continue", 0);
+#endif
+ break;
+ default:
+ break;
+ }
+ }
+
+ return S_OK;
+}
+
+#ifndef FEATURE_PAL
+
+DECLARE_API(HandleCLRN)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ return HandleCLRNotificationEvent();
+}
+
+#else // FEATURE_PAL
+
+HRESULT HandleExceptionNotification(ILLDBServices *client)
+{
+ INIT_API();
+ return HandleCLRNotificationEvent();
+}
+
+#endif // FEATURE_PAL
+
+DECLARE_API(bpmd)
+{
+ INIT_API_NOEE();
+ MINIDUMP_NOT_SUPPORTED();
+ int i;
+ char buffer[1024];
+
+ if (IsDumpFile())
+ {
+ ExtOut(SOSPrefix "bpmd is not supported on a dump file.\n");
+ return Status;
+ }
+
+
+ // We keep a list of managed breakpoints the user wants to set, and display pending bps
+ // bpmd. If you call bpmd <module name> <method> we will set or update an existing bp.
+ // bpmd acts as a feeder of breakpoints to bp when the time is right.
+ //
+
+ StringHolder DllName,TypeName;
+ int lineNumber = 0;
+ size_t Offset = 0;
+
+ DWORD_PTR pMD = NULL;
+ BOOL fNoFutureModule = FALSE;
+ BOOL fList = FALSE;
+ size_t clearItem = 0;
+ BOOL fClearAll = FALSE;
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-md", &pMD, COHEX, TRUE},
+ {"-nofuturemodule", &fNoFutureModule, COBOOL, FALSE},
+ {"-list", &fList, COBOOL, FALSE},
+ {"-clear", &clearItem, COSIZE_T, TRUE},
+ {"-clearall", &fClearAll, COBOOL, FALSE},
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&DllName.data, COSTRING},
+ {&TypeName.data, COSTRING},
+ {&Offset, COSIZE_T},
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ bool fBadParam = false;
+ bool fIsFilename = false;
+ int commandsParsed = 0;
+
+ if (pMD != NULL)
+ {
+ if (nArg != 0)
+ {
+ fBadParam = true;
+ }
+ commandsParsed++;
+ }
+ if (fList)
+ {
+ commandsParsed++;
+ if (nArg != 0)
+ {
+ fBadParam = true;
+ }
+ }
+ if (fClearAll)
+ {
+ commandsParsed++;
+ if (nArg != 0)
+ {
+ fBadParam = true;
+ }
+ }
+ if (clearItem != 0)
+ {
+ commandsParsed++;
+ if (nArg != 0)
+ {
+ fBadParam = true;
+ }
+ }
+ if (1 <= nArg && nArg <= 3)
+ {
+ commandsParsed++;
+ // did we get dll and type name or file:line#? Search for a colon in the first arg
+ // to see if it is in fact a file:line#
+ CHAR* pColon = strchr(DllName.data, ':');
+#ifndef FEATURE_PAL
+ if (FAILED(g_ExtSymbols->GetModuleByModuleName(MAIN_CLR_MODULE_NAME_A, 0, NULL, NULL))) {
+#else
+ if (FAILED(g_ExtSymbols->GetModuleByModuleName(MAIN_CLR_DLL_NAME_A, 0, NULL, NULL))) {
+#endif
+ ExtOut("%s not loaded yet\n", MAIN_CLR_DLL_NAME_A);
+ return Status;
+ }
+
+ if(NULL != pColon)
+ {
+ fIsFilename = true;
+ *pColon = '\0';
+ pColon++;
+ if(1 != sscanf_s(pColon, "%d", &lineNumber))
+ {
+ ExtOut("Unable to parse line number\n");
+ fBadParam = true;
+ }
+ else if(lineNumber < 0)
+ {
+ ExtOut("Line number must be positive\n");
+ fBadParam = true;
+ }
+ if(nArg != 1) fBadParam = 1;
+ }
+ }
+
+ if (fBadParam || (commandsParsed != 1))
+ {
+ ExtOut("Usage: " SOSPrefix "bpmd -md <MethodDesc pointer>\n");
+ ExtOut("Usage: " SOSPrefix "bpmd [-nofuturemodule] <module name> <managed function name> [<il offset>]\n");
+ ExtOut("Usage: " SOSPrefix "bpmd <filename>:<line number>\n");
+ ExtOut("Usage: " SOSPrefix "bpmd -list\n");
+ ExtOut("Usage: " SOSPrefix "bpmd -clear <pending breakpoint number>\n");
+ ExtOut("Usage: " SOSPrefix "bpmd -clearall\n");
+#ifdef FEATURE_PAL
+ ExtOut("See \"soshelp bpmd\" for more details.\n");
+#else
+ ExtOut("See \"!help bpmd\" for more details.\n");
+#endif
+ return Status;
+ }
+
+ if (fList)
+ {
+ g_bpoints.ListBreakpoints();
+ return Status;
+ }
+ if (clearItem != 0)
+ {
+ g_bpoints.ClearBreakpoint(clearItem);
+ return Status;
+ }
+ if (fClearAll)
+ {
+ g_bpoints.ClearAllBreakpoints();
+ return Status;
+ }
+ // Add a breakpoint
+ // Do we already have this breakpoint?
+ // Or, before setting it, is the module perhaps already loaded and code
+ // is available? If so, don't add to our pending list, just go ahead and
+ // set the real breakpoint.
+
+ LPWSTR ModuleName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
+ LPWSTR FunctionName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
+ LPWSTR Filename = (LPWSTR)alloca(MAX_LONGPATH * sizeof(WCHAR));
+
+ BOOL bNeedNotificationExceptions = FALSE;
+
+ if (pMD == NULL)
+ {
+ int numModule = 0;
+ int numMethods = 0;
+
+ ArrayHolder<DWORD_PTR> moduleList = NULL;
+
+ if(!fIsFilename)
+ {
+ MultiByteToWideChar(CP_ACP, 0, DllName.data, -1, ModuleName, mdNameLen);
+ MultiByteToWideChar(CP_ACP, 0, TypeName.data, -1, FunctionName, mdNameLen);
+ }
+ else
+ {
+ MultiByteToWideChar(CP_ACP, 0, DllName.data, -1, Filename, MAX_LONGPATH);
+ }
+
+ // Get modules that may need a breakpoint bound
+ if ((Status = CheckEEDll()) == S_OK)
+ {
+ if ((Status = LoadClrDebugDll()) != S_OK)
+ {
+ // if the EE is loaded but DAC isn't we should stop.
+ DACMessage(Status);
+ return Status;
+ }
+ g_bDacBroken = FALSE; \
+
+ // Get the module list
+ moduleList = ModuleFromName(fIsFilename ? NULL : DllName.data, &numModule);
+
+ // Its OK if moduleList is NULL
+ // There is a very normal case when checking for modules after clr is loaded
+ // but before any AppDomains or assemblies are created
+ // for example:
+ // >sxe ld:clr
+ // >g
+ // ...
+ // ModLoad: clr.dll
+ // >!bpmd Foo.dll Foo.Bar
+ }
+ // If LoadClrDebugDll() succeeded make sure we release g_clrData
+ ToRelease<IXCLRDataProcess> spIDP(g_clrData);
+ ToRelease<ISOSDacInterface> spISD(g_sos);
+ ResetGlobals();
+
+ // we can get here with EE not loaded => 0 modules
+ // EE is loaded => 0 or more modules
+ ArrayHolder<DWORD_PTR> pMDs = NULL;
+ for (int iModule = 0; iModule < numModule; iModule++)
+ {
+ ToRelease<IXCLRDataModule> ModDef;
+ if (g_sos->GetModule(moduleList[iModule], &ModDef) != S_OK)
+ {
+ continue;
+ }
+
+ HRESULT symbolsLoaded = S_FALSE;
+ if(!fIsFilename)
+ {
+ g_bpoints.ResolvePendingNonModuleBoundBreakpoint(ModuleName, FunctionName, moduleList[iModule], (DWORD)Offset);
+ }
+ else
+ {
+ SymbolReader symbolReader;
+ symbolsLoaded = g_bpoints.LoadSymbolsForModule(moduleList[iModule], &symbolReader);
+ if(symbolsLoaded == S_OK &&
+ g_bpoints.ResolvePendingNonModuleBoundBreakpoint(Filename, lineNumber, moduleList[iModule], &symbolReader) == S_OK)
+ {
+ // if we have symbols then get the function name so we can lookup the MethodDescs
+ mdMethodDef methodDefToken;
+ ULONG32 ilOffset;
+ if(SUCCEEDED(symbolReader.ResolveSequencePoint(Filename, lineNumber, moduleList[iModule], &methodDefToken, &ilOffset)))
+ {
+ ToRelease<IXCLRDataMethodDefinition> pMethodDef = NULL;
+ if (SUCCEEDED(ModDef->GetMethodDefinitionByToken(methodDefToken, &pMethodDef)))
+ {
+ ULONG32 nameLen = 0;
+ pMethodDef->GetName(0, mdNameLen, &nameLen, FunctionName);
+
+ // get the size of the required buffer
+ int buffSize = WideCharToMultiByte(CP_ACP, 0, FunctionName, -1, TypeName.data, 0, NULL, NULL);
+
+ TypeName.data = new NOTHROW char[buffSize];
+ if (TypeName.data != NULL)
+ {
+ int bytesWritten = WideCharToMultiByte(CP_ACP, 0, FunctionName, -1, TypeName.data, buffSize, NULL, NULL);
+ _ASSERTE(bytesWritten == buffSize);
+ }
+ }
+ }
+ }
+ }
+
+ HRESULT gotMethodDescs = GetMethodDescsFromName(moduleList[iModule], ModDef, TypeName.data, &pMDs, &numMethods);
+ if (FAILED(gotMethodDescs) && (!fIsFilename))
+ {
+ // BPs via file name will enumerate through modules so there will be legitimate failures.
+ // for module/type name we already found a match so this shouldn't fail (this is the original behavior).
+ ExtOut("Error getting MethodDescs for module %p\n", moduleList[iModule]);
+ return Status;
+ }
+
+ // for filename+line number only print extra info if symbols for this module are loaded (it can get quite noisy otherwise).
+ if ((!fIsFilename) || (fIsFilename && symbolsLoaded == S_OK))
+ {
+ for (int i = 0; i < numMethods; i++)
+ {
+ if (pMDs[i] == MD_NOT_YET_LOADED)
+ {
+ continue;
+ }
+ ExtOut("MethodDesc = %p\n", SOS_PTR(pMDs[i]));
+ }
+ }
+
+ if (g_bpoints.Update(moduleList[iModule], FALSE))
+ {
+ bNeedNotificationExceptions = TRUE;
+ }
+ }
+
+ if (!fNoFutureModule)
+ {
+ // add a pending breakpoint that will find future loaded modules, and
+ // wait for the module load notification.
+ if (!fIsFilename)
+ {
+ g_bpoints.Add(ModuleName, FunctionName, NULL, (DWORD)Offset);
+ }
+ else
+ {
+ g_bpoints.Add(Filename, lineNumber, NULL);
+ }
+ bNeedNotificationExceptions = TRUE;
+
+ ULONG32 flags = 0;
+ g_clrData->GetOtherNotificationFlags(&flags);
+ flags |= (CLRDATA_NOTIFY_ON_MODULE_LOAD | CLRDATA_NOTIFY_ON_MODULE_UNLOAD);
+ g_clrData->SetOtherNotificationFlags(flags);
+ }
+ }
+ else /* We were given a MethodDesc already */
+ {
+ // if we've got an explicit MD, then we better have CLR and mscordacwks loaded
+ INIT_API_EE()
+ INIT_API_DAC();
+
+ DacpMethodDescData MethodDescData;
+ ExtOut("MethodDesc = %p\n", SOS_PTR(pMD));
+ if (MethodDescData.Request(g_sos, TO_CDADDR(pMD)) != S_OK)
+ {
+ ExtOut("%p is not a valid MethodDesc\n", SOS_PTR(pMD));
+ return Status;
+ }
+
+ if (MethodDescData.bHasNativeCode)
+ {
+ IssueDebuggerBPCommand((size_t) MethodDescData.NativeCodeAddr);
+ }
+ else if (MethodDescData.bIsDynamic)
+ {
+#ifndef FEATURE_PAL
+ // Dynamic methods don't have JIT notifications. This is something we must
+ // fix in the next release. Until then, you have a cumbersome user experience.
+ ExtOut("This DynamicMethodDesc is not yet JITTED. Placing memory breakpoint at %p\n",
+ MethodDescData.AddressOfNativeCodeSlot);
+
+ sprintf_s(buffer, _countof(buffer),
+#ifdef _TARGET_WIN64_
+ "ba w8"
+#else
+ "ba w4"
+#endif // _TARGET_WIN64_
+
+ " /1 %p \"bp poi(%p); g\"",
+ (void*) (size_t) MethodDescData.AddressOfNativeCodeSlot,
+ (void*) (size_t) MethodDescData.AddressOfNativeCodeSlot);
+
+ Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
+ if (FAILED(Status))
+ {
+ ExtOut("Unable to set breakpoint with IDebugControl::Execute: %x\n",Status);
+ ExtOut("Attempted to run: %s\n", buffer);
+ }
+#else
+ ExtErr("This DynamicMethodDesc is not yet JITTED %p\n", MethodDescData.AddressOfNativeCodeSlot);
+#endif // FEATURE_PAL
+ }
+ else
+ {
+ // Must issue a pending breakpoint.
+ if (g_sos->GetMethodDescName(pMD, mdNameLen, FunctionName, NULL) != S_OK)
+ {
+ ExtOut("Unable to get method name for MethodDesc %p\n", SOS_PTR(pMD));
+ return Status;
+ }
+
+ FileNameForModule ((DWORD_PTR) MethodDescData.ModulePtr, ModuleName);
+
+ // We didn't find code, add a breakpoint.
+ g_bpoints.ResolvePendingNonModuleBoundBreakpoint(ModuleName, FunctionName, TO_TADDR(MethodDescData.ModulePtr), 0);
+ g_bpoints.Update(TO_TADDR(MethodDescData.ModulePtr), FALSE);
+ bNeedNotificationExceptions = TRUE;
+ }
+ }
+
+ if (bNeedNotificationExceptions)
+ {
+ ExtOut("Adding pending breakpoints...\n");
+#ifndef FEATURE_PAL
+ sprintf_s(buffer, _countof(buffer), "sxe -c \"!HandleCLRN\" clrn");
+ Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
+#else
+ Status = g_ExtServices->SetExceptionCallback(HandleExceptionNotification);
+#endif // FEATURE_PAL
+ }
+
+ return Status;
+}
+
+#ifndef FEATURE_PAL
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the managed threadpool *
+* *
+\**********************************************************************/
+DECLARE_API(ThreadPool)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ DacpThreadpoolData threadpool;
+
+ if ((Status = threadpool.Request(g_sos)) == S_OK)
+ {
+ BOOL doHCDump = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-ti", &doHCDump, COBOOL, FALSE}
+ };
+
+ if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ {
+ return Status;
+ }
+
+ ExtOut ("CPU utilization: %d%%\n", threadpool.cpuUtilization);
+ ExtOut ("Worker Thread:");
+ ExtOut (" Total: %d", threadpool.NumWorkingWorkerThreads + threadpool.NumIdleWorkerThreads + threadpool.NumRetiredWorkerThreads);
+ ExtOut (" Running: %d", threadpool.NumWorkingWorkerThreads);
+ ExtOut (" Idle: %d", threadpool.NumIdleWorkerThreads);
+ ExtOut (" MaxLimit: %d", threadpool.MaxLimitTotalWorkerThreads);
+ ExtOut (" MinLimit: %d", threadpool.MinLimitTotalWorkerThreads);
+ ExtOut ("\n");
+
+ int numWorkRequests = 0;
+ CLRDATA_ADDRESS workRequestPtr = threadpool.FirstUnmanagedWorkRequest;
+ DacpWorkRequestData workRequestData;
+ while (workRequestPtr)
+ {
+ if ((Status = workRequestData.Request(g_sos,workRequestPtr))!=S_OK)
+ {
+ ExtOut(" Failed to examine a WorkRequest\n");
+ return Status;
+ }
+ numWorkRequests++;
+ workRequestPtr = workRequestData.NextWorkRequest;
+ }
+
+ ExtOut ("Work Request in Queue: %d\n", numWorkRequests);
+ workRequestPtr = threadpool.FirstUnmanagedWorkRequest;
+ while (workRequestPtr)
+ {
+ if ((Status = workRequestData.Request(g_sos,workRequestPtr))!=S_OK)
+ {
+ ExtOut(" Failed to examine a WorkRequest\n");
+ return Status;
+ }
+
+ if (workRequestData.Function == threadpool.AsyncTimerCallbackCompletionFPtr)
+ ExtOut (" AsyncTimerCallbackCompletion TimerInfo@%p\n", SOS_PTR(workRequestData.Context));
+ else
+ ExtOut (" Unknown Function: %p Context: %p\n", SOS_PTR(workRequestData.Function),
+ SOS_PTR(workRequestData.Context));
+
+ workRequestPtr = workRequestData.NextWorkRequest;
+ }
+
+ if (doHCDump)
+ {
+ ExtOut ("--------------------------------------\n");
+ ExtOut ("\nThread Injection History\n");
+ if (threadpool.HillClimbingLogSize > 0)
+ {
+ static char const * const TransitionNames[] =
+ {
+ "Warmup",
+ "Initializing",
+ "RandomMove",
+ "ClimbingMove",
+ "ChangePoint",
+ "Stabilizing",
+ "Starvation",
+ "ThreadTimedOut",
+ "Undefined"
+ };
+
+ ExtOut("\n Time Transition New #Threads #Samples Throughput\n");
+ DacpHillClimbingLogEntry entry;
+
+ // get the most recent entry first, so we can calculate time offsets
+
+ int index = (threadpool.HillClimbingLogFirstIndex + threadpool.HillClimbingLogSize-1) % HillClimbingLogCapacity;
+ CLRDATA_ADDRESS entryPtr = threadpool.HillClimbingLog + (index * sizeof(HillClimbingLogEntry));
+ if ((Status = entry.Request(g_sos,entryPtr))!=S_OK)
+ {
+ ExtOut(" Failed to examine a HillClimbing log entry\n");
+ return Status;
+ }
+ DWORD endTime = entry.TickCount;
+
+ for (int i = 0; i < threadpool.HillClimbingLogSize; i++)
+ {
+ index = (i + threadpool.HillClimbingLogFirstIndex) % HillClimbingLogCapacity;
+ entryPtr = threadpool.HillClimbingLog + (index * sizeof(HillClimbingLogEntry));
+
+ if ((Status = entry.Request(g_sos,entryPtr))!=S_OK)
+ {
+ ExtOut(" Failed to examine a HillClimbing log entry\n");
+ return Status;
+ }
+
+ ExtOut("%8.2lf %-14s %12d %12d %11.2lf\n",
+ (double)(int)(entry.TickCount - endTime) / 1000.0,
+ TransitionNames[entry.Transition],
+ entry.NewControlSetting,
+ entry.LastHistoryCount,
+ entry.LastHistoryMean);
+ }
+ }
+ }
+
+ ExtOut ("--------------------------------------\n");
+ ExtOut ("Number of Timers: %d\n", threadpool.NumTimers);
+ ExtOut ("--------------------------------------\n");
+
+ ExtOut ("Completion Port Thread:");
+ ExtOut ("Total: %d", threadpool.NumCPThreads);
+ ExtOut (" Free: %d", threadpool.NumFreeCPThreads);
+ ExtOut (" MaxFree: %d", threadpool.MaxFreeCPThreads);
+ ExtOut (" CurrentLimit: %d", threadpool.CurrentLimitTotalCPThreads);
+ ExtOut (" MaxLimit: %d", threadpool.MaxLimitTotalCPThreads);
+ ExtOut (" MinLimit: %d", threadpool.MinLimitTotalCPThreads);
+ ExtOut ("\n");
+ }
+ else
+ {
+ ExtOut("Failed to request ThreadpoolMgr information\n");
+ }
+ return Status;
+}
+
+#endif // FEATURE_PAL
+
+DECLARE_API(FindAppDomain)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ DWORD_PTR p_Object = NULL;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE},
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&p_Object, COHEX},
+ };
+ size_t nArg;
+
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+
+ if ((p_Object == 0) || !sos::IsObject(p_Object))
+ {
+ ExtOut("%p is not a valid object\n", SOS_PTR(p_Object));
+ return Status;
+ }
+
+ DacpAppDomainStoreData adstore;
+ if (adstore.Request(g_sos) != S_OK)
+ {
+ ExtOut("Error getting AppDomain information\n");
+ return Status;
+ }
+
+ CLRDATA_ADDRESS appDomain = GetAppDomain (TO_CDADDR(p_Object));
+
+ if (appDomain != NULL)
+ {
+ DMLOut("AppDomain: %s\n", DMLDomain(appDomain));
+ if (appDomain == adstore.sharedDomain)
+ {
+ ExtOut("Name: Shared Domain\n");
+ ExtOut("ID: (shared domain)\n");
+ }
+ else if (appDomain == adstore.systemDomain)
+ {
+ ExtOut("Name: System Domain\n");
+ ExtOut("ID: (system domain)\n");
+ }
+ else
+ {
+ DacpAppDomainData domain;
+ if ((domain.Request(g_sos, appDomain) != S_OK) ||
+ (g_sos->GetAppDomainName(appDomain,mdNameLen,g_mdName, NULL)!=S_OK))
+ {
+ ExtOut("Error getting AppDomain %p.\n", SOS_PTR(appDomain));
+ return Status;
+ }
+
+ ExtOut("Name: %S\n", (g_mdName[0]!=L'\0') ? g_mdName : W("None"));
+ ExtOut("ID: %d\n", domain.dwId);
+ }
+ }
+ else
+ {
+ ExtOut("The type is declared in the shared domain and other\n");
+ ExtOut("methods of finding the AppDomain failed. Try running\n");
+ if (IsDMLEnabled())
+ DMLOut("<exec cmd=\"!gcroot /d %p\">!gcroot %p</exec>, and if you find a root on a\n", p_Object, p_Object);
+ else
+ ExtOut("!gcroot %p, and if you find a root on a\n", p_Object);
+ ExtOut("stack, check the AppDomain of that stack with !threads.\n");
+ ExtOut("Note that the Thread could have transitioned between\n");
+ ExtOut("multiple AppDomains.\n");
+ }
+
+ return Status;
+}
+
+#ifndef FEATURE_PAL
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to get the COM state (e.g. APT,contexe *
+* activity. *
+* *
+\**********************************************************************/
+#ifdef FEATURE_COMINTEROP
+DECLARE_API(COMState)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+
+ ULONG numThread;
+ ULONG maxId;
+ g_ExtSystem->GetTotalNumberThreads(&numThread,&maxId);
+
+ ULONG curId;
+ g_ExtSystem->GetCurrentThreadId(&curId);
+
+ SIZE_T AllocSize;
+ if (!ClrSafeInt<SIZE_T>::multiply(sizeof(ULONG), numThread, AllocSize))
+ {
+ ExtOut(" Error! integer overflow on numThread 0x%08x\n", numThread);
+ return Status;
+ }
+ ULONG *ids = (ULONG*)alloca(AllocSize);
+ ULONG *sysIds = (ULONG*)alloca(AllocSize);
+ g_ExtSystem->GetThreadIdsByIndex(0,numThread,ids,sysIds);
+#if defined(_TARGET_WIN64_)
+ ExtOut(" ID TEB APT APTId CallerTID Context\n");
+#else
+ ExtOut(" ID TEB APT APTId CallerTID Context\n");
+#endif
+ for (ULONG i = 0; i < numThread; i ++) {
+ g_ExtSystem->SetCurrentThreadId(ids[i]);
+ CLRDATA_ADDRESS cdaTeb;
+ g_ExtSystem->GetCurrentThreadTeb(&cdaTeb);
+ ExtOut("%3d %4x %p", ids[i], sysIds[i], SOS_PTR(CDA_TO_UL64(cdaTeb)));
+ // Apartment state
+ TADDR OleTlsDataAddr;
+ if (SafeReadMemory(TO_TADDR(cdaTeb) + offsetof(TEB,ReservedForOle),
+ &OleTlsDataAddr,
+ sizeof(OleTlsDataAddr), NULL) && OleTlsDataAddr != 0) {
+ DWORD AptState;
+ if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwFlags),
+ &AptState,
+ sizeof(AptState), NULL)) {
+ if (AptState & OLETLS_APARTMENTTHREADED) {
+ ExtOut(" STA");
+ }
+ else if (AptState & OLETLS_MULTITHREADED) {
+ ExtOut(" MTA");
+ }
+ else if (AptState & OLETLS_INNEUTRALAPT) {
+ ExtOut(" NTA");
+ }
+ else {
+ ExtOut(" Ukn");
+ }
+
+ // Read these fields only if we were able to read anything of the SOleTlsData structure
+ DWORD dwApartmentID;
+ if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwApartmentID),
+ &dwApartmentID,
+ sizeof(dwApartmentID), NULL)) {
+ ExtOut(" %8x", dwApartmentID);
+ }
+ else
+ ExtOut(" %8x", 0);
+
+ DWORD dwTIDCaller;
+ if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwTIDCaller),
+ &dwTIDCaller,
+ sizeof(dwTIDCaller), NULL)) {
+ ExtOut(" %8x", dwTIDCaller);
+ }
+ else
+ ExtOut(" %8x", 0);
+
+ size_t Context;
+ if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,pCurrentCtx),
+ &Context,
+ sizeof(Context), NULL)) {
+ ExtOut(" %p", SOS_PTR(Context));
+ }
+ else
+ ExtOut(" %p", SOS_PTR(0));
+
+ }
+ else
+ ExtOut(" Ukn");
+ }
+ else
+ ExtOut(" Ukn");
+ ExtOut("\n");
+ }
+
+ g_ExtSystem->SetCurrentThreadId(curId);
+ return Status;
+}
+#endif // FEATURE_COMINTEROP
+
+#endif // FEATURE_PAL
+
+BOOL traverseEh(UINT clauseIndex,UINT totalClauses,DACEHInfo *pEHInfo,LPVOID token)
+{
+ size_t methodStart = (size_t) token;
+
+ if (IsInterrupt())
+ {
+ return FALSE;
+ }
+
+ ExtOut("EHHandler %d: %s ", clauseIndex, EHTypeName(pEHInfo->clauseType));
+
+ LPCWSTR typeName = EHTypedClauseTypeName(pEHInfo);
+ if (typeName != NULL)
+ {
+ ExtOut("catch(%S) ", typeName);
+ }
+
+ if (IsClonedFinally(pEHInfo))
+ ExtOut("(cloned finally)");
+ else if (pEHInfo->isDuplicateClause)
+ ExtOut("(duplicate)");
+
+ ExtOut("\n");
+ ExtOut("Clause: ");
+
+ ULONG64 addrStart = pEHInfo->tryStartOffset + methodStart;
+ ULONG64 addrEnd = pEHInfo->tryEndOffset + methodStart;
+
+#ifdef _WIN64
+ ExtOut("[%08x`%08x, %08x`%08x]",
+ (ULONG)(addrStart >> 32), (ULONG)addrStart,
+ (ULONG)(addrEnd >> 32), (ULONG)addrEnd);
+#else
+ ExtOut("[%08x, %08x]", (ULONG)addrStart, (ULONG)addrEnd);
+#endif
+
+ ExtOut(" [%x, %x]\n",
+ (UINT32) pEHInfo->tryStartOffset,
+ (UINT32) pEHInfo->tryEndOffset);
+
+ ExtOut("Handler: ");
+
+ addrStart = pEHInfo->handlerStartOffset + methodStart;
+ addrEnd = pEHInfo->handlerEndOffset + methodStart;
+
+#ifdef _WIN64
+ ExtOut("[%08x`%08x, %08x`%08x]",
+ (ULONG)(addrStart >> 32), (ULONG)addrStart,
+ (ULONG)(addrEnd >> 32), (ULONG)addrEnd);
+#else
+ ExtOut("[%08x, %08x]", (ULONG)addrStart, (ULONG)addrEnd);
+#endif
+
+ ExtOut(" [%x, %x]\n",
+ (UINT32) pEHInfo->handlerStartOffset,
+ (UINT32) pEHInfo->handlerEndOffset);
+
+ if (pEHInfo->clauseType == EHFilter)
+ {
+ ExtOut("Filter: ");
+
+ addrStart = pEHInfo->filterOffset + methodStart;
+
+#ifdef _WIN64
+ ExtOut("[%08x`%08x]", (ULONG)(addrStart >> 32), (ULONG)addrStart);
+#else
+ ExtOut("[%08x]", (ULONG)addrStart);
+#endif
+
+ ExtOut(" [%x]\n",
+ (UINT32) pEHInfo->filterOffset);
+ }
+
+ ExtOut("\n");
+ return TRUE;
+}
+
+DECLARE_API(EHInfo)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ DWORD_PTR dwStartAddr = NULL;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"/d", &dml, COBOOL, FALSE},
+ };
+
+ CMDValue arg[] =
+ { // vptr, type
+ {&dwStartAddr, COHEX},
+ };
+
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || (0 == nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ DWORD_PTR tmpAddr = dwStartAddr;
+
+ if (!IsMethodDesc(dwStartAddr))
+ {
+ JITTypes jitType;
+ DWORD_PTR methodDesc;
+ DWORD_PTR gcinfoAddr;
+ IP2MethodDesc (dwStartAddr, methodDesc, jitType, gcinfoAddr);
+ tmpAddr = methodDesc;
+ }
+
+ DacpMethodDescData MD;
+ if ((tmpAddr == 0) || (MD.Request(g_sos, TO_CDADDR(tmpAddr)) != S_OK))
+ {
+ ExtOut("%p is not a MethodDesc\n", SOS_PTR(tmpAddr));
+ return Status;
+ }
+
+ if (1 == nArg && !MD.bHasNativeCode)
+ {
+ ExtOut("No EH info available\n");
+ return Status;
+ }
+
+ DacpCodeHeaderData codeHeaderData;
+ if (codeHeaderData.Request(g_sos, TO_CDADDR(MD.NativeCodeAddr)) != S_OK)
+ {
+ ExtOut("Unable to get codeHeader information\n");
+ return Status;
+ }
+
+ DMLOut("MethodDesc: %s\n", DMLMethodDesc(MD.MethodDescPtr));
+ DumpMDInfo(TO_TADDR(MD.MethodDescPtr));
+
+ ExtOut("\n");
+ Status = g_sos->TraverseEHInfo(TO_CDADDR(MD.NativeCodeAddr), traverseEh, (LPVOID)MD.NativeCodeAddr);
+
+ if (Status == E_ABORT)
+ {
+ ExtOut("<user aborted>\n");
+ }
+ else if (Status != S_OK)
+ {
+ ExtOut("Failed to perform EHInfo traverse\n");
+ }
+
+ return Status;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the GC encoding of a managed *
+* function. *
+* *
+\**********************************************************************/
+DECLARE_API(GCInfo)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+
+ TADDR taStartAddr = NULL;
+ TADDR taGCInfoAddr;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"/d", &dml, COBOOL, FALSE},
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&taStartAddr, COHEX},
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || (0 == nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ TADDR tmpAddr = taStartAddr;
+
+ if (!IsMethodDesc(taStartAddr))
+ {
+ JITTypes jitType;
+ TADDR methodDesc;
+ TADDR gcinfoAddr;
+ IP2MethodDesc(taStartAddr, methodDesc, jitType, gcinfoAddr);
+ tmpAddr = methodDesc;
+ }
+
+ DacpMethodDescData MD;
+ if ((tmpAddr == 0) || (MD.Request(g_sos, TO_CDADDR(tmpAddr)) != S_OK))
+ {
+ ExtOut("%p is not a valid MethodDesc\n", SOS_PTR(taStartAddr));
+ return Status;
+ }
+
+ if (1 == nArg && !MD.bHasNativeCode)
+ {
+ ExtOut("No GC info available\n");
+ return Status;
+ }
+
+ DacpCodeHeaderData codeHeaderData;
+
+ if (
+ // Try to get code header data from taStartAddr. This will get the code
+ // header corresponding to the IP address, even if the function was rejitted
+ (codeHeaderData.Request(g_sos, TO_CDADDR(taStartAddr)) != S_OK) &&
+
+ // If that didn't work, just try to use the code address that the MD
+ // points to. If the function was rejitted, this will only give you the
+ // original JITted code, but that's better than nothing
+ (codeHeaderData.Request(g_sos, TO_CDADDR(MD.NativeCodeAddr)) != S_OK)
+ )
+ {
+ // We always used to emit this (before rejit support), even if we couldn't get
+ // the code header, so keep on doing so.
+ ExtOut("entry point %p\n", SOS_PTR(MD.NativeCodeAddr));
+
+ // And now the error....
+ ExtOut("Unable to get codeHeader information\n");
+ return Status;
+ }
+
+ // We have the code header, so use it to determine the method start
+
+ ExtOut("entry point %p\n", SOS_PTR(codeHeaderData.MethodStart));
+
+ if (codeHeaderData.JITType == TYPE_UNKNOWN)
+ {
+ ExtOut("unknown Jit\n");
+ return Status;
+ }
+ else if (codeHeaderData.JITType == TYPE_JIT)
+ {
+ ExtOut("Normal JIT generated code\n");
+ }
+ else if (codeHeaderData.JITType == TYPE_PJIT)
+ {
+ ExtOut("preJIT generated code\n");
+ }
+
+ taGCInfoAddr = TO_TADDR(codeHeaderData.GCInfo);
+
+ ExtOut("GC info %p\n", SOS_PTR(taGCInfoAddr));
+
+ // assume that GC encoding table is never more than
+ // 40 + methodSize * 2
+ int tableSize = 0;
+ if (!ClrSafeInt<int>::multiply(codeHeaderData.MethodSize, 2, tableSize) ||
+ !ClrSafeInt<int>::addition(tableSize, 40, tableSize))
+ {
+ ExtOut("<integer overflow>\n");
+ return E_FAIL;
+ }
+ ArrayHolder<BYTE> table = new NOTHROW BYTE[tableSize];
+ if (table == NULL)
+ {
+ ExtOut("Could not allocate memory to read the gc info.\n");
+ return E_OUTOFMEMORY;
+ }
+
+ memset(table, 0, tableSize);
+ // We avoid using move here, because we do not want to return
+ if (!SafeReadMemory(taGCInfoAddr, table, tableSize, NULL))
+ {
+ ExtOut("Could not read memory %p\n", SOS_PTR(taGCInfoAddr));
+ return Status;
+ }
+
+ // Mutable table pointer since we need to pass the appropriate
+ // offset into the table to DumpGCTable.
+ GCInfoToken gcInfoToken = { table, GCINFO_VERSION };
+ unsigned int methodSize = (unsigned int)codeHeaderData.MethodSize;
+
+ g_targetMachine->DumpGCInfo(gcInfoToken, methodSize, ExtOut, true /*encBytes*/, true /*bPrintHeader*/);
+
+ return Status;
+}
+
+#if !defined(FEATURE_PAL)
+
+void DecodeGCTableEntry (const char *fmt, ...)
+{
+ GCEncodingInfo *pInfo = (GCEncodingInfo*)GetFiberData();
+ va_list va;
+
+ //
+ // Append the new data to the buffer
+ //
+
+ va_start(va, fmt);
+
+ int cch = _vsnprintf_s(&pInfo->buf[pInfo->cch], _countof(pInfo->buf) - pInfo->cch, _countof(pInfo->buf) - pInfo->cch - 1, fmt, va);
+ if (cch >= 0)
+ pInfo->cch += cch;
+
+ va_end(va);
+
+ pInfo->buf[pInfo->cch] = '\0';
+
+ //
+ // If there are complete lines in the buffer, decode them.
+ //
+
+ for (;;)
+ {
+ char *pNewLine = strchr(pInfo->buf, '\n');
+
+ if (!pNewLine)
+ break;
+
+ //
+ // The line should start with a 16-bit (x86) or 32-bit (non-x86) hex
+ // offset. strtoul returns ULONG_MAX or 0 on failure. 0 is a valid
+ // offset for the first encoding, or while the last offset was 0.
+ //
+
+ if (isxdigit(pInfo->buf[0]))
+ {
+ char *pEnd;
+ ULONG ofs = strtoul(pInfo->buf, &pEnd, 16);
+
+ if ( isspace(*pEnd)
+ && -1 != ofs
+ && ( -1 == pInfo->ofs
+ || 0 == pInfo->ofs
+ || ofs > 0))
+ {
+ pInfo->ofs = ofs;
+ *pNewLine = '\0';
+
+ SwitchToFiber(pInfo->pvMainFiber);
+ }
+ }
+ else if (0 == strncmp(pInfo->buf, "Untracked:", 10))
+ {
+ pInfo->ofs = 0;
+ *pNewLine = '\0';
+
+ SwitchToFiber(pInfo->pvMainFiber);
+ }
+
+ //
+ // Shift the remaining data to the start of the buffer
+ //
+
+ strcpy_s(pInfo->buf, _countof(pInfo->buf), pNewLine+1);
+ pInfo->cch = (int)strlen(pInfo->buf);
+ }
+}
+
+
+VOID CALLBACK DumpGCTableFiberEntry (LPVOID pvGCEncodingInfo)
+{
+ GCEncodingInfo *pInfo = (GCEncodingInfo*)pvGCEncodingInfo;
+ GCInfoToken gcInfoToken = { pInfo->table, GCINFO_VERSION };
+ g_targetMachine->DumpGCInfo(gcInfoToken, pInfo->methodSize, DecodeGCTableEntry, false /*encBytes*/, false /*bPrintHeader*/);
+
+ pInfo->fDoneDecoding = true;
+ SwitchToFiber(pInfo->pvMainFiber);
+}
+#endif // !FEATURE_PAL
+
+BOOL gatherEh(UINT clauseIndex,UINT totalClauses,DACEHInfo *pEHInfo,LPVOID token)
+{
+ SOSEHInfo *pInfo = (SOSEHInfo *) token;
+
+ if (pInfo == NULL)
+ {
+ return FALSE;
+ }
+
+ if (pInfo->m_pInfos == NULL)
+ {
+ // First time, initialize structure
+ pInfo->EHCount = totalClauses;
+ pInfo->m_pInfos = new NOTHROW DACEHInfo[totalClauses];
+ if (pInfo->m_pInfos == NULL)
+ {
+ ReportOOM();
+ return FALSE;
+ }
+ }
+
+ pInfo->m_pInfos[clauseIndex] = *((DACEHInfo*)pEHInfo);
+ return TRUE;
+}
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to unassembly a managed function. *
+* It tries to print symbolic info for function call, contants... *
+* *
+\**********************************************************************/
+DECLARE_API(u)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+
+ DWORD_PTR dwStartAddr = NULL;
+ BOOL fWithGCInfo = FALSE;
+ BOOL fWithEHInfo = FALSE;
+ BOOL bSuppressLines = FALSE;
+ BOOL bDisplayOffsets = FALSE;
+ BOOL dml = FALSE;
+ size_t nArg;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+#ifndef FEATURE_PAL
+ {"-gcinfo", &fWithGCInfo, COBOOL, FALSE},
+#endif
+ {"-ehinfo", &fWithEHInfo, COBOOL, FALSE},
+ {"-n", &bSuppressLines, COBOOL, FALSE},
+ {"-o", &bDisplayOffsets, COBOOL, FALSE},
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE},
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&dwStartAddr, COHEX},
+ };
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || (nArg < 1))
+ {
+ return Status;
+ }
+ // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
+ ULONG symlines = 0;
+ if (!bSuppressLines && SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
+ {
+ symlines &= SYMOPT_LOAD_LINES;
+ }
+ bSuppressLines = bSuppressLines || (symlines == 0);
+
+ EnableDMLHolder dmlHolder(dml);
+ // dwStartAddr is either some IP address or a MethodDesc. Start off assuming it's a
+ // MethodDesc.
+ DWORD_PTR methodDesc = dwStartAddr;
+ if (!IsMethodDesc(methodDesc))
+ {
+ // Not a methodDesc, so gotta find it ourselves
+ DWORD_PTR tmpAddr = dwStartAddr;
+ JITTypes jt;
+ DWORD_PTR gcinfoAddr;
+ IP2MethodDesc (tmpAddr, methodDesc, jt,
+ gcinfoAddr);
+ if (!methodDesc || jt == TYPE_UNKNOWN)
+ {
+ // It is not managed code.
+ ExtOut("Unmanaged code\n");
+ UnassemblyUnmanaged(dwStartAddr, bSuppressLines);
+ return Status;
+ }
+ }
+
+ DacpMethodDescData MethodDescData;
+ if ((Status=MethodDescData.Request(g_sos, TO_CDADDR(methodDesc))) != S_OK)
+ {
+ ExtOut("Failed to get method desc for %p.\n", SOS_PTR(dwStartAddr));
+ return Status;
+ }
+
+ if (!MethodDescData.bHasNativeCode)
+ {
+ ExtOut("Not jitted yet\n");
+ return Status;
+ }
+
+ // Get the appropriate code header. If we were passed an MD, then use
+ // MethodDescData.NativeCodeAddr to find the code header; if we were passed an IP, use
+ // that IP to find the code header. This ensures that, for rejitted functions, we
+ // disassemble the rejit version that the user explicitly specified with their IP.
+ DacpCodeHeaderData codeHeaderData;
+ if (codeHeaderData.Request(
+ g_sos,
+ TO_CDADDR(
+ (dwStartAddr == methodDesc) ? MethodDescData.NativeCodeAddr : dwStartAddr)
+ ) != S_OK)
+
+ {
+ ExtOut("Unable to get codeHeader information\n");
+ return Status;
+ }
+
+ if (codeHeaderData.MethodStart == 0)
+ {
+ ExtOut("not a valid MethodDesc\n");
+ return Status;
+ }
+
+ if (codeHeaderData.JITType == TYPE_UNKNOWN)
+ {
+ ExtOut("unknown Jit\n");
+ return Status;
+ }
+ else if (codeHeaderData.JITType == TYPE_JIT)
+ {
+ ExtOut("Normal JIT generated code\n");
+ }
+ else if (codeHeaderData.JITType == TYPE_PJIT)
+ {
+ ExtOut("preJIT generated code\n");
+ }
+
+ NameForMD_s(methodDesc, g_mdName, mdNameLen);
+ ExtOut("%S\n", g_mdName);
+ if (codeHeaderData.ColdRegionStart != NULL)
+ {
+ ExtOut("Begin %p, size %x. Cold region begin %p, size %x\n",
+ SOS_PTR(codeHeaderData.MethodStart), codeHeaderData.HotRegionSize,
+ SOS_PTR(codeHeaderData.ColdRegionStart), codeHeaderData.ColdRegionSize);
+ }
+ else
+ {
+ ExtOut("Begin %p, size %x\n", SOS_PTR(codeHeaderData.MethodStart), codeHeaderData.MethodSize);
+ }
+
+#if !defined(FEATURE_PAL)
+ //
+ // Set up to mix gc info with the code if requested
+ //
+
+ GCEncodingInfo gcEncodingInfo = {0};
+
+ // The actual GC Encoding Table, this is updated during the course of the function.
+ gcEncodingInfo.table = NULL;
+
+ // The holder to make sure we clean up the memory for the table
+ ArrayHolder<BYTE> table = NULL;
+
+ if (fWithGCInfo)
+ {
+ // assume that GC encoding table is never more than 40 + methodSize * 2
+ int tableSize = 0;
+ if (!ClrSafeInt<int>::multiply(codeHeaderData.MethodSize, 2, tableSize) ||
+ !ClrSafeInt<int>::addition(tableSize, 40, tableSize))
+ {
+ ExtOut("<integer overflow>\n");
+ return E_FAIL;
+ }
+
+
+ // Assign the new array to the mutable gcEncodingInfo table and to the
+ // table ArrayHolder to clean this up when the function exits.
+ table = gcEncodingInfo.table = new NOTHROW BYTE[tableSize];
+
+ if (gcEncodingInfo.table == NULL)
+ {
+ ExtOut("Could not allocate memory to read the gc info.\n");
+ return E_OUTOFMEMORY;
+ }
+
+ memset (gcEncodingInfo.table, 0, tableSize);
+ // We avoid using move here, because we do not want to return
+ if (!SafeReadMemory(TO_TADDR(codeHeaderData.GCInfo), gcEncodingInfo.table, tableSize, NULL))
+ {
+ ExtOut("Could not read memory %p\n", SOS_PTR(codeHeaderData.GCInfo));
+ return Status;
+ }
+
+ //
+ // Skip the info header
+ //
+ gcEncodingInfo.methodSize = (unsigned int)codeHeaderData.MethodSize;
+
+ //
+ // DumpGCTable will call gcPrintf for each encoding. We'd like a "give
+ // me the next encoding" interface, but we're stuck with the callback.
+ // To reconcile this without messing up too much code, we'll create a
+ // fiber to dump the gc table. When we need the next gc encoding,
+ // we'll switch to this fiber. The callback will note the next offset,
+ // and switch back to the main fiber.
+ //
+
+ gcEncodingInfo.ofs = -1;
+ gcEncodingInfo.hotSizeToAdd = 0;
+
+ gcEncodingInfo.pvMainFiber = ConvertThreadToFiber(NULL);
+ if (!gcEncodingInfo.pvMainFiber && ERROR_ALREADY_FIBER == GetLastError())
+ gcEncodingInfo.pvMainFiber = GetCurrentFiber();
+
+ if (!gcEncodingInfo.pvMainFiber)
+ return Status;
+
+ gcEncodingInfo.pvGCTableFiber = CreateFiber(0, DumpGCTableFiberEntry, &gcEncodingInfo);
+ if (!gcEncodingInfo.pvGCTableFiber)
+ return Status;
+
+ SwitchToFiber(gcEncodingInfo.pvGCTableFiber);
+ }
+#endif
+
+ SOSEHInfo *pInfo = NULL;
+ if (fWithEHInfo)
+ {
+ pInfo = new NOTHROW SOSEHInfo;
+ if (pInfo == NULL)
+ {
+ ReportOOM();
+ }
+ else if (g_sos->TraverseEHInfo(MethodDescData.NativeCodeAddr, gatherEh, (LPVOID)pInfo) != S_OK)
+ {
+ ExtOut("Failed to gather EHInfo data\n");
+ delete pInfo;
+ pInfo = NULL;
+ }
+ }
+
+ if (codeHeaderData.ColdRegionStart == NULL)
+ {
+ g_targetMachine->Unassembly (
+ (DWORD_PTR) codeHeaderData.MethodStart,
+ ((DWORD_PTR)codeHeaderData.MethodStart) + codeHeaderData.MethodSize,
+ dwStartAddr,
+ (DWORD_PTR) MethodDescData.GCStressCodeCopy,
+#if !defined(FEATURE_PAL)
+ fWithGCInfo ? &gcEncodingInfo :
+#endif
+ NULL,
+ pInfo,
+ bSuppressLines,
+ bDisplayOffsets
+ );
+ }
+ else
+ {
+ ExtOut("Hot region:\n");
+ g_targetMachine->Unassembly (
+ (DWORD_PTR) codeHeaderData.MethodStart,
+ ((DWORD_PTR)codeHeaderData.MethodStart) + codeHeaderData.HotRegionSize,
+ dwStartAddr,
+ (DWORD_PTR) MethodDescData.GCStressCodeCopy,
+#if !defined(FEATURE_PAL)
+ fWithGCInfo ? &gcEncodingInfo :
+#endif
+ NULL,
+ pInfo,
+ bSuppressLines,
+ bDisplayOffsets
+ );
+
+ ExtOut("Cold region:\n");
+
+#if !defined(FEATURE_PAL)
+ // Displaying gcinfo for a cold region requires knowing the size of
+ // the hot region preceeding.
+ gcEncodingInfo.hotSizeToAdd = codeHeaderData.HotRegionSize;
+#endif
+ g_targetMachine->Unassembly (
+ (DWORD_PTR) codeHeaderData.ColdRegionStart,
+ ((DWORD_PTR)codeHeaderData.ColdRegionStart) + codeHeaderData.ColdRegionSize,
+ dwStartAddr,
+ ((DWORD_PTR) MethodDescData.GCStressCodeCopy) + codeHeaderData.HotRegionSize,
+#if !defined(FEATURE_PAL)
+ fWithGCInfo ? &gcEncodingInfo :
+#endif
+ NULL,
+ pInfo,
+ bSuppressLines,
+ bDisplayOffsets
+ );
+
+ }
+
+ if (pInfo)
+ {
+ delete pInfo;
+ pInfo = NULL;
+ }
+
+#if !defined(FEATURE_PAL)
+ if (fWithGCInfo)
+ DeleteFiber(gcEncodingInfo.pvGCTableFiber);
+#endif
+
+ return Status;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the in-memory stress log *
+* !DumpLog [filename] *
+* will dump the stress log corresponding to the clr.dll *
+* loaded in the debuggee's VAS *
+* !DumpLog -addr <addr_of_StressLog::theLog> [filename] *
+* will dump the stress log associated with any DLL linked *
+* against utilcode.lib, most commonly mscordbi.dll *
+* (e.g. !DumpLog -addr mscordbi!StressLog::theLog) *
+* *
+\**********************************************************************/
+DECLARE_API(DumpLog)
+{
+ INIT_API_NO_RET_ON_FAILURE();
+
+ MINIDUMP_NOT_SUPPORTED();
+
+ const char* fileName = "StressLog.txt";
+
+ CLRDATA_ADDRESS StressLogAddress = NULL;
+
+ StringHolder sFileName, sLogAddr;
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-addr", &sLogAddr.data, COSTRING, TRUE}
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&sFileName.data, COSTRING}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+ if (nArg > 0 && sFileName.data != NULL)
+ {
+ fileName = sFileName.data;
+ }
+
+ // allow users to specify -addr mscordbdi!StressLog::theLog, for example.
+ if (sLogAddr.data != NULL)
+ {
+ StressLogAddress = GetExpression(sLogAddr.data);
+ }
+
+ if (StressLogAddress == NULL)
+ {
+ if (g_bDacBroken)
+ {
+#ifdef FEATURE_PAL
+ ExtOut("No stress log address. DAC is broken; can't get it\n");
+ return E_FAIL;
+#else
+ // Try to find stress log symbols
+ DWORD_PTR dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!StressLog::theLog");
+ StressLogAddress = dwAddr;
+#endif
+ }
+ else if (g_sos->GetStressLogAddress(&StressLogAddress) != S_OK)
+ {
+ ExtOut("Unable to find stress log via DAC\n");
+ return E_FAIL;
+ }
+ }
+
+ if (StressLogAddress == NULL)
+ {
+ ExtOut("Please provide the -addr argument for the address of the stress log, since no recognized runtime is loaded.\n");
+ return E_FAIL;
+ }
+
+ ExtOut("Attempting to dump Stress log to file '%s'\n", fileName);
+
+
+
+ Status = StressLog::Dump(StressLogAddress, fileName, g_ExtData);
+
+ if (Status == S_OK)
+ ExtOut("SUCCESS: Stress log dumped\n");
+ else if (Status == S_FALSE)
+ ExtOut("No Stress log in the image, no file written\n");
+ else
+ ExtOut("FAILURE: Stress log not dumped\n");
+
+ return Status;
+}
+
+#ifdef TRACE_GC
+
+DECLARE_API (DumpGCLog)
+{
+ INIT_API_NODAC();
+ MINIDUMP_NOT_SUPPORTED();
+
+ if (GetEEFlavor() == UNKNOWNEE)
+ {
+ ExtOut("CLR not loaded\n");
+ return Status;
+ }
+
+ const char* fileName = "GCLog.txt";
+
+ while (isspace (*args))
+ args ++;
+
+ if (*args != 0)
+ fileName = args;
+
+ DWORD_PTR dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!SVR::gc_log_buffer");
+ moveN (dwAddr, dwAddr);
+
+ if (dwAddr == 0)
+ {
+ dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!WKS::gc_log_buffer");
+ moveN (dwAddr, dwAddr);
+ if (dwAddr == 0)
+ {
+ ExtOut("Can't get either WKS or SVR GC's log file");
+ return E_FAIL;
+ }
+ }
+
+ ExtOut("Dumping GC log at %08x\n", dwAddr);
+
+ g_bDacBroken = FALSE;
+
+ ExtOut("Attempting to dump GC log to file '%s'\n", fileName);
+
+ Status = E_FAIL;
+
+ HANDLE hGCLog = CreateFileA(
+ fileName,
+ GENERIC_WRITE,
+ FILE_SHARE_READ,
+ NULL,
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (hGCLog == INVALID_HANDLE_VALUE)
+ {
+ ExtOut("failed to create file: %d\n", GetLastError());
+ goto exit;
+ }
+
+ int iLogSize = 1024*1024;
+ BYTE* bGCLog = new NOTHROW BYTE[iLogSize];
+ if (bGCLog == NULL)
+ {
+ ReportOOM();
+ goto exit;
+ }
+
+ memset (bGCLog, 0, iLogSize);
+ if (!SafeReadMemory(dwAddr, bGCLog, iLogSize, NULL))
+ {
+ ExtOut("failed to read memory from %08x\n", dwAddr);
+ }
+
+ int iRealLogSize = iLogSize - 1;
+ while (iRealLogSize >= 0)
+ {
+ if (bGCLog[iRealLogSize] != '*')
+ {
+ break;
+ }
+
+ iRealLogSize--;
+ }
+
+ DWORD dwWritten = 0;
+ WriteFile (hGCLog, bGCLog, iRealLogSize + 1, &dwWritten, NULL);
+
+ Status = S_OK;
+
+exit:
+
+ if (hGCLog != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle (hGCLog);
+ }
+
+ if (Status == S_OK)
+ ExtOut("SUCCESS: Stress log dumped\n");
+ else if (Status == S_FALSE)
+ ExtOut("No Stress log in the image, no file written\n");
+ else
+ ExtOut("FAILURE: Stress log not dumped\n");
+
+ return Status;
+}
+#endif //TRACE_GC
+
+#ifndef FEATURE_PAL
+DECLARE_API (DumpGCConfigLog)
+{
+ INIT_API();
+#ifdef GC_CONFIG_DRIVEN
+ MINIDUMP_NOT_SUPPORTED();
+
+ if (GetEEFlavor() == UNKNOWNEE)
+ {
+ ExtOut("CLR not loaded\n");
+ return Status;
+ }
+
+ const char* fileName = "GCConfigLog.txt";
+
+ while (isspace (*args))
+ args ++;
+
+ if (*args != 0)
+ fileName = args;
+
+ if (!InitializeHeapData ())
+ {
+ ExtOut("GC Heap not initialized yet.\n");
+ return S_OK;
+ }
+
+ BOOL fIsServerGC = IsServerBuild();
+
+ DWORD_PTR dwAddr = 0;
+ DWORD_PTR dwAddrOffset = 0;
+
+ if (fIsServerGC)
+ {
+ dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!SVR::gc_config_log_buffer");
+ dwAddrOffset = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!SVR::gc_config_log_buffer_offset");
+ }
+ else
+ {
+ dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!WKS::gc_config_log_buffer");
+ dwAddrOffset = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!WKS::gc_config_log_buffer_offset");
+ }
+
+ moveN (dwAddr, dwAddr);
+ moveN (dwAddrOffset, dwAddrOffset);
+
+ if (dwAddr == 0)
+ {
+ ExtOut("Can't get either WKS or SVR GC's config log buffer");
+ return E_FAIL;
+ }
+
+ ExtOut("Dumping GC log at %08x\n", dwAddr);
+
+ g_bDacBroken = FALSE;
+
+ ExtOut("Attempting to dump GC log to file '%s'\n", fileName);
+
+ Status = E_FAIL;
+
+ HANDLE hGCLog = CreateFileA(
+ fileName,
+ GENERIC_WRITE,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (hGCLog == INVALID_HANDLE_VALUE)
+ {
+ ExtOut("failed to create file: %d\n", GetLastError());
+ goto exit;
+ }
+
+ {
+ int iLogSize = (int)dwAddrOffset;
+
+ ArrayHolder<BYTE> bGCLog = new NOTHROW BYTE[iLogSize];
+ if (bGCLog == NULL)
+ {
+ ReportOOM();
+ goto exit;
+ }
+
+ memset (bGCLog, 0, iLogSize);
+ if (!SafeReadMemory(dwAddr, bGCLog, iLogSize, NULL))
+ {
+ ExtOut("failed to read memory from %08x\n", dwAddr);
+ }
+
+ SetFilePointer (hGCLog, 0, 0, FILE_END);
+ DWORD dwWritten;
+ WriteFile (hGCLog, bGCLog, iLogSize, &dwWritten, NULL);
+ }
+
+ Status = S_OK;
+
+exit:
+
+ if (hGCLog != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle (hGCLog);
+ }
+
+ if (Status == S_OK)
+ ExtOut("SUCCESS: Stress log dumped\n");
+ else if (Status == S_FALSE)
+ ExtOut("No Stress log in the image, no file written\n");
+ else
+ ExtOut("FAILURE: Stress log not dumped\n");
+
+ return Status;
+#else
+ ExtOut("Not implemented\n");
+ return S_OK;
+#endif //GC_CONFIG_DRIVEN
+}
+#endif // FEATURE_PAL
+
+#ifdef GC_CONFIG_DRIVEN
+static const char * const str_interesting_data_points[] =
+{
+ "pre short", // 0
+ "post short", // 1
+ "merged pins", // 2
+ "converted pins", // 3
+ "pre pin", // 4
+ "post pin", // 5
+ "pre and post pin", // 6
+ "pre short padded", // 7
+ "post short padded", // 7
+};
+
+static const char * const str_heap_compact_reasons[] =
+{
+ "low on ephemeral space",
+ "high fragmetation",
+ "couldn't allocate gaps",
+ "user specfied compact LOH",
+ "last GC before OOM",
+ "induced compacting GC",
+ "fragmented gen0 (ephemeral GC)",
+ "high memory load (ephemeral GC)",
+ "high memory load and frag",
+ "very high memory load and frag",
+ "no gc mode"
+};
+
+static BOOL gc_heap_compact_reason_mandatory_p[] =
+{
+ TRUE, //compact_low_ephemeral = 0,
+ FALSE, //compact_high_frag = 1,
+ TRUE, //compact_no_gaps = 2,
+ TRUE, //compact_loh_forced = 3,
+ TRUE, //compact_last_gc = 4
+ TRUE, //compact_induced_compacting = 5,
+ FALSE, //compact_fragmented_gen0 = 6,
+ FALSE, //compact_high_mem_load = 7,
+ TRUE, //compact_high_mem_frag = 8,
+ TRUE, //compact_vhigh_mem_frag = 9,
+ TRUE //compact_no_gc_mode = 10
+};
+
+static const char * const str_heap_expand_mechanisms[] =
+{
+ "reused seg with normal fit",
+ "reused seg with best fit",
+ "expand promoting eph",
+ "expand with a new seg",
+ "no memory for a new seg",
+ "expand in next full GC"
+};
+
+static const char * const str_bit_mechanisms[] =
+{
+ "using mark list",
+ "demotion"
+};
+
+static const char * const str_gc_global_mechanisms[] =
+{
+ "concurrent GCs",
+ "compacting GCs",
+ "promoting GCs",
+ "GCs that did demotion",
+ "card bundles",
+ "elevation logic"
+};
+
+void PrintInterestingGCInfo(DacpGCInterestingInfoData* dataPerHeap)
+{
+ ExtOut("Interesting data points\n");
+ size_t* data = dataPerHeap->interestingDataPoints;
+ for (int i = 0; i < NUM_GC_DATA_POINTS; i++)
+ {
+ ExtOut("%20s: %d\n", str_interesting_data_points[i], data[i]);
+ }
+
+ ExtOut("\nCompacting reasons\n");
+ data = dataPerHeap->compactReasons;
+ for (int i = 0; i < MAX_COMPACT_REASONS_COUNT; i++)
+ {
+ ExtOut("[%s]%35s: %d\n", (gc_heap_compact_reason_mandatory_p[i] ? "M" : "W"), str_heap_compact_reasons[i], data[i]);
+ }
+
+ ExtOut("\nExpansion mechanisms\n");
+ data = dataPerHeap->expandMechanisms;
+ for (int i = 0; i < MAX_EXPAND_MECHANISMS_COUNT; i++)
+ {
+ ExtOut("%30s: %d\n", str_heap_expand_mechanisms[i], data[i]);
+ }
+
+ ExtOut("\nOther mechanisms enabled\n");
+ data = dataPerHeap->bitMechanisms;
+ for (int i = 0; i < MAX_GC_MECHANISM_BITS_COUNT; i++)
+ {
+ ExtOut("%20s: %d\n", str_bit_mechanisms[i], data[i]);
+ }
+}
+#endif //GC_CONFIG_DRIVEN
+
+DECLARE_API(DumpGCData)
+{
+ INIT_API();
+
+#ifdef GC_CONFIG_DRIVEN
+ MINIDUMP_NOT_SUPPORTED();
+
+ if (!InitializeHeapData ())
+ {
+ ExtOut("GC Heap not initialized yet.\n");
+ return S_OK;
+ }
+
+ DacpGCInterestingInfoData interestingInfo;
+ interestingInfo.RequestGlobal(g_sos);
+ for (int i = 0; i < MAX_GLOBAL_GC_MECHANISMS_COUNT; i++)
+ {
+ ExtOut("%-30s: %d\n", str_gc_global_mechanisms[i], interestingInfo.globalMechanisms[i]);
+ }
+
+ ExtOut("\n[info per heap]\n");
+
+ if (!IsServerBuild())
+ {
+ if (interestingInfo.Request(g_sos) != S_OK)
+ {
+ ExtOut("Error requesting interesting GC info\n");
+ return E_FAIL;
+ }
+
+ PrintInterestingGCInfo(&interestingInfo);
+ }
+ else
+ {
+ DWORD dwNHeaps = GetGcHeapCount();
+ DWORD dwAllocSize;
+ if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
+ {
+ ExtOut("Failed to get GCHeaps: integer overflow\n");
+ return Status;
+ }
+
+ CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
+ if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
+ {
+ ExtOut("Failed to get GCHeaps\n");
+ return Status;
+ }
+
+ for (DWORD n = 0; n < dwNHeaps; n ++)
+ {
+ if (interestingInfo.Request(g_sos, heapAddrs[n]) != S_OK)
+ {
+ ExtOut("Heap %d: Error requesting interesting GC info\n", n);
+ return E_FAIL;
+ }
+
+ ExtOut("--------info for heap %d--------\n", n);
+ PrintInterestingGCInfo(&interestingInfo);
+ ExtOut("\n");
+ }
+ }
+
+ return S_OK;
+#else
+ ExtOut("Not implemented\n");
+ return S_OK;
+#endif //GC_CONFIG_DRIVEN
+}
+
+#ifndef FEATURE_PAL
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the build number and type of the *
+* mscoree.dll *
+* *
+\**********************************************************************/
+DECLARE_API (EEVersion)
+{
+ INIT_API();
+
+ EEFLAVOR eef = GetEEFlavor();
+ if (eef == UNKNOWNEE) {
+ ExtOut("CLR not loaded\n");
+ return Status;
+ }
+
+ if (g_ExtSymbols2) {
+ VS_FIXEDFILEINFO version;
+
+ BOOL ret = GetEEVersion(&version);
+
+ if (ret)
+ {
+ if (version.dwFileVersionMS != (DWORD)-1)
+ {
+ ExtOut("%u.%u.%u.%u",
+ HIWORD(version.dwFileVersionMS),
+ LOWORD(version.dwFileVersionMS),
+ HIWORD(version.dwFileVersionLS),
+ LOWORD(version.dwFileVersionLS));
+ if (version.dwFileFlags & VS_FF_DEBUG)
+ {
+ ExtOut(" Checked or debug build");
+ }
+ else
+ {
+ BOOL fRet = IsRetailBuild ((size_t)moduleInfo[eef].baseAddr);
+
+ if (fRet)
+ ExtOut(" retail");
+ else
+ ExtOut(" free");
+ }
+
+ ExtOut("\n");
+ }
+ }
+ }
+
+ if (!InitializeHeapData ())
+ ExtOut("GC Heap not initialized, so GC mode is not determined yet.\n");
+ else if (IsServerBuild())
+ ExtOut("Server mode with %d gc heaps\n", GetGcHeapCount());
+ else
+ ExtOut("Workstation mode\n");
+
+ if (!GetGcStructuresValid())
+ {
+ ExtOut("In plan phase of garbage collection\n");
+ }
+
+ // Print SOS version
+ VS_FIXEDFILEINFO sosVersion;
+ if (GetSOSVersion(&sosVersion))
+ {
+ if (sosVersion.dwFileVersionMS != (DWORD)-1)
+ {
+ ExtOut("SOS Version: %u.%u.%u.%u",
+ HIWORD(sosVersion.dwFileVersionMS),
+ LOWORD(sosVersion.dwFileVersionMS),
+ HIWORD(sosVersion.dwFileVersionLS),
+ LOWORD(sosVersion.dwFileVersionLS));
+ if (sosVersion.dwFileFlags & VS_FF_DEBUG)
+ {
+ ExtOut(" Checked or debug build");
+ }
+ else
+ {
+ ExtOut(" retail build");
+ }
+
+ ExtOut("\n");
+ }
+ }
+ return Status;
+}
+#endif // FEATURE_PAL
+
+#ifndef FEATURE_PAL
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to print the environment setting for *
+* the current process. *
+* *
+\**********************************************************************/
+DECLARE_API (ProcInfo)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ if (IsDumpFile())
+ {
+ ExtOut("!ProcInfo is not supported on a dump file.\n");
+ return Status;
+ }
+
+#define INFO_ENV 0x00000001
+#define INFO_TIME 0x00000002
+#define INFO_MEM 0x00000004
+#define INFO_ALL 0xFFFFFFFF
+
+ DWORD fProcInfo = INFO_ALL;
+
+ if (_stricmp (args, "-env") == 0) {
+ fProcInfo = INFO_ENV;
+ }
+
+ if (_stricmp (args, "-time") == 0) {
+ fProcInfo = INFO_TIME;
+ }
+
+ if (_stricmp (args, "-mem") == 0) {
+ fProcInfo = INFO_MEM;
+ }
+
+ if (fProcInfo & INFO_ENV) {
+ ExtOut("---------------------------------------\n");
+ ExtOut("Environment\n");
+ ULONG64 pPeb;
+ g_ExtSystem->GetCurrentProcessPeb(&pPeb);
+
+ static ULONG Offset_ProcessParam = -1;
+ static ULONG Offset_Environment = -1;
+ if (Offset_ProcessParam == -1)
+ {
+ ULONG TypeId;
+ ULONG64 NtDllBase;
+ if (SUCCEEDED(g_ExtSymbols->GetModuleByModuleName ("ntdll",0,NULL,
+ &NtDllBase)))
+ {
+ if (SUCCEEDED(g_ExtSymbols->GetTypeId (NtDllBase, "PEB", &TypeId)))
+ {
+ if (FAILED (g_ExtSymbols->GetFieldOffset(NtDllBase, TypeId,
+ "ProcessParameters", &Offset_ProcessParam)))
+ Offset_ProcessParam = -1;
+ }
+ if (SUCCEEDED(g_ExtSymbols->GetTypeId (NtDllBase, "_RTL_USER_PROCESS_PARAMETERS", &TypeId)))
+ {
+ if (FAILED (g_ExtSymbols->GetFieldOffset(NtDllBase, TypeId,
+ "Environment", &Offset_Environment)))
+ Offset_Environment = -1;
+ }
+ }
+ }
+ // We can not get it from PDB. Use the fixed one.
+ if (Offset_ProcessParam == -1)
+ Offset_ProcessParam = offsetof (DT_PEB, ProcessParameters);
+
+ if (Offset_Environment == -1)
+ Offset_Environment = offsetof (DT_RTL_USER_PROCESS_PARAMETERS, Environment);
+
+
+ ULONG64 addr = pPeb + Offset_ProcessParam;
+ DWORD_PTR value;
+ g_ExtData->ReadVirtual(UL64_TO_CDA(addr), &value, sizeof(PVOID), NULL);
+ addr = value + Offset_Environment;
+ g_ExtData->ReadVirtual(UL64_TO_CDA(addr), &value, sizeof(PVOID), NULL);
+
+ static WCHAR buffer[DT_OS_PAGE_SIZE/2];
+ ULONG readBytes = DT_OS_PAGE_SIZE;
+ ULONG64 Page;
+ if ((g_ExtData->ReadDebuggerData( DEBUG_DATA_MmPageSize, &Page, sizeof(Page), NULL)) == S_OK
+ && Page > 0)
+ {
+ ULONG uPageSize = (ULONG)(ULONG_PTR)Page;
+ if (readBytes > uPageSize) {
+ readBytes = uPageSize;
+ }
+ }
+ addr = value;
+ while (1) {
+ if (IsInterrupt())
+ return Status;
+ if (FAILED(g_ExtData->ReadVirtual(UL64_TO_CDA(addr), &buffer, readBytes, NULL)))
+ break;
+ addr += readBytes;
+ WCHAR *pt = buffer;
+ WCHAR *end = pt;
+ while (pt < &buffer[DT_OS_PAGE_SIZE/2]) {
+ end = _wcschr (pt, L'\0');
+ if (end == NULL) {
+ char format[20];
+ sprintf_s (format,_countof (format), "%dS", &buffer[DT_OS_PAGE_SIZE/2] - pt);
+ ExtOut(format, pt);
+ break;
+ }
+ else if (end == pt) {
+ break;
+ }
+ ExtOut("%S\n", pt);
+ pt = end + 1;
+ }
+ if (end == pt) {
+ break;
+ }
+ }
+ }
+
+ HANDLE hProcess = INVALID_HANDLE_VALUE;
+ if (fProcInfo & (INFO_TIME | INFO_MEM)) {
+ ULONG64 handle;
+ g_ExtSystem->GetCurrentProcessHandle(&handle);
+ hProcess = (HANDLE)handle;
+ }
+
+ if (!IsDumpFile() && fProcInfo & INFO_TIME) {
+ FILETIME CreationTime;
+ FILETIME ExitTime;
+ FILETIME KernelTime;
+ FILETIME UserTime;
+
+ typedef BOOL (WINAPI *FntGetProcessTimes)(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME, LPFILETIME);
+ static FntGetProcessTimes pFntGetProcessTimes = (FntGetProcessTimes)-1;
+ if (pFntGetProcessTimes == (FntGetProcessTimes)-1) {
+ HINSTANCE hstat = LoadLibrary ("Kernel32.dll");
+ if (hstat != 0)
+ {
+ pFntGetProcessTimes = (FntGetProcessTimes)GetProcAddress (hstat, "GetProcessTimes");
+ FreeLibrary (hstat);
+ }
+ else
+ pFntGetProcessTimes = NULL;
+ }
+
+ if (pFntGetProcessTimes && pFntGetProcessTimes (hProcess,&CreationTime,&ExitTime,&KernelTime,&UserTime)) {
+ ExtOut("---------------------------------------\n");
+ ExtOut("Process Times\n");
+ static char *Month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
+ "Oct", "Nov", "Dec"};
+ SYSTEMTIME SystemTime;
+ FILETIME LocalFileTime;
+ if (FileTimeToLocalFileTime (&CreationTime,&LocalFileTime)
+ && FileTimeToSystemTime (&LocalFileTime,&SystemTime)) {
+ ExtOut("Process Started at: %4d %s %2d %d:%d:%d.%02d\n",
+ SystemTime.wYear, Month[SystemTime.wMonth-1], SystemTime.wDay,
+ SystemTime.wHour, SystemTime.wMinute,
+ SystemTime.wSecond, SystemTime.wMilliseconds/10);
+ }
+
+ DWORD nDay = 0;
+ DWORD nHour = 0;
+ DWORD nMin = 0;
+ DWORD nSec = 0;
+ DWORD nHundred = 0;
+
+ ULONG64 totalTime;
+
+ totalTime = KernelTime.dwLowDateTime + (((ULONG64)KernelTime.dwHighDateTime) << 32);
+ nDay = (DWORD)(totalTime/(24*3600*10000000ui64));
+ totalTime %= 24*3600*10000000ui64;
+ nHour = (DWORD)(totalTime/(3600*10000000ui64));
+ totalTime %= 3600*10000000ui64;
+ nMin = (DWORD)(totalTime/(60*10000000));
+ totalTime %= 60*10000000;
+ nSec = (DWORD)(totalTime/10000000);
+ totalTime %= 10000000;
+ nHundred = (DWORD)(totalTime/100000);
+ ExtOut("Kernel CPU time : %d days %02d:%02d:%02d.%02d\n",
+ nDay, nHour, nMin, nSec, nHundred);
+
+ DWORD sDay = nDay;
+ DWORD sHour = nHour;
+ DWORD sMin = nMin;
+ DWORD sSec = nSec;
+ DWORD sHundred = nHundred;
+
+ totalTime = UserTime.dwLowDateTime + (((ULONG64)UserTime.dwHighDateTime) << 32);
+ nDay = (DWORD)(totalTime/(24*3600*10000000ui64));
+ totalTime %= 24*3600*10000000ui64;
+ nHour = (DWORD)(totalTime/(3600*10000000ui64));
+ totalTime %= 3600*10000000ui64;
+ nMin = (DWORD)(totalTime/(60*10000000));
+ totalTime %= 60*10000000;
+ nSec = (DWORD)(totalTime/10000000);
+ totalTime %= 10000000;
+ nHundred = (DWORD)(totalTime/100000);
+ ExtOut("User CPU time : %d days %02d:%02d:%02d.%02d\n",
+ nDay, nHour, nMin, nSec, nHundred);
+
+ sDay += nDay;
+ sHour += nHour;
+ sMin += nMin;
+ sSec += nSec;
+ sHundred += nHundred;
+ if (sHundred >= 100) {
+ sSec += sHundred/100;
+ sHundred %= 100;
+ }
+ if (sSec >= 60) {
+ sMin += sSec/60;
+ sSec %= 60;
+ }
+ if (sMin >= 60) {
+ sHour += sMin/60;
+ sMin %= 60;
+ }
+ if (sHour >= 24) {
+ sDay += sHour/24;
+ sHour %= 24;
+ }
+ ExtOut("Total CPU time : %d days %02d:%02d:%02d.%02d\n",
+ sDay, sHour, sMin, sSec, sHundred);
+ }
+ }
+
+ if (!IsDumpFile() && fProcInfo & INFO_MEM) {
+ typedef
+ NTSTATUS
+ (NTAPI
+ *FntNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
+
+ static FntNtQueryInformationProcess pFntNtQueryInformationProcess = (FntNtQueryInformationProcess)-1;
+ if (pFntNtQueryInformationProcess == (FntNtQueryInformationProcess)-1) {
+ HINSTANCE hstat = LoadLibrary ("ntdll.dll");
+ if (hstat != 0)
+ {
+ pFntNtQueryInformationProcess = (FntNtQueryInformationProcess)GetProcAddress (hstat, "NtQueryInformationProcess");
+ FreeLibrary (hstat);
+ }
+ else
+ pFntNtQueryInformationProcess = NULL;
+ }
+ VM_COUNTERS memory;
+ if (pFntNtQueryInformationProcess &&
+ NT_SUCCESS (pFntNtQueryInformationProcess (hProcess,ProcessVmCounters,&memory,sizeof(memory),NULL))) {
+ ExtOut("---------------------------------------\n");
+ ExtOut("Process Memory\n");
+ ExtOut("WorkingSetSize: %8d KB PeakWorkingSetSize: %8d KB\n",
+ memory.WorkingSetSize/1024, memory.PeakWorkingSetSize/1024);
+ ExtOut("VirtualSize: %8d KB PeakVirtualSize: %8d KB\n",
+ memory.VirtualSize/1024, memory.PeakVirtualSize/1024);
+ ExtOut("PagefileUsage: %8d KB PeakPagefileUsage: %8d KB\n",
+ memory.PagefileUsage/1024, memory.PeakPagefileUsage/1024);
+ }
+
+ MEMORYSTATUS memstat;
+ GlobalMemoryStatus (&memstat);
+ ExtOut("---------------------------------------\n");
+ ExtOut("%ld percent of memory is in use.\n\n",
+ memstat.dwMemoryLoad);
+ ExtOut("Memory Availability (Numbers in MB)\n\n");
+ ExtOut(" %8s %8s\n", "Total", "Avail");
+ ExtOut("Physical Memory %8d %8d\n", memstat.dwTotalPhys/1024/1024, memstat.dwAvailPhys/1024/1024);
+ ExtOut("Page File %8d %8d\n", memstat.dwTotalPageFile/1024/1024, memstat.dwAvailPageFile/1024/1024);
+ ExtOut("Virtual Memory %8d %8d\n", memstat.dwTotalVirtual/1024/1024, memstat.dwAvailVirtual/1024/1024);
+ }
+
+ return Status;
+}
+#endif // FEATURE_PAL
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to find the address of EE data for a *
+* metadata token. *
+* *
+\**********************************************************************/
+DECLARE_API(Token2EE)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ StringHolder DllName;
+ ULONG64 token = 0;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE},
+#endif
+ };
+
+ CMDValue arg[] =
+ { // vptr, type
+ {&DllName.data, COSTRING},
+ {&token, COHEX}
+ };
+
+ size_t nArg;
+ if (!GetCMDOption(args,option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+ if (nArg!=2)
+ {
+ ExtOut("Usage: !Token2EE module_name mdToken\n");
+ ExtOut(" You can pass * for module_name to search all modules.\n");
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ int numModule;
+ ArrayHolder<DWORD_PTR> moduleList = NULL;
+
+ if (strcmp(DllName.data, "*") == 0)
+ {
+ moduleList = ModuleFromName(NULL, &numModule);
+ }
+ else
+ {
+ moduleList = ModuleFromName(DllName.data, &numModule);
+ }
+
+ if (moduleList == NULL)
+ {
+ ExtOut("Failed to request module list.\n");
+ }
+ else
+ {
+ for (int i = 0; i < numModule; i ++)
+ {
+ if (IsInterrupt())
+ break;
+
+ if (i > 0)
+ {
+ ExtOut("--------------------------------------\n");
+ }
+
+ DWORD_PTR dwAddr = moduleList[i];
+ WCHAR FileName[MAX_LONGPATH];
+ FileNameForModule(dwAddr, FileName);
+
+ // We'd like a short form for this output
+ LPWSTR pszFilename = _wcsrchr (FileName, DIRECTORY_SEPARATOR_CHAR_W);
+ if (pszFilename == NULL)
+ {
+ pszFilename = FileName;
+ }
+ else
+ {
+ pszFilename++; // skip past the last "\" character
+ }
+
+ DMLOut("Module: %s\n", DMLModule(dwAddr));
+ ExtOut("Assembly: %S\n", pszFilename);
+
+ GetInfoFromModule(dwAddr, (ULONG)token);
+ }
+ }
+
+ return Status;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to find the address of EE data for a *
+* metadata token. *
+* *
+\**********************************************************************/
+DECLARE_API(Name2EE)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ StringHolder DllName, TypeName;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE},
+#endif
+ };
+
+ CMDValue arg[] =
+ { // vptr, type
+ {&DllName.data, COSTRING},
+ {&TypeName.data, COSTRING}
+ };
+ size_t nArg;
+
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+
+ if (nArg == 1)
+ {
+ // The input may be in the form <modulename>!<type>
+ // If so, do some surgery on the input params.
+
+ // There should be only 1 ! character
+ LPSTR pszSeperator = strchr (DllName.data, '!');
+ if (pszSeperator != NULL)
+ {
+ if (strchr (pszSeperator + 1, '!') == NULL)
+ {
+ size_t capacity_TypeName_data = strlen(pszSeperator + 1) + 1;
+ TypeName.data = new NOTHROW char[capacity_TypeName_data];
+ if (TypeName.data)
+ {
+ // get the type name,
+ strcpy_s (TypeName.data, capacity_TypeName_data, pszSeperator + 1);
+ // and truncate DllName
+ *pszSeperator = '\0';
+
+ // Do some extra validation
+ if (strlen (DllName.data) >= 1 &&
+ strlen (TypeName.data) > 1)
+ {
+ nArg = 2;
+ }
+ }
+ }
+ }
+ }
+
+ if (nArg != 2)
+ {
+ ExtOut("Usage: " SOSPrefix "name2ee module_name item_name\n");
+ ExtOut(" or " SOSPrefix "name2ee module_name!item_name\n");
+ ExtOut(" use * for module_name to search all loaded modules\n");
+ ExtOut("Examples: " SOSPrefix "name2ee mscorlib.dll System.String.ToString\n");
+ ExtOut(" " SOSPrefix "name2ee *!System.String\n");
+ return Status;
+ }
+
+ int numModule;
+ ArrayHolder<DWORD_PTR> moduleList = NULL;
+ if (strcmp(DllName.data, "*") == 0)
+ {
+ moduleList = ModuleFromName(NULL, &numModule);
+ }
+ else
+ {
+ moduleList = ModuleFromName(DllName.data, &numModule);
+ }
+
+
+ if (moduleList == NULL)
+ {
+ ExtOut("Failed to request module list.\n", DllName.data);
+ }
+ else
+ {
+ for (int i = 0; i < numModule; i ++)
+ {
+ if (IsInterrupt())
+ break;
+
+ if (i > 0)
+ {
+ ExtOut("--------------------------------------\n");
+ }
+
+ DWORD_PTR dwAddr = moduleList[i];
+ WCHAR FileName[MAX_LONGPATH];
+ FileNameForModule (dwAddr, FileName);
+
+ // We'd like a short form for this output
+ LPWSTR pszFilename = _wcsrchr (FileName, DIRECTORY_SEPARATOR_CHAR_W);
+ if (pszFilename == NULL)
+ {
+ pszFilename = FileName;
+ }
+ else
+ {
+ pszFilename++; // skip past the last "\" character
+ }
+
+ DMLOut("Module: %s\n", DMLModule(dwAddr));
+ ExtOut("Assembly: %S\n", pszFilename);
+ GetInfoFromName(dwAddr, TypeName.data);
+ }
+ }
+
+ return Status;
+}
+
+
+#ifndef FEATURE_PAL
+DECLARE_API(PathTo)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ DWORD_PTR root = NULL;
+ DWORD_PTR target = NULL;
+ BOOL dml = FALSE;
+ size_t nArg;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"/d", &dml, COBOOL, FALSE},
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&root, COHEX},
+ {&target, COHEX},
+ };
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ if (root == 0 || target == 0)
+ {
+ ExtOut("Invalid argument %s\n", args);
+ return Status;
+ }
+
+ GCRootImpl gcroot;
+ bool result = gcroot.PrintPathToObject(root, target);
+
+ if (!result)
+ ExtOut("Did not find a path from %p to %p.\n", SOS_PTR(root), SOS_PTR(target));
+
+ return Status;
+}
+#endif
+
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function finds all roots (on stack or in handles) for a *
+* given object. *
+* *
+\**********************************************************************/
+DECLARE_API(GCRoot)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ BOOL bNoStacks = FALSE;
+ DWORD_PTR obj = NULL;
+ BOOL dml = FALSE;
+ BOOL all = FALSE;
+ size_t nArg;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-nostacks", &bNoStacks, COBOOL, FALSE},
+ {"-all", &all, COBOOL, FALSE},
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE},
+#endif
+ };
+ CMDValue arg[] =
+
+ { // vptr, type
+ {&obj, COHEX}
+ };
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+ if (obj == 0)
+ {
+ ExtOut("Invalid argument %s\n", args);
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ GCRootImpl gcroot;
+ int i = gcroot.PrintRootsForObject(obj, all == TRUE, bNoStacks == TRUE);
+
+ if (IsInterrupt())
+ ExtOut("Interrupted, data may be incomplete.\n");
+
+ if (all)
+ ExtOut("Found %d roots.\n", i);
+ else
+ ExtOut("Found %d unique roots (run '!GCRoot -all' to see all roots).\n", i);
+
+ return Status;
+}
+
+DECLARE_API(GCWhere)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ BOOL dml = FALSE;
+ BOOL bGetBrick;
+ BOOL bGetCard;
+ TADDR taddrObj = 0;
+ size_t nArg;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-brick", &bGetBrick, COBOOL, FALSE},
+ {"-card", &bGetCard, COBOOL, FALSE},
+ {"/d", &dml, COBOOL, FALSE},
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&taddrObj, COHEX}
+ };
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ // Obtain allocation context for each managed thread.
+ AllocInfo allocInfo;
+ allocInfo.Init();
+
+ TADDR_SEGINFO trngSeg = { 0, 0, 0 };
+ TADDR_RANGE allocCtx = { 0, 0 };
+ int gen = -1;
+ BOOL bLarge = FALSE;
+ BOOL bFound = FALSE;
+
+ size_t size = 0;
+ if (sos::IsObject(taddrObj))
+ {
+ TADDR taddrMT;
+ BOOL bContainsPointers;
+ if(FAILED(GetMTOfObject(taddrObj, &taddrMT)) ||
+ !GetSizeEfficient(taddrObj, taddrMT, FALSE, size, bContainsPointers))
+ {
+ ExtWarn("Couldn't get size for object %#p: possible heap corruption.\n",
+ SOS_PTR(taddrObj));
+ }
+ }
+
+ if (!IsServerBuild())
+ {
+ DacpGcHeapDetails heapDetails;
+ if (heapDetails.Request(g_sos) != S_OK)
+ {
+ ExtOut("Error requesting gc heap details\n");
+ return Status;
+ }
+
+ if (GCObjInHeap(taddrObj, heapDetails, trngSeg, gen, allocCtx, bLarge))
+ {
+ ExtOut("Address " WIN64_8SPACES " Gen Heap segment " WIN64_8SPACES " begin " WIN64_8SPACES " allocated " WIN64_8SPACES " size\n");
+ ExtOut("%p %d %2d %p %p %p 0x%x(%d)\n",
+ SOS_PTR(taddrObj), gen, 0, SOS_PTR(trngSeg.segAddr), SOS_PTR(trngSeg.start), SOS_PTR(trngSeg.end), size, size);
+ bFound = TRUE;
+ }
+ }
+ else
+ {
+ DacpGcHeapData gcheap;
+ if (gcheap.Request(g_sos) != S_OK)
+ {
+ ExtOut("Error requesting GC Heap data\n");
+ return Status;
+ }
+
+ DWORD dwAllocSize;
+ DWORD dwNHeaps = gcheap.HeapCount;
+ if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
+ {
+ ExtOut("Failed to get GCHeaps: integer overflow\n");
+ return Status;
+ }
+
+ CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
+ if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
+ {
+ ExtOut("Failed to get GCHeaps\n");
+ return Status;
+ }
+
+ for (DWORD n = 0; n < dwNHeaps; n ++)
+ {
+ DacpGcHeapDetails heapDetails;
+ if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
+ {
+ ExtOut("Error requesting details\n");
+ return Status;
+ }
+
+ if (GCObjInHeap(taddrObj, heapDetails, trngSeg, gen, allocCtx, bLarge))
+ {
+ ExtOut("Address " WIN64_8SPACES " Gen Heap segment " WIN64_8SPACES " begin " WIN64_8SPACES " allocated" WIN64_8SPACES " size\n");
+ ExtOut("%p %d %2d %p %p %p 0x%x(%d)\n",
+ SOS_PTR(taddrObj), gen, n, SOS_PTR(trngSeg.segAddr), SOS_PTR(trngSeg.start), SOS_PTR(trngSeg.end), size, size);
+ bFound = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (!bFound)
+ {
+ ExtOut("Address %#p not found in the managed heap.\n", SOS_PTR(taddrObj));
+ }
+
+ return Status;
+}
+
+#ifndef FEATURE_PAL
+
+DECLARE_API(FindRoots)
+{
+#ifndef FEATURE_PAL
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ if (IsDumpFile())
+ {
+ ExtOut("!FindRoots is not supported on a dump file.\n");
+ return Status;
+ }
+
+ LONG_PTR gen = -100; // initialized outside the legal range: [-1, 2]
+ StringHolder sgen;
+ TADDR taObj = NULL;
+ BOOL dml = FALSE;
+ size_t nArg;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-gen", &sgen.data, COSTRING, TRUE},
+ {"/d", &dml, COBOOL, FALSE},
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&taObj, COHEX}
+ };
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ if (sgen.data != NULL)
+ {
+ if (_stricmp(sgen.data, "any") == 0)
+ {
+ gen = -1;
+ }
+ else
+ {
+ gen = GetExpression(sgen.data);
+ }
+ }
+ if ((gen < -1 || gen > 2) && (taObj == 0))
+ {
+ ExtOut("Incorrect options. Usage:\n\t!FindRoots -gen <N>\n\t\twhere N is 0, 1, 2, or \"any\". OR\n\t!FindRoots <obj>\n");
+ return Status;
+ }
+
+ if (gen >= -1 && gen <= 2)
+ {
+ IXCLRDataProcess2* idp2 = NULL;
+ if (FAILED(g_clrData->QueryInterface(IID_IXCLRDataProcess2, (void**) &idp2)))
+ {
+ ExtOut("Your version of the runtime/DAC do not support this command.\n");
+ return Status;
+ }
+
+ // Request GC_MARK_END notifications from debuggee
+ GcEvtArgs gea = { GC_MARK_END, { ((gen == -1) ? 7 : (1 << gen)) } };
+ idp2->SetGcNotification(gea);
+ // ... and register the notification handler
+ g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "sxe -c \"!HandleCLRN\" clrn", 0);
+ // the above notification is removed in CNotification::OnGcEvent()
+ }
+ else
+ {
+ // verify that the last event in the debugger was indeed a CLRN exception
+ DEBUG_LAST_EVENT_INFO_EXCEPTION dle;
+ CNotification Notification;
+
+ if (!CheckCLRNotificationEvent(&dle))
+ {
+ ExtOut("The command !FindRoots can only be used after the debugger stopped on a CLRN GC notification.\n");
+ ExtOut("At this time !GCRoot should be used instead.\n");
+ return Status;
+ }
+ // validate argument
+ if (!g_snapshot.Build())
+ {
+ ExtOut("Unable to build snapshot of the garbage collector state\n");
+ return Status;
+ }
+
+ if (g_snapshot.GetHeap(taObj) == NULL)
+ {
+ ExtOut("Address %#p is not in the managed heap.\n", SOS_PTR(taObj));
+ return Status;
+ }
+
+ int ogen = g_snapshot.GetGeneration(taObj);
+ if (ogen > CNotification::GetCondemnedGen())
+ {
+ DMLOut("Object %s will survive this collection:\n\tgen(%#p) = %d > %d = condemned generation.\n",
+ DMLObject(taObj), SOS_PTR(taObj), ogen, CNotification::GetCondemnedGen());
+ return Status;
+ }
+
+ GCRootImpl gcroot;
+ int roots = gcroot.FindRoots(CNotification::GetCondemnedGen(), taObj);
+
+ ExtOut("Found %d roots.\n", roots);
+ }
+
+ return Status;
+#else
+ return E_NOTIMPL;
+#endif
+}
+
+class GCHandleStatsForDomains
+{
+public:
+ const static int SHARED_DOMAIN_INDEX = 0;
+ const static int SYSTEM_DOMAIN_INDEX = 1;
+
+ GCHandleStatsForDomains()
+ : m_singleDomainMode(FALSE), m_numDomains(0), m_pStatistics(NULL), m_pDomainPointers(NULL)
+ {
+ }
+
+ ~GCHandleStatsForDomains()
+ {
+ if (m_pStatistics)
+ {
+ if (m_singleDomainMode)
+ delete m_pStatistics;
+ else
+ delete [] m_pStatistics;
+ }
+
+ if (m_pDomainPointers)
+ delete [] m_pDomainPointers;
+ }
+
+ BOOL Init(BOOL singleDomainMode)
+ {
+ m_singleDomainMode = singleDomainMode;
+ if (m_singleDomainMode)
+ {
+ m_numDomains = 1;
+ m_pStatistics = new NOTHROW GCHandleStatistics();
+ if (m_pStatistics == NULL)
+ return FALSE;
+ }
+ else
+ {
+ DacpAppDomainStoreData adsData;
+ if (adsData.Request(g_sos) != S_OK)
+ return FALSE;
+
+ m_numDomains = adsData.DomainCount + 2;
+ ArrayHolder<CLRDATA_ADDRESS> pArray = new NOTHROW CLRDATA_ADDRESS[adsData.DomainCount + 2];
+ if (pArray == NULL)
+ return FALSE;
+
+ pArray[SHARED_DOMAIN_INDEX] = adsData.sharedDomain;
+ pArray[SYSTEM_DOMAIN_INDEX] = adsData.systemDomain;
+
+ if (g_sos->GetAppDomainList(adsData.DomainCount, pArray+2, NULL) != S_OK)
+ return FALSE;
+
+ m_pDomainPointers = pArray.Detach();
+ m_pStatistics = new NOTHROW GCHandleStatistics[adsData.DomainCount + 2];
+ if (m_pStatistics == NULL)
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ GCHandleStatistics *LookupStatistics(CLRDATA_ADDRESS appDomainPtr) const
+ {
+ if (m_singleDomainMode)
+ {
+ // You can pass NULL appDomainPtr if you are in singleDomainMode
+ return m_pStatistics;
+ }
+ else
+ {
+ for (int i=0; i < m_numDomains; i++)
+ if (m_pDomainPointers[i] == appDomainPtr)
+ return m_pStatistics + i;
+ }
+
+ return NULL;
+ }
+
+
+ GCHandleStatistics *GetStatistics(int appDomainIndex) const
+ {
+ SOS_Assert(appDomainIndex >= 0);
+ SOS_Assert(appDomainIndex < m_numDomains);
+
+ return m_singleDomainMode ? m_pStatistics : m_pStatistics + appDomainIndex;
+ }
+
+ int GetNumDomains() const
+ {
+ return m_numDomains;
+ }
+
+ CLRDATA_ADDRESS GetDomain(int index) const
+ {
+ SOS_Assert(index >= 0);
+ SOS_Assert(index < m_numDomains);
+ return m_pDomainPointers[index];
+ }
+
+private:
+ BOOL m_singleDomainMode;
+ int m_numDomains;
+ GCHandleStatistics *m_pStatistics;
+ CLRDATA_ADDRESS *m_pDomainPointers;
+};
+
+class GCHandlesImpl
+{
+public:
+ GCHandlesImpl(PCSTR args)
+ : mPerDomain(FALSE), mStat(FALSE), mDML(FALSE), mType((int)~0)
+ {
+ ArrayHolder<char> type = NULL;
+ CMDOption option[] =
+ {
+ {"-perdomain", &mPerDomain, COBOOL, FALSE},
+ {"-stat", &mStat, COBOOL, FALSE},
+ {"-type", &type, COSTRING, TRUE},
+ {"/d", &mDML, COBOOL, FALSE},
+ };
+
+ if (!GetCMDOption(args,option,_countof(option),NULL,0,NULL))
+ sos::Throw<sos::Exception>("Failed to parse command line arguments.");
+
+ if (type != NULL)
+ if (_stricmp(type, "Pinned") == 0)
+ mType = HNDTYPE_PINNED;
+ else if (_stricmp(type, "RefCounted") == 0)
+ mType = HNDTYPE_REFCOUNTED;
+ else if (_stricmp(type, "WeakShort") == 0)
+ mType = HNDTYPE_WEAK_SHORT;
+ else if (_stricmp(type, "WeakLong") == 0)
+ mType = HNDTYPE_WEAK_LONG;
+ else if (_stricmp(type, "Strong") == 0)
+ mType = HNDTYPE_STRONG;
+ else if (_stricmp(type, "Variable") == 0)
+ mType = HNDTYPE_VARIABLE;
+ else if (_stricmp(type, "AsyncPinned") == 0)
+ mType = HNDTYPE_ASYNCPINNED;
+ else if (_stricmp(type, "SizedRef") == 0)
+ mType = HNDTYPE_SIZEDREF;
+ else if (_stricmp(type, "Dependent") == 0)
+ mType = HNDTYPE_DEPENDENT;
+ else if (_stricmp(type, "WeakWinRT") == 0)
+ mType = HNDTYPE_WEAK_WINRT;
+ else
+ sos::Throw<sos::Exception>("Unknown handle type '%s'.", type.GetPtr());
+ }
+
+ void Run()
+ {
+ EnableDMLHolder dmlHolder(mDML);
+
+ mOut.ReInit(6, POINTERSIZE_HEX, AlignRight);
+ mOut.SetWidths(5, POINTERSIZE_HEX, 11, POINTERSIZE_HEX, 8, POINTERSIZE_HEX);
+ mOut.SetColAlignment(1, AlignLeft);
+
+ if (mHandleStat.Init(!mPerDomain) == FALSE)
+ sos::Throw<sos::Exception>("Error getting per-appdomain handle information");
+
+ if (!mStat)
+ mOut.WriteRow("Handle", "Type", "Object", "Size", "Data", "Type");
+
+ WalkHandles();
+
+ for (int i=0; (i < mHandleStat.GetNumDomains()) && !IsInterrupt(); i++)
+ {
+ GCHandleStatistics *pStats = mHandleStat.GetStatistics(i);
+
+ if (mPerDomain)
+ {
+ Print( "------------------------------------------------------------------------------\n");
+ Print("GC Handle Statistics for AppDomain ", AppDomainPtr(mHandleStat.GetDomain(i)));
+
+ if (i == GCHandleStatsForDomains::SHARED_DOMAIN_INDEX)
+ Print(" (Shared Domain)\n");
+ else if (i == GCHandleStatsForDomains::SYSTEM_DOMAIN_INDEX)
+ Print(" (System Domain)\n");
+ else
+ Print("\n");
+ }
+
+ if (!mStat)
+ Print("\n");
+ PrintGCStat(&pStats->hs);
+
+ // Don't print handle stats if the user has filtered by type. All handles will be the same
+ // type, and the total count will be displayed by PrintGCStat.
+ if (mType == (unsigned int)~0)
+ {
+ Print("\n");
+ PrintGCHandleStats(pStats);
+ }
+ }
+ }
+
+private:
+ void WalkHandles()
+ {
+ ToRelease<ISOSHandleEnum> handles;
+ if (FAILED(g_sos->GetHandleEnum(&handles)))
+ {
+ if (IsMiniDumpFile())
+ sos::Throw<sos::Exception>("Unable to display GC handles.\nA minidump without full memory may not have this information.");
+ else
+ sos::Throw<sos::Exception>("Failed to walk the handle table.");
+ }
+
+ // GCC can't handle stacks which are too large.
+#ifndef FEATURE_PAL
+ SOSHandleData data[256];
+#else
+ SOSHandleData data[4];
+#endif
+
+ unsigned int fetched = 0;
+ HRESULT hr = S_OK;
+ do
+ {
+ if (FAILED(hr = handles->Next(_countof(data), data, &fetched)))
+ {
+ ExtOut("Error %x while walking the handle table.\n", hr);
+ break;
+ }
+
+ WalkHandles(data, fetched);
+ } while (_countof(data) == fetched);
+ }
+
+ void WalkHandles(SOSHandleData data[], unsigned int count)
+ {
+ for (unsigned int i = 0; i < count; ++i)
+ {
+ sos::CheckInterrupt();
+
+ if (mType != (unsigned int)~0 && mType != data[i].Type)
+ continue;
+
+ GCHandleStatistics *pStats = mHandleStat.LookupStatistics(data[i].AppDomain);
+ TADDR objAddr = 0;
+ TADDR mtAddr = 0;
+ size_t size = 0;
+ const WCHAR *mtName = 0;
+ const char *type = 0;
+
+ if (FAILED(MOVE(objAddr, data[i].Handle)))
+ {
+ objAddr = 0;
+ mtName = W("<error>");
+ }
+ else
+ {
+ sos::Object obj(TO_TADDR(objAddr));
+ mtAddr = obj.GetMT();
+ if (sos::MethodTable::IsFreeMT(mtAddr))
+ {
+ mtName = W("<free>");
+ }
+ else if (!sos::MethodTable::IsValid(mtAddr))
+ {
+ mtName = W("<error>");
+ }
+ else
+ {
+ size = obj.GetSize();
+ if (mType == (unsigned int)~0 || mType == data[i].Type)
+ pStats->hs.Add(obj.GetMT(), (DWORD)size);
+ }
+ }
+
+ switch(data[i].Type)
+ {
+ case HNDTYPE_PINNED:
+ type = "Pinned";
+ if (pStats) pStats->pinnedHandleCount++;
+ break;
+ case HNDTYPE_REFCOUNTED:
+ type = "RefCounted";
+ if (pStats) pStats->refCntHandleCount++;
+ break;
+ case HNDTYPE_STRONG:
+ type = "Strong";
+ if (pStats) pStats->strongHandleCount++;
+ break;
+ case HNDTYPE_WEAK_SHORT:
+ type = "WeakShort";
+ if (pStats) pStats->weakShortHandleCount++;
+ break;
+ case HNDTYPE_WEAK_LONG:
+ type = "WeakLong";
+ if (pStats) pStats->weakLongHandleCount++;
+ break;
+ case HNDTYPE_ASYNCPINNED:
+ type = "AsyncPinned";
+ if (pStats) pStats->asyncPinnedHandleCount++;
+ break;
+ case HNDTYPE_VARIABLE:
+ type = "Variable";
+ if (pStats) pStats->variableCount++;
+ break;
+ case HNDTYPE_SIZEDREF:
+ type = "SizedRef";
+ if (pStats) pStats->sizedRefCount++;
+ break;
+ case HNDTYPE_DEPENDENT:
+ type = "Dependent";
+ if (pStats) pStats->dependentCount++;
+ break;
+ case HNDTYPE_WEAK_WINRT:
+ type = "WeakWinRT";
+ if (pStats) pStats->weakWinRTHandleCount++;
+ break;
+ default:
+ DebugBreak();
+ type = "Unknown";
+ pStats->unknownHandleCount++;
+ break;
+ }
+
+ if (type && !mStat)
+ {
+ sos::MethodTable mt = mtAddr;
+ if (mtName == 0)
+ mtName = mt.GetName();
+
+ if (data[i].Type == HNDTYPE_REFCOUNTED)
+ mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), Decimal(data[i].RefCount), mtName);
+ else if (data[i].Type == HNDTYPE_DEPENDENT)
+ mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), ObjectPtr(data[i].Secondary), mtName);
+ else if (data[i].Type == HNDTYPE_WEAK_WINRT)
+ mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), Pointer(data[i].Secondary), mtName);
+ else
+ mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), "", mtName);
+ }
+ }
+ }
+
+ inline void PrintHandleRow(const char *text, int count)
+ {
+ if (count)
+ mOut.WriteRow(text, Decimal(count));
+ }
+
+ void PrintGCHandleStats(GCHandleStatistics *pStats)
+ {
+ Print("Handles:\n");
+ mOut.ReInit(2, 21, AlignLeft, 4);
+
+ PrintHandleRow("Strong Handles:", pStats->strongHandleCount);
+ PrintHandleRow("Pinned Handles:", pStats->pinnedHandleCount);
+ PrintHandleRow("Async Pinned Handles:", pStats->asyncPinnedHandleCount);
+ PrintHandleRow("Ref Count Handles:", pStats->refCntHandleCount);
+ PrintHandleRow("Weak Long Handles:", pStats->weakLongHandleCount);
+ PrintHandleRow("Weak Short Handles:", pStats->weakShortHandleCount);
+ PrintHandleRow("Weak WinRT Handles:", pStats->weakWinRTHandleCount);
+ PrintHandleRow("Variable Handles:", pStats->variableCount);
+ PrintHandleRow("SizedRef Handles:", pStats->sizedRefCount);
+ PrintHandleRow("Dependent Handles:", pStats->dependentCount);
+ PrintHandleRow("Other Handles:", pStats->unknownHandleCount);
+ }
+
+private:
+ BOOL mPerDomain, mStat, mDML;
+ unsigned int mType;
+ TableOutput mOut;
+ GCHandleStatsForDomains mHandleStat;
+};
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function dumps GC Handle statistics *
+* *
+\**********************************************************************/
+DECLARE_API(GCHandles)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ try
+ {
+ GCHandlesImpl gchandles(args);
+ gchandles.Run();
+ }
+ catch(const sos::Exception &e)
+ {
+ Print(e.what());
+ }
+
+ return Status;
+}
+
+BOOL derivedFrom(CLRDATA_ADDRESS mtObj, __in_z LPWSTR baseString)
+{
+ // We want to follow back until we get the mt for System.Exception
+ DacpMethodTableData dmtd;
+ CLRDATA_ADDRESS walkMT = mtObj;
+ while(walkMT != NULL)
+ {
+ if (dmtd.Request(g_sos, walkMT) != S_OK)
+ {
+ break;
+ }
+ NameForMT_s (TO_TADDR(walkMT), g_mdName, mdNameLen);
+ if (_wcscmp (baseString, g_mdName) == 0)
+ {
+ return TRUE;
+ }
+ walkMT = dmtd.ParentMethodTable;
+ }
+ return FALSE;
+}
+
+// This is an experimental and undocumented SOS API that attempts to step through code
+// stopping once jitted code is reached. It currently has some issues - it can take arbitrarily long
+// to reach jitted code and canceling it is terrible. At best it doesn't cancel, at worst it
+// kills the debugger. IsInterrupt() doesn't work nearly as nicely as one would hope :/
+#ifndef FEATURE_PAL
+DECLARE_API(TraceToCode)
+{
+ INIT_API_NOEE();
+
+ static ULONG64 g_clrBaseAddr = 0;
+
+
+ while(true)
+ {
+ if (IsInterrupt())
+ {
+ ExtOut("Interrupted\n");
+ return S_FALSE;
+ }
+
+ ULONG64 Offset;
+ g_ExtRegisters->GetInstructionOffset(&Offset);
+
+ DWORD codeType = 0;
+ ULONG64 base = 0;
+ CLRDATA_ADDRESS cdaStart = TO_CDADDR(Offset);
+ DacpMethodDescData MethodDescData;
+ if(g_ExtSymbols->GetModuleByOffset(Offset, 0, NULL, &base) == S_OK)
+ {
+ if(g_clrBaseAddr == 0)
+ {
+ g_ExtSymbols->GetModuleByModuleName (MAIN_CLR_MODULE_NAME_A,0,NULL,
+ &g_clrBaseAddr);
+ }
+ if(g_clrBaseAddr == base)
+ {
+ ExtOut("Compiled code in CLR\n");
+ codeType = 4;
+ }
+ else
+ {
+ ExtOut("Compiled code in module @ 0x%I64x\n", base);
+ codeType = 8;
+ }
+ }
+ else if (g_sos != NULL || LoadClrDebugDll()==S_OK)
+ {
+ CLRDATA_ADDRESS addr;
+ if(g_sos->GetMethodDescPtrFromIP(cdaStart, &addr) == S_OK)
+ {
+ WCHAR wszNameBuffer[1024]; // should be large enough
+
+ // get the MethodDesc name
+ if ((g_sos->GetMethodDescName(addr, 1024, wszNameBuffer, NULL) == S_OK) &&
+ _wcsncmp(W("DomainBoundILStubClass"), wszNameBuffer, 22)==0)
+ {
+ ExtOut("ILStub\n");
+ codeType = 2;
+ }
+ else
+ {
+ ExtOut("Jitted code\n");
+ codeType = 1;
+ }
+ }
+ else
+ {
+ ExtOut("Not compiled or jitted, assuming stub\n");
+ codeType = 16;
+ }
+ }
+ else
+ {
+ // not compiled but CLR isn't loaded... some other code generator?
+ return E_FAIL;
+ }
+
+ if(codeType == 1)
+ {
+ return S_OK;
+ }
+ else
+ {
+ Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "thr; .echo wait" ,0);
+ if (FAILED(Status))
+ {
+ ExtOut("Error tracing instruction\n");
+ return Status;
+ }
+ }
+ }
+
+ return Status;
+
+}
+#endif // FEATURE_PAL
+
+// This is an experimental and undocumented API that sets a debugger pseudo-register based
+// on the type of code at the given IP. It can be used in scripts to keep stepping until certain
+// kinds of code have been reached. Presumbably its slower than !TraceToCode but at least it
+// cancels much better
+#ifndef FEATURE_PAL
+DECLARE_API(GetCodeTypeFlags)
+{
+ INIT_API();
+
+
+ char buffer[100+mdNameLen];
+ size_t ip;
+ StringHolder PReg;
+
+ CMDValue arg[] = {
+ // vptr, type
+ {&ip, COSIZE_T},
+ {&PReg.data, COSTRING}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ size_t preg = 1; // by default
+ if (nArg == 2)
+ {
+ preg = GetExpression(PReg.data);
+ if (preg > 19)
+ {
+ ExtOut("Pseudo-register number must be between 0 and 19\n");
+ return Status;
+ }
+ }
+
+ sprintf_s(buffer,_countof (buffer),
+ "r$t%d=0",
+ preg);
+ Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer ,0);
+ if (FAILED(Status))
+ {
+ ExtOut("Error initialized register $t%d to zero\n", preg);
+ return Status;
+ }
+
+ ULONG64 base = 0;
+ CLRDATA_ADDRESS cdaStart = TO_CDADDR(ip);
+ DWORD codeType = 0;
+ CLRDATA_ADDRESS addr;
+ if(g_sos->GetMethodDescPtrFromIP(cdaStart, &addr) == S_OK)
+ {
+ WCHAR wszNameBuffer[1024]; // should be large enough
+
+ // get the MethodDesc name
+ if (g_sos->GetMethodDescName(addr, 1024, wszNameBuffer, NULL) == S_OK &&
+ _wcsncmp(W("DomainBoundILStubClass"), wszNameBuffer, 22)==0)
+ {
+ ExtOut("ILStub\n");
+ codeType = 2;
+ }
+ else
+ {
+ ExtOut("Jitted code");
+ codeType = 1;
+ }
+ }
+ else if(g_ExtSymbols->GetModuleByOffset (ip, 0, NULL, &base) == S_OK)
+ {
+ ULONG64 clrBaseAddr = 0;
+ if(SUCCEEDED(g_ExtSymbols->GetModuleByModuleName (MAIN_CLR_MODULE_NAME_A,0,NULL, &clrBaseAddr)) && base==clrBaseAddr)
+ {
+ ExtOut("Compiled code in CLR");
+ codeType = 4;
+ }
+ else
+ {
+ ExtOut("Compiled code in module @ 0x%I64x\n", base);
+ codeType = 8;
+ }
+ }
+ else
+ {
+ ExtOut("Not compiled or jitted, assuming stub\n");
+ codeType = 16;
+ }
+
+ sprintf_s(buffer,_countof (buffer),
+ "r$t%d=%x",
+ preg, codeType);
+ Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
+ if (FAILED(Status))
+ {
+ ExtOut("Error setting register $t%d\n", preg);
+ return Status;
+ }
+ return Status;
+
+}
+#endif // FEATURE_PAL
+
+DECLARE_API(StopOnException)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+
+ char buffer[100+mdNameLen];
+
+ BOOL fDerived = FALSE;
+ BOOL fCreate1 = FALSE;
+ BOOL fCreate2 = FALSE;
+
+ CMDOption option[] = {
+ // name, vptr, type, hasValue
+ {"-derived", &fDerived, COBOOL, FALSE}, // catch derived exceptions
+ {"-create", &fCreate1, COBOOL, FALSE}, // create 1st chance handler
+ {"-create2", &fCreate2, COBOOL, FALSE}, // create 2nd chance handler
+ };
+
+ StringHolder TypeName,PReg;
+
+ CMDValue arg[] = {
+ // vptr, type
+ {&TypeName.data, COSTRING},
+ {&PReg.data, COSTRING}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+ if (IsDumpFile())
+ {
+ ExtOut("Live debugging session required\n");
+ return Status;
+ }
+ if (nArg < 1 || nArg > 2)
+ {
+ ExtOut("usage: StopOnException [-derived] [-create | -create2] <type name>\n");
+ ExtOut(" [<pseudo-register number for result>]\n");
+ ExtOut("ex: StopOnException -create System.OutOfMemoryException 1\n");
+ return Status;
+ }
+
+ size_t preg = 1; // by default
+ if (nArg == 2)
+ {
+ preg = GetExpression(PReg.data);
+ if (preg > 19)
+ {
+ ExtOut("Pseudo-register number must be between 0 and 19\n");
+ return Status;
+ }
+ }
+
+ sprintf_s(buffer,_countof (buffer),
+ "r$t%d=0",
+ preg);
+ Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
+ if (FAILED(Status))
+ {
+ ExtOut("Error initialized register $t%d to zero\n", preg);
+ return Status;
+ }
+
+ if (fCreate1 || fCreate2)
+ {
+ sprintf_s(buffer,_countof (buffer),
+ "sxe %s \"!soe %s %s %d;.if(@$t%d==0) {g} .else {.echo '%s hit'}\" %x",
+ fCreate1 ? "-c" : "-c2",
+ fDerived ? "-derived" : "",
+ TypeName.data,
+ preg,
+ preg,
+ TypeName.data,
+ EXCEPTION_COMPLUS
+ );
+
+ Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
+ if (FAILED(Status))
+ {
+ ExtOut("Error setting breakpoint: %s\n", buffer);
+ return Status;
+ }
+
+ ExtOut("Breakpoint set\n");
+ return Status;
+ }
+
+ // Find the last thrown exception on this thread.
+ // Does it match? If so, set the register.
+ CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
+ DacpThreadData Thread;
+
+ if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
+ {
+ ExtOut("The current thread is unmanaged\n");
+ return Status;
+ }
+
+ TADDR taLTOH;
+ if (!SafeReadMemory(Thread.lastThrownObjectHandle,
+ &taLTOH,
+ sizeof(taLTOH), NULL))
+ {
+ ExtOut("There is no current managed exception on this thread\n");
+ return Status;
+ }
+
+ if (taLTOH)
+ {
+ LPWSTR typeNameWide = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
+ MultiByteToWideChar(CP_ACP,0,TypeName.data,-1,typeNameWide,mdNameLen);
+
+ TADDR taMT;
+ if (SafeReadMemory(taLTOH, &taMT, sizeof(taMT), NULL))
+ {
+ NameForMT_s (taMT, g_mdName, mdNameLen);
+ if ((_wcscmp(g_mdName,typeNameWide) == 0) ||
+ (fDerived && derivedFrom(taMT, typeNameWide)))
+ {
+ sprintf_s(buffer,_countof (buffer),
+ "r$t%d=1",
+ preg);
+ Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
+ if (FAILED(Status))
+ {
+ ExtOut("Failed to execute the following command: %s\n", buffer);
+ }
+ }
+ }
+ }
+
+ return Status;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function finds the size of an object or all roots. *
+* *
+\**********************************************************************/
+DECLARE_API(ObjSize)
+{
+#ifndef FEATURE_PAL
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ BOOL dml = FALSE;
+ StringHolder str_Object;
+
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"/d", &dml, COBOOL, FALSE},
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&str_Object.data, COSTRING}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ TADDR obj = GetExpression(str_Object.data);
+
+ GCRootImpl gcroot;
+ if (obj == 0)
+ {
+ gcroot.ObjSize();
+ }
+ else
+ {
+ if(!sos::IsObject(obj))
+ {
+ ExtOut("%p is not a valid object.\n", SOS_PTR(obj));
+ return Status;
+ }
+
+ size_t size = gcroot.ObjSize(obj);
+ TADDR mt = 0;
+ MOVE(mt, obj);
+ sos::MethodTable methodTable = mt;
+ ExtOut("sizeof(%p) = %d (0x%x) bytes (%S)\n", SOS_PTR(obj), size, size, methodTable.GetName());
+ }
+ return Status;
+#else
+ return E_NOTIMPL;
+#endif
+
+}
+
+#ifndef FEATURE_PAL
+// For FEATURE_PAL, MEMORY_BASIC_INFORMATION64 doesn't exist yet. TODO?
+DECLARE_API(GCHandleLeaks)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ ExtOut("-------------------------------------------------------------------------------\n");
+ ExtOut("GCHandleLeaks will report any GCHandles that couldn't be found in memory. \n");
+ ExtOut("Strong and Pinned GCHandles are reported at this time. You can safely abort the\n");
+ ExtOut("memory scan with Control-C or Control-Break. \n");
+ ExtOut("-------------------------------------------------------------------------------\n");
+
+ static DWORD_PTR array[2000];
+ UINT i;
+ BOOL dml = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"/d", &dml, COBOOL, FALSE},
+ };
+
+ if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+
+ UINT iFinal = FindAllPinnedAndStrong(array,sizeof(array)/sizeof(DWORD_PTR));
+ ExtOut("Found %d handles:\n",iFinal);
+ for (i=1;i<=iFinal;i++)
+ {
+ ExtOut("%p\t", SOS_PTR(array[i-1]));
+ if ((i % 4) == 0)
+ ExtOut("\n");
+ }
+
+ ExtOut("\nSearching memory\n");
+ // Now search memory for this:
+ DWORD_PTR buffer[1024];
+ ULONG64 memCur = 0x0;
+ BOOL bAbort = FALSE;
+
+ //find out memory used by stress log
+ StressLogMem stressLog;
+ CLRDATA_ADDRESS StressLogAddress = NULL;
+ if (LoadClrDebugDll() != S_OK)
+ {
+ // Try to find stress log symbols
+ DWORD_PTR dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!StressLog::theLog");
+ StressLogAddress = dwAddr;
+ g_bDacBroken = TRUE;
+ }
+ else
+ {
+ if (g_sos->GetStressLogAddress(&StressLogAddress) != S_OK)
+ {
+ ExtOut("Unable to find stress log via DAC\n");
+ }
+ g_bDacBroken = FALSE;
+ }
+
+ if (stressLog.Init (StressLogAddress, g_ExtData))
+ {
+ ExtOut("Reference found in stress log will be ignored\n");
+ }
+ else
+ {
+ ExtOut("Failed to read whole or part of stress log, some references may come from stress log\n");
+ }
+
+
+ while (!bAbort)
+ {
+ NTSTATUS status;
+ MEMORY_BASIC_INFORMATION64 memInfo;
+
+ status = g_ExtData2->QueryVirtual(UL64_TO_CDA(memCur), &memInfo);
+
+ if( !NT_SUCCESS(status) )
+ {
+ break;
+ }
+
+ if (memInfo.State == MEM_COMMIT)
+ {
+ for (ULONG64 memIter = memCur; memIter < (memCur + memInfo.RegionSize); memIter+=sizeof(buffer))
+ {
+ if (IsInterrupt())
+ {
+ ExtOut("Quitting at %p due to user abort\n", SOS_PTR(memIter));
+ bAbort = TRUE;
+ break;
+ }
+
+ if ((memIter % 0x10000000)==0x0)
+ {
+ ExtOut("Searching %p...\n", SOS_PTR(memIter));
+ }
+
+ ULONG size = 0;
+ HRESULT ret;
+ ret = g_ExtData->ReadVirtual(UL64_TO_CDA(memIter), buffer, sizeof(buffer), &size);
+ if (ret == S_OK)
+ {
+ for (UINT x=0;x<1024;x++)
+ {
+ DWORD_PTR value = buffer[x];
+ // We don't care about the low bit. Also, the GCHandle class turns on the
+ // low bit for pinned handles, so without the statement below, we wouldn't
+ // notice pinned handles.
+ value = value & ~1;
+ for (i=0;i<iFinal;i++)
+ {
+ ULONG64 addrInDebugee = (ULONG64)memIter+(x*sizeof(DWORD_PTR));
+ if ((array[i] & ~1) == value)
+ {
+ if (stressLog.IsInStressLog (addrInDebugee))
+ {
+ ExtOut("Found %p in stress log at location %p, reference not counted\n", SOS_PTR(value), addrInDebugee);
+ }
+ else
+ {
+ ExtOut("Found %p at location %p\n", SOS_PTR(value), addrInDebugee);
+ array[i] |= 0x1;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ if (size > 0)
+ {
+ ExtOut("only read %x bytes at %p\n", size, SOS_PTR(memIter));
+ }
+ }
+ }
+ }
+
+ memCur += memInfo.RegionSize;
+ }
+
+ int numNotFound = 0;
+ for (i=0;i<iFinal;i++)
+ {
+ if ((array[i] & 0x1) == 0)
+ {
+ numNotFound++;
+ // ExtOut("WARNING: %p not found\n", SOS_PTR(array[i]));
+ }
+ }
+
+ if (numNotFound > 0)
+ {
+ ExtOut("------------------------------------------------------------------------------\n");
+ ExtOut("Some handles were not found. If the number of not-found handles grows over the\n");
+ ExtOut("lifetime of your application, you may have a GCHandle leak. This will cause \n");
+ ExtOut("the GC Heap to grow larger as objects are being kept alive, referenced only \n");
+ ExtOut("by the orphaned handle. If the number doesn't grow over time, note that there \n");
+ ExtOut("may be some noise in this output, as an unmanaged application may be storing \n");
+ ExtOut("the handle in a non-standard way, perhaps with some bits flipped. The memory \n");
+ ExtOut("scan wouldn't be able to find those. \n");
+ ExtOut("------------------------------------------------------------------------------\n");
+
+ ExtOut("Didn't find %d handles:\n", numNotFound);
+ int numPrinted=0;
+ for (i=0;i<iFinal;i++)
+ {
+ if ((array[i] & 0x1) == 0)
+ {
+ numPrinted++;
+ ExtOut("%p\t", SOS_PTR(array[i]));
+ if ((numPrinted % 4) == 0)
+ ExtOut("\n");
+ }
+ }
+ ExtOut("\n");
+ }
+ else
+ {
+ ExtOut("------------------------------------------------------------------------------\n");
+ ExtOut("All handles found");
+ if (bAbort)
+ ExtOut(" even though you aborted.\n");
+ else
+ ExtOut(".\n");
+ ExtOut("A leak may still exist because in a general scan of process memory SOS can't \n");
+ ExtOut("differentiate between garbage and valid structures, so you may have false \n");
+ ExtOut("positives. If you still suspect a leak, use this function over time to \n");
+ ExtOut("identify a possible trend. \n");
+ ExtOut("------------------------------------------------------------------------------\n");
+ }
+
+ return Status;
+}
+#endif // FEATURE_PAL
+
+#endif // FEATURE_PAL
+
+class ClrStackImplWithICorDebug
+{
+private:
+ static HRESULT DereferenceAndUnboxValue(ICorDebugValue * pValue, ICorDebugValue** ppOutputValue, BOOL * pIsNull = NULL)
+ {
+ HRESULT Status = S_OK;
+ *ppOutputValue = NULL;
+ if(pIsNull != NULL) *pIsNull = FALSE;
+
+ ToRelease<ICorDebugReferenceValue> pReferenceValue;
+ Status = pValue->QueryInterface(IID_ICorDebugReferenceValue, (LPVOID*) &pReferenceValue);
+ if (SUCCEEDED(Status))
+ {
+ BOOL isNull = FALSE;
+ IfFailRet(pReferenceValue->IsNull(&isNull));
+ if(!isNull)
+ {
+ ToRelease<ICorDebugValue> pDereferencedValue;
+ IfFailRet(pReferenceValue->Dereference(&pDereferencedValue));
+ return DereferenceAndUnboxValue(pDereferencedValue, ppOutputValue);
+ }
+ else
+ {
+ if(pIsNull != NULL) *pIsNull = TRUE;
+ *ppOutputValue = pValue;
+ (*ppOutputValue)->AddRef();
+ return S_OK;
+ }
+ }
+
+ ToRelease<ICorDebugBoxValue> pBoxedValue;
+ Status = pValue->QueryInterface(IID_ICorDebugBoxValue, (LPVOID*) &pBoxedValue);
+ if (SUCCEEDED(Status))
+ {
+ ToRelease<ICorDebugObjectValue> pUnboxedValue;
+ IfFailRet(pBoxedValue->GetObject(&pUnboxedValue));
+ return DereferenceAndUnboxValue(pUnboxedValue, ppOutputValue);
+ }
+ *ppOutputValue = pValue;
+ (*ppOutputValue)->AddRef();
+ return S_OK;
+ }
+
+ static BOOL ShouldExpandVariable(__in_z WCHAR* varToExpand, __in_z WCHAR* currentExpansion)
+ {
+ if(currentExpansion == NULL || varToExpand == NULL) return FALSE;
+
+ size_t varToExpandLen = _wcslen(varToExpand);
+ size_t currentExpansionLen = _wcslen(currentExpansion);
+ if(currentExpansionLen > varToExpandLen) return FALSE;
+ if(currentExpansionLen < varToExpandLen && varToExpand[currentExpansionLen] != L'.') return FALSE;
+ if(_wcsncmp(currentExpansion, varToExpand, currentExpansionLen) != 0) return FALSE;
+
+ return TRUE;
+ }
+
+ static BOOL IsEnum(ICorDebugValue * pInputValue)
+ {
+ ToRelease<ICorDebugValue> pValue;
+ if(FAILED(DereferenceAndUnboxValue(pInputValue, &pValue, NULL))) return FALSE;
+
+ WCHAR baseTypeName[mdNameLen];
+ ToRelease<ICorDebugValue2> pValue2;
+ ToRelease<ICorDebugType> pType;
+ ToRelease<ICorDebugType> pBaseType;
+
+ if(FAILED(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2))) return FALSE;
+ if(FAILED(pValue2->GetExactType(&pType))) return FALSE;
+ if(FAILED(pType->GetBase(&pBaseType)) || pBaseType == NULL) return FALSE;
+ if(FAILED(GetTypeOfValue(pBaseType, baseTypeName, mdNameLen))) return FALSE;
+
+ return (_wcsncmp(baseTypeName, W("System.Enum"), 11) == 0);
+ }
+
+ static HRESULT AddGenericArgs(ICorDebugType * pType, __inout_ecount(typeNameLen) WCHAR* typeName, ULONG typeNameLen)
+ {
+ bool isFirst = true;
+ ToRelease<ICorDebugTypeEnum> pTypeEnum;
+ if(SUCCEEDED(pType->EnumerateTypeParameters(&pTypeEnum)))
+ {
+ ULONG numTypes = 0;
+ ToRelease<ICorDebugType> pCurrentTypeParam;
+
+ while(SUCCEEDED(pTypeEnum->Next(1, &pCurrentTypeParam, &numTypes)))
+ {
+ if(numTypes == 0) break;
+
+ if(isFirst)
+ {
+ isFirst = false;
+ wcsncat_s(typeName, typeNameLen, W("&lt;"), typeNameLen);
+ }
+ else wcsncat_s(typeName, typeNameLen, W(","), typeNameLen);
+
+ WCHAR typeParamName[mdNameLen];
+ typeParamName[0] = L'\0';
+ GetTypeOfValue(pCurrentTypeParam, typeParamName, mdNameLen);
+ wcsncat_s(typeName, typeNameLen, typeParamName, typeNameLen);
+ }
+ if(!isFirst)
+ wcsncat_s(typeName, typeNameLen, W("&gt;"), typeNameLen);
+ }
+
+ return S_OK;
+ }
+
+ static HRESULT GetTypeOfValue(ICorDebugType * pType, __inout_ecount(typeNameLen) WCHAR* typeName, ULONG typeNameLen)
+ {
+ HRESULT Status = S_OK;
+
+ CorElementType corElemType;
+ IfFailRet(pType->GetType(&corElemType));
+
+ switch (corElemType)
+ {
+ //List of unsupported CorElementTypes:
+ //ELEMENT_TYPE_END = 0x0,
+ //ELEMENT_TYPE_VAR = 0x13, // a class type variable VAR <U1>
+ //ELEMENT_TYPE_GENERICINST = 0x15, // GENERICINST <generic type> <argCnt> <arg1> ... <argn>
+ //ELEMENT_TYPE_TYPEDBYREF = 0x16, // TYPEDREF (it takes no args) a typed referece to some other type
+ //ELEMENT_TYPE_MVAR = 0x1e, // a method type variable MVAR <U1>
+ //ELEMENT_TYPE_CMOD_REQD = 0x1F, // required C modifier : E_T_CMOD_REQD <mdTypeRef/mdTypeDef>
+ //ELEMENT_TYPE_CMOD_OPT = 0x20, // optional C modifier : E_T_CMOD_OPT <mdTypeRef/mdTypeDef>
+ //ELEMENT_TYPE_INTERNAL = 0x21, // INTERNAL <typehandle>
+ //ELEMENT_TYPE_MAX = 0x22, // first invalid element type
+ //ELEMENT_TYPE_MODIFIER = 0x40,
+ //ELEMENT_TYPE_SENTINEL = 0x01 | ELEMENT_TYPE_MODIFIER, // sentinel for varargs
+ //ELEMENT_TYPE_PINNED = 0x05 | ELEMENT_TYPE_MODIFIER,
+ //ELEMENT_TYPE_R4_HFA = 0x06 | ELEMENT_TYPE_MODIFIER, // used only internally for R4 HFA types
+ //ELEMENT_TYPE_R8_HFA = 0x07 | ELEMENT_TYPE_MODIFIER, // used only internally for R8 HFA types
+ default:
+ swprintf_s(typeName, typeNameLen, W("(Unhandled CorElementType: 0x%x)\0"), corElemType);
+ break;
+
+ case ELEMENT_TYPE_VALUETYPE:
+ case ELEMENT_TYPE_CLASS:
+ {
+ //Defaults in case we fail...
+ if(corElemType == ELEMENT_TYPE_VALUETYPE) swprintf_s(typeName, typeNameLen, W("struct\0"));
+ else swprintf_s(typeName, typeNameLen, W("class\0"));
+
+ mdTypeDef typeDef;
+ ToRelease<ICorDebugClass> pClass;
+ if(SUCCEEDED(pType->GetClass(&pClass)) && SUCCEEDED(pClass->GetToken(&typeDef)))
+ {
+ ToRelease<ICorDebugModule> pModule;
+ IfFailRet(pClass->GetModule(&pModule));
+
+ ToRelease<IUnknown> pMDUnknown;
+ ToRelease<IMetaDataImport> pMD;
+ IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
+ IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
+
+ if(SUCCEEDED(NameForToken_s(TokenFromRid(typeDef, mdtTypeDef), pMD, g_mdName, mdNameLen, false)))
+ swprintf_s(typeName, typeNameLen, W("%s\0"), g_mdName);
+ }
+ AddGenericArgs(pType, typeName, typeNameLen);
+ }
+ break;
+ case ELEMENT_TYPE_VOID:
+ swprintf_s(typeName, typeNameLen, W("void\0"));
+ break;
+ case ELEMENT_TYPE_BOOLEAN:
+ swprintf_s(typeName, typeNameLen, W("bool\0"));
+ break;
+ case ELEMENT_TYPE_CHAR:
+ swprintf_s(typeName, typeNameLen, W("char\0"));
+ break;
+ case ELEMENT_TYPE_I1:
+ swprintf_s(typeName, typeNameLen, W("signed byte\0"));
+ break;
+ case ELEMENT_TYPE_U1:
+ swprintf_s(typeName, typeNameLen, W("byte\0"));
+ break;
+ case ELEMENT_TYPE_I2:
+ swprintf_s(typeName, typeNameLen, W("short\0"));
+ break;
+ case ELEMENT_TYPE_U2:
+ swprintf_s(typeName, typeNameLen, W("unsigned short\0"));
+ break;
+ case ELEMENT_TYPE_I4:
+ swprintf_s(typeName, typeNameLen, W("int\0"));
+ break;
+ case ELEMENT_TYPE_U4:
+ swprintf_s(typeName, typeNameLen, W("unsigned int\0"));
+ break;
+ case ELEMENT_TYPE_I8:
+ swprintf_s(typeName, typeNameLen, W("long\0"));
+ break;
+ case ELEMENT_TYPE_U8:
+ swprintf_s(typeName, typeNameLen, W("unsigned long\0"));
+ break;
+ case ELEMENT_TYPE_R4:
+ swprintf_s(typeName, typeNameLen, W("float\0"));
+ break;
+ case ELEMENT_TYPE_R8:
+ swprintf_s(typeName, typeNameLen, W("double\0"));
+ break;
+ case ELEMENT_TYPE_OBJECT:
+ swprintf_s(typeName, typeNameLen, W("object\0"));
+ break;
+ case ELEMENT_TYPE_STRING:
+ swprintf_s(typeName, typeNameLen, W("string\0"));
+ break;
+ case ELEMENT_TYPE_I:
+ swprintf_s(typeName, typeNameLen, W("IntPtr\0"));
+ break;
+ case ELEMENT_TYPE_U:
+ swprintf_s(typeName, typeNameLen, W("UIntPtr\0"));
+ break;
+ case ELEMENT_TYPE_SZARRAY:
+ case ELEMENT_TYPE_ARRAY:
+ case ELEMENT_TYPE_BYREF:
+ case ELEMENT_TYPE_PTR:
+ {
+ ToRelease<ICorDebugType> pFirstParameter;
+ if(SUCCEEDED(pType->GetFirstTypeParameter(&pFirstParameter)))
+ GetTypeOfValue(pFirstParameter, typeName, typeNameLen);
+ else
+ swprintf_s(typeName, typeNameLen, W("<unknown>\0"));
+
+ switch(corElemType)
+ {
+ case ELEMENT_TYPE_SZARRAY:
+ wcsncat_s(typeName, typeNameLen, W("[]\0"), typeNameLen);
+ return S_OK;
+ case ELEMENT_TYPE_ARRAY:
+ {
+ ULONG32 rank = 0;
+ pType->GetRank(&rank);
+ wcsncat_s(typeName, typeNameLen, W("["), typeNameLen);
+ for(ULONG32 i = 0; i < rank - 1; i++)
+ {
+ //
+ wcsncat_s(typeName, typeNameLen, W(","), typeNameLen);
+ }
+ wcsncat_s(typeName, typeNameLen, W("]\0"), typeNameLen);
+ }
+ return S_OK;
+ case ELEMENT_TYPE_BYREF:
+ wcsncat_s(typeName, typeNameLen, W("&\0"), typeNameLen);
+ return S_OK;
+ case ELEMENT_TYPE_PTR:
+ wcsncat_s(typeName, typeNameLen, W("*\0"), typeNameLen);
+ return S_OK;
+ default:
+ // note we can never reach here as this is a nested switch
+ // and corElemType can only be one of the values above
+ break;
+ }
+ }
+ break;
+ case ELEMENT_TYPE_FNPTR:
+ swprintf_s(typeName, typeNameLen, W("*(...)\0"));
+ break;
+ case ELEMENT_TYPE_TYPEDBYREF:
+ swprintf_s(typeName, typeNameLen, W("typedbyref\0"));
+ break;
+ }
+ return S_OK;
+ }
+
+ static HRESULT GetTypeOfValue(ICorDebugValue * pValue, __inout_ecount(typeNameLen) WCHAR* typeName, ULONG typeNameLen)
+ {
+ HRESULT Status = S_OK;
+
+ CorElementType corElemType;
+ IfFailRet(pValue->GetType(&corElemType));
+
+ ToRelease<ICorDebugType> pType;
+ ToRelease<ICorDebugValue2> pValue2;
+ if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugValue2, (void**) &pValue2)) && SUCCEEDED(pValue2->GetExactType(&pType)))
+ return GetTypeOfValue(pType, typeName, typeNameLen);
+ else
+ swprintf_s(typeName, typeNameLen, W("<unknown>\0"));
+
+ return S_OK;
+ }
+
+ static HRESULT PrintEnumValue(ICorDebugValue* pInputValue, BYTE* enumValue)
+ {
+ HRESULT Status = S_OK;
+
+ ToRelease<ICorDebugValue> pValue;
+ IfFailRet(DereferenceAndUnboxValue(pInputValue, &pValue, NULL));
+
+ mdTypeDef currentTypeDef;
+ ToRelease<ICorDebugClass> pClass;
+ ToRelease<ICorDebugValue2> pValue2;
+ ToRelease<ICorDebugType> pType;
+ ToRelease<ICorDebugModule> pModule;
+ IfFailRet(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2));
+ IfFailRet(pValue2->GetExactType(&pType));
+ IfFailRet(pType->GetClass(&pClass));
+ IfFailRet(pClass->GetModule(&pModule));
+ IfFailRet(pClass->GetToken(&currentTypeDef));
+
+ ToRelease<IUnknown> pMDUnknown;
+ ToRelease<IMetaDataImport> pMD;
+ IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
+ IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
+
+
+ //First, we need to figure out the underlying enum type so that we can correctly type cast the raw values of each enum constant
+ //We get that from the non-static field of the enum variable (I think the field is called __value or something similar)
+ ULONG numFields = 0;
+ HCORENUM fEnum = NULL;
+ mdFieldDef fieldDef;
+ CorElementType enumUnderlyingType = ELEMENT_TYPE_END;
+ while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
+ {
+ DWORD fieldAttr = 0;
+ PCCOR_SIGNATURE pSignatureBlob = NULL;
+ ULONG sigBlobLength = 0;
+ if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, NULL, 0, NULL, &fieldAttr, &pSignatureBlob, &sigBlobLength, NULL, NULL, NULL)))
+ {
+ if((fieldAttr & fdStatic) == 0)
+ {
+ CorSigUncompressCallingConv(pSignatureBlob);
+ enumUnderlyingType = CorSigUncompressElementType(pSignatureBlob);
+ break;
+ }
+ }
+ }
+ pMD->CloseEnum(fEnum);
+
+
+ //Now that we know the underlying enum type, let's decode the enum variable into OR-ed, human readable enum contants
+ fEnum = NULL;
+ bool isFirst = true;
+ ULONG64 remainingValue = *((ULONG64*)enumValue);
+ while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
+ {
+ ULONG nameLen = 0;
+ DWORD fieldAttr = 0;
+ WCHAR mdName[mdNameLen];
+ WCHAR typeName[mdNameLen];
+ UVCP_CONSTANT pRawValue = NULL;
+ ULONG rawValueLength = 0;
+ if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, mdName, mdNameLen, &nameLen, &fieldAttr, NULL, NULL, NULL, &pRawValue, &rawValueLength)))
+ {
+ DWORD enumValueRequiredAttributes = fdPublic | fdStatic | fdLiteral | fdHasDefault;
+ if((fieldAttr & enumValueRequiredAttributes) != enumValueRequiredAttributes)
+ continue;
+
+ ULONG64 currentConstValue = 0;
+ switch (enumUnderlyingType)
+ {
+ case ELEMENT_TYPE_CHAR:
+ case ELEMENT_TYPE_I1:
+ currentConstValue = (ULONG64)(*((CHAR*)pRawValue));
+ break;
+ case ELEMENT_TYPE_U1:
+ currentConstValue = (ULONG64)(*((BYTE*)pRawValue));
+ break;
+ case ELEMENT_TYPE_I2:
+ currentConstValue = (ULONG64)(*((SHORT*)pRawValue));
+ break;
+ case ELEMENT_TYPE_U2:
+ currentConstValue = (ULONG64)(*((USHORT*)pRawValue));
+ break;
+ case ELEMENT_TYPE_I4:
+ currentConstValue = (ULONG64)(*((INT32*)pRawValue));
+ break;
+ case ELEMENT_TYPE_U4:
+ currentConstValue = (ULONG64)(*((UINT32*)pRawValue));
+ break;
+ case ELEMENT_TYPE_I8:
+ currentConstValue = (ULONG64)(*((LONG*)pRawValue));
+ break;
+ case ELEMENT_TYPE_U8:
+ currentConstValue = (ULONG64)(*((ULONG*)pRawValue));
+ break;
+ case ELEMENT_TYPE_I:
+ currentConstValue = (ULONG64)(*((int*)pRawValue));
+ break;
+ case ELEMENT_TYPE_U:
+ case ELEMENT_TYPE_R4:
+ case ELEMENT_TYPE_R8:
+ // Technically U and the floating-point ones are options in the CLI, but not in the CLS or C#, so these are NYI
+ default:
+ currentConstValue = 0;
+ }
+
+ if((currentConstValue == remainingValue) || ((currentConstValue != 0) && ((currentConstValue & remainingValue) == currentConstValue)))
+ {
+ remainingValue &= ~currentConstValue;
+ if(isFirst)
+ {
+ ExtOut(" = %S", mdName);
+ isFirst = false;
+ }
+ else ExtOut(" | %S", mdName);
+ }
+ }
+ }
+ pMD->CloseEnum(fEnum);
+
+ return S_OK;
+ }
+
+ static HRESULT PrintStringValue(ICorDebugValue * pValue)
+ {
+ HRESULT Status;
+
+ ToRelease<ICorDebugStringValue> pStringValue;
+ IfFailRet(pValue->QueryInterface(IID_ICorDebugStringValue, (LPVOID*) &pStringValue));
+
+ ULONG32 cchValue;
+ IfFailRet(pStringValue->GetLength(&cchValue));
+ cchValue++; // Allocate one more for null terminator
+
+ CQuickString quickString;
+ quickString.Alloc(cchValue);
+
+ ULONG32 cchValueReturned;
+ IfFailRet(pStringValue->GetString(
+ cchValue,
+ &cchValueReturned,
+ quickString.String()));
+
+ ExtOut(" = \"%S\"\n", quickString.String());
+
+ return S_OK;
+ }
+
+ static HRESULT PrintSzArrayValue(ICorDebugValue * pValue, ICorDebugILFrame * pILFrame, IMetaDataImport * pMD, int indent, __in_z WCHAR* varToExpand, __inout_ecount(currentExpansionSize) WCHAR* currentExpansion, DWORD currentExpansionSize, int currentFrame)
+ {
+ HRESULT Status = S_OK;
+
+ ToRelease<ICorDebugArrayValue> pArrayValue;
+ IfFailRet(pValue->QueryInterface(IID_ICorDebugArrayValue, (LPVOID*) &pArrayValue));
+
+ ULONG32 nRank;
+ IfFailRet(pArrayValue->GetRank(&nRank));
+ if (nRank != 1)
+ {
+ return E_UNEXPECTED;
+ }
+
+ ULONG32 cElements;
+ IfFailRet(pArrayValue->GetCount(&cElements));
+
+ if (cElements == 0) ExtOut(" (empty)\n");
+ else if (cElements == 1) ExtOut(" (1 element)\n");
+ else ExtOut(" (%d elements)\n", cElements);
+
+ if(!ShouldExpandVariable(varToExpand, currentExpansion)) return S_OK;
+ size_t currentExpansionLen = _wcslen(currentExpansion);
+
+ for (ULONG32 i=0; i < cElements; i++)
+ {
+ for(int j = 0; j <= indent; j++) ExtOut(" ");
+ currentExpansion[currentExpansionLen] = L'\0';
+ swprintf_s(currentExpansion, mdNameLen, W("%s.[%d]\0"), currentExpansion, i);
+
+ bool printed = false;
+ CorElementType corElemType;
+ ToRelease<ICorDebugType> pFirstParameter;
+ ToRelease<ICorDebugValue2> pValue2;
+ ToRelease<ICorDebugType> pType;
+ if(SUCCEEDED(pArrayValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2)) && SUCCEEDED(pValue2->GetExactType(&pType)))
+ {
+ if(SUCCEEDED(pType->GetFirstTypeParameter(&pFirstParameter)) && SUCCEEDED(pFirstParameter->GetType(&corElemType)))
+ {
+ switch(corElemType)
+ {
+ //If the array element is something that we can expand with !clrstack, show information about the type of this element
+ case ELEMENT_TYPE_VALUETYPE:
+ case ELEMENT_TYPE_CLASS:
+ case ELEMENT_TYPE_SZARRAY:
+ {
+ WCHAR typeOfElement[mdNameLen];
+ GetTypeOfValue(pFirstParameter, typeOfElement, mdNameLen);
+ DMLOut(" |- %s = %S", DMLManagedVar(currentExpansion, currentFrame, i), typeOfElement);
+ printed = true;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if(!printed) DMLOut(" |- %s", DMLManagedVar(currentExpansion, currentFrame, i));
+
+ ToRelease<ICorDebugValue> pElementValue;
+ IfFailRet(pArrayValue->GetElementAtPosition(i, &pElementValue));
+ IfFailRet(PrintValue(pElementValue, pILFrame, pMD, indent + 1, varToExpand, currentExpansion, currentExpansionSize, currentFrame));
+ }
+
+ return S_OK;
+ }
+
+ static HRESULT PrintValue(ICorDebugValue * pInputValue, ICorDebugILFrame * pILFrame, IMetaDataImport * pMD, int indent, __in_z WCHAR* varToExpand, __inout_ecount(currentExpansionSize) WCHAR* currentExpansion, DWORD currentExpansionSize, int currentFrame)
+ {
+ HRESULT Status = S_OK;
+
+ BOOL isNull = TRUE;
+ ToRelease<ICorDebugValue> pValue;
+ IfFailRet(DereferenceAndUnboxValue(pInputValue, &pValue, &isNull));
+
+ if(isNull)
+ {
+ ExtOut(" = null\n");
+ return S_OK;
+ }
+
+ ULONG32 cbSize;
+ IfFailRet(pValue->GetSize(&cbSize));
+ ArrayHolder<BYTE> rgbValue = new NOTHROW BYTE[cbSize];
+ if (rgbValue == NULL)
+ {
+ ReportOOM();
+ return E_OUTOFMEMORY;
+ }
+
+ memset(rgbValue.GetPtr(), 0, cbSize * sizeof(BYTE));
+
+ CorElementType corElemType;
+ IfFailRet(pValue->GetType(&corElemType));
+ if (corElemType == ELEMENT_TYPE_STRING)
+ {
+ return PrintStringValue(pValue);
+ }
+
+ if (corElemType == ELEMENT_TYPE_SZARRAY)
+ {
+ return PrintSzArrayValue(pValue, pILFrame, pMD, indent, varToExpand, currentExpansion, currentExpansionSize, currentFrame);
+ }
+
+ ToRelease<ICorDebugGenericValue> pGenericValue;
+ IfFailRet(pValue->QueryInterface(IID_ICorDebugGenericValue, (LPVOID*) &pGenericValue));
+ IfFailRet(pGenericValue->GetValue((LPVOID) &(rgbValue[0])));
+
+ if(IsEnum(pValue))
+ {
+ Status = PrintEnumValue(pValue, rgbValue);
+ ExtOut("\n");
+ return Status;
+ }
+
+ switch (corElemType)
+ {
+ default:
+ ExtOut(" (Unhandled CorElementType: 0x%x)\n", corElemType);
+ break;
+
+ case ELEMENT_TYPE_PTR:
+ ExtOut(" = <pointer>\n");
+ break;
+
+ case ELEMENT_TYPE_FNPTR:
+ {
+ CORDB_ADDRESS addr = 0;
+ ToRelease<ICorDebugReferenceValue> pReferenceValue = NULL;
+ if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugReferenceValue, (LPVOID*) &pReferenceValue)))
+ pReferenceValue->GetValue(&addr);
+ ExtOut(" = <function pointer 0x%x>\n", addr);
+ }
+ break;
+
+ case ELEMENT_TYPE_VALUETYPE:
+ case ELEMENT_TYPE_CLASS:
+ CORDB_ADDRESS addr;
+ if(SUCCEEDED(pValue->GetAddress(&addr)))
+ {
+ ExtOut(" @ 0x%I64x\n", addr);
+ }
+ else
+ {
+ ExtOut("\n");
+ }
+ ProcessFields(pValue, NULL, pILFrame, indent + 1, varToExpand, currentExpansion, currentExpansionSize, currentFrame);
+ break;
+
+ case ELEMENT_TYPE_BOOLEAN:
+ ExtOut(" = %s\n", rgbValue[0] == 0 ? "false" : "true");
+ break;
+
+ case ELEMENT_TYPE_CHAR:
+ ExtOut(" = '%C'\n", *(WCHAR *) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_I1:
+ ExtOut(" = %d\n", *(char*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_U1:
+ ExtOut(" = %d\n", *(unsigned char*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_I2:
+ ExtOut(" = %hd\n", *(short*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_U2:
+ ExtOut(" = %hu\n", *(unsigned short*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_I:
+ ExtOut(" = %d\n", *(int*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_U:
+ ExtOut(" = %u\n", *(unsigned int*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_I4:
+ ExtOut(" = %d\n", *(int*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_U4:
+ ExtOut(" = %u\n", *(unsigned int*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_I8:
+ ExtOut(" = %I64d\n", *(__int64*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_U8:
+ ExtOut(" = %I64u\n", *(unsigned __int64*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_R4:
+ ExtOut(" = %f\n", (double) *(float*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_R8:
+ ExtOut(" = %f\n", *(double*) &(rgbValue[0]));
+ break;
+
+ case ELEMENT_TYPE_OBJECT:
+ ExtOut(" = object\n");
+ break;
+
+ // TODO: The following corElementTypes are not yet implemented here. Array
+ // might be interesting to add, though the others may be of rather limited use:
+ // ELEMENT_TYPE_ARRAY = 0x14, // MDARRAY <type> <rank> <bcount> <bound1> ... <lbcount> <lb1> ...
+ //
+ // ELEMENT_TYPE_GENERICINST = 0x15, // GENERICINST <generic type> <argCnt> <arg1> ... <argn>
+ }
+
+ return S_OK;
+ }
+
+ static HRESULT PrintParameters(BOOL bParams, BOOL bLocals, IMetaDataImport * pMD, mdTypeDef typeDef, mdMethodDef methodDef, ICorDebugILFrame * pILFrame, ICorDebugModule * pModule, __in_z WCHAR* varToExpand, int currentFrame)
+ {
+ HRESULT Status = S_OK;
+
+ ULONG cParams = 0;
+ ToRelease<ICorDebugValueEnum> pParamEnum;
+ IfFailRet(pILFrame->EnumerateArguments(&pParamEnum));
+ IfFailRet(pParamEnum->GetCount(&cParams));
+ if (cParams > 0 && bParams)
+ {
+ DWORD methAttr = 0;
+ IfFailRet(pMD->GetMethodProps(methodDef, NULL, NULL, 0, NULL, &methAttr, NULL, NULL, NULL, NULL));
+
+ ExtOut("\nPARAMETERS:\n");
+ for (ULONG i=0; i < cParams; i++)
+ {
+ ULONG paramNameLen = 0;
+ mdParamDef paramDef;
+ WCHAR paramName[mdNameLen] = W("\0");
+
+ if(i == 0 && (methAttr & mdStatic) == 0)
+ swprintf_s(paramName, mdNameLen, W("this\0"));
+ else
+ {
+ int idx = ((methAttr & mdStatic) == 0)? i : (i + 1);
+ if(SUCCEEDED(pMD->GetParamForMethodIndex(methodDef, idx, &paramDef)))
+ pMD->GetParamProps(paramDef, NULL, NULL, paramName, mdNameLen, &paramNameLen, NULL, NULL, NULL, NULL);
+ }
+ if(_wcslen(paramName) == 0)
+ swprintf_s(paramName, mdNameLen, W("param_%d\0"), i);
+
+ ToRelease<ICorDebugValue> pValue;
+ ULONG cArgsFetched;
+ Status = pParamEnum->Next(1, &pValue, &cArgsFetched);
+
+ if (FAILED(Status))
+ {
+ ExtOut(" + (Error 0x%x retrieving parameter '%S')\n", Status, paramName);
+ continue;
+ }
+
+ if (Status == S_FALSE)
+ {
+ break;
+ }
+
+ WCHAR typeName[mdNameLen] = W("\0");
+ GetTypeOfValue(pValue, typeName, mdNameLen);
+ DMLOut(" + %S %s", typeName, DMLManagedVar(paramName, currentFrame, paramName));
+
+ ToRelease<ICorDebugReferenceValue> pRefValue;
+ if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugReferenceValue, (void**)&pRefValue)) && pRefValue != NULL)
+ {
+ BOOL bIsNull = TRUE;
+ pRefValue->IsNull(&bIsNull);
+ if(bIsNull)
+ {
+ ExtOut(" = null\n");
+ continue;
+ }
+ }
+
+ WCHAR currentExpansion[mdNameLen];
+ swprintf_s(currentExpansion, mdNameLen, W("%s\0"), paramName);
+ if((Status=PrintValue(pValue, pILFrame, pMD, 0, varToExpand, currentExpansion, mdNameLen, currentFrame)) != S_OK)
+ ExtOut(" + (Error 0x%x printing parameter %d)\n", Status, i);
+ }
+ }
+ else if (cParams == 0 && bParams)
+ ExtOut("\nPARAMETERS: (none)\n");
+
+ ULONG cLocals = 0;
+ ToRelease<ICorDebugValueEnum> pLocalsEnum;
+ IfFailRet(pILFrame->EnumerateLocalVariables(&pLocalsEnum));
+ IfFailRet(pLocalsEnum->GetCount(&cLocals));
+ if (cLocals > 0 && bLocals)
+ {
+ bool symbolsAvailable = false;
+ SymbolReader symReader;
+ if(SUCCEEDED(symReader.LoadSymbols(pMD, pModule)))
+ symbolsAvailable = true;
+ ExtOut("\nLOCALS:\n");
+ for (ULONG i=0; i < cLocals; i++)
+ {
+ ULONG paramNameLen = 0;
+ WCHAR paramName[mdNameLen] = W("\0");
+
+ ToRelease<ICorDebugValue> pValue;
+ if(symbolsAvailable)
+ {
+ Status = symReader.GetNamedLocalVariable(pILFrame, i, paramName, mdNameLen, &pValue);
+ }
+ else
+ {
+ ULONG cArgsFetched;
+ Status = pLocalsEnum->Next(1, &pValue, &cArgsFetched);
+ }
+ if(_wcslen(paramName) == 0)
+ swprintf_s(paramName, mdNameLen, W("local_%d\0"), i);
+
+ if (FAILED(Status))
+ {
+ ExtOut(" + (Error 0x%x retrieving local variable '%S')\n", Status, paramName);
+ continue;
+ }
+
+ if (Status == S_FALSE)
+ {
+ break;
+ }
+
+ WCHAR typeName[mdNameLen] = W("\0");
+ GetTypeOfValue(pValue, typeName, mdNameLen);
+ DMLOut(" + %S %s", typeName, DMLManagedVar(paramName, currentFrame, paramName));
+
+ ToRelease<ICorDebugReferenceValue> pRefValue = NULL;
+ if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugReferenceValue, (void**)&pRefValue)) && pRefValue != NULL)
+ {
+ BOOL bIsNull = TRUE;
+ pRefValue->IsNull(&bIsNull);
+ if(bIsNull)
+ {
+ ExtOut(" = null\n");
+ continue;
+ }
+ }
+
+ WCHAR currentExpansion[mdNameLen];
+ swprintf_s(currentExpansion, mdNameLen, W("%s\0"), paramName);
+ if((Status=PrintValue(pValue, pILFrame, pMD, 0, varToExpand, currentExpansion, mdNameLen, currentFrame)) != S_OK)
+ ExtOut(" + (Error 0x%x printing local variable %d)\n", Status, i);
+ }
+ }
+ else if (cLocals == 0 && bLocals)
+ ExtOut("\nLOCALS: (none)\n");
+
+ if(bParams || bLocals)
+ ExtOut("\n");
+
+ return S_OK;
+ }
+
+ static HRESULT ProcessFields(ICorDebugValue* pInputValue, ICorDebugType* pTypeCast, ICorDebugILFrame * pILFrame, int indent, __in_z WCHAR* varToExpand, __inout_ecount(currentExpansionSize) WCHAR* currentExpansion, DWORD currentExpansionSize, int currentFrame)
+ {
+ if(!ShouldExpandVariable(varToExpand, currentExpansion)) return S_OK;
+ size_t currentExpansionLen = _wcslen(currentExpansion);
+
+ HRESULT Status = S_OK;
+
+ BOOL isNull = FALSE;
+ ToRelease<ICorDebugValue> pValue;
+ IfFailRet(DereferenceAndUnboxValue(pInputValue, &pValue, &isNull));
+
+ if(isNull) return S_OK;
+
+ mdTypeDef currentTypeDef;
+ ToRelease<ICorDebugClass> pClass;
+ ToRelease<ICorDebugValue2> pValue2;
+ ToRelease<ICorDebugType> pType;
+ ToRelease<ICorDebugModule> pModule;
+ IfFailRet(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2));
+ if(pTypeCast == NULL)
+ IfFailRet(pValue2->GetExactType(&pType));
+ else
+ {
+ pType = pTypeCast;
+ pType->AddRef();
+ }
+ IfFailRet(pType->GetClass(&pClass));
+ IfFailRet(pClass->GetModule(&pModule));
+ IfFailRet(pClass->GetToken(&currentTypeDef));
+
+ ToRelease<IUnknown> pMDUnknown;
+ ToRelease<IMetaDataImport> pMD;
+ IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
+ IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
+
+ WCHAR baseTypeName[mdNameLen] = W("\0");
+ ToRelease<ICorDebugType> pBaseType;
+ if(SUCCEEDED(pType->GetBase(&pBaseType)) && pBaseType != NULL && SUCCEEDED(GetTypeOfValue(pBaseType, baseTypeName, mdNameLen)))
+ {
+ if(_wcsncmp(baseTypeName, W("System.Enum"), 11) == 0)
+ return S_OK;
+ else if(_wcsncmp(baseTypeName, W("System.Object"), 13) != 0 && _wcsncmp(baseTypeName, W("System.ValueType"), 16) != 0)
+ {
+ currentExpansion[currentExpansionLen] = W('\0');
+ wcscat_s(currentExpansion, currentExpansionSize, W(".\0"));
+ wcscat_s(currentExpansion, currentExpansionSize, W("[basetype]"));
+ for(int i = 0; i < indent; i++) ExtOut(" ");
+ DMLOut(" |- %S %s\n", baseTypeName, DMLManagedVar(currentExpansion, currentFrame, W("[basetype]")));
+
+ if(ShouldExpandVariable(varToExpand, currentExpansion))
+ ProcessFields(pInputValue, pBaseType, pILFrame, indent + 1, varToExpand, currentExpansion, currentExpansionSize, currentFrame);
+ }
+ }
+
+
+ ULONG numFields = 0;
+ HCORENUM fEnum = NULL;
+ mdFieldDef fieldDef;
+ while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
+ {
+ ULONG nameLen = 0;
+ DWORD fieldAttr = 0;
+ WCHAR mdName[mdNameLen];
+ WCHAR typeName[mdNameLen];
+ if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, mdName, mdNameLen, &nameLen, &fieldAttr, NULL, NULL, NULL, NULL, NULL)))
+ {
+ currentExpansion[currentExpansionLen] = W('\0');
+ wcscat_s(currentExpansion, currentExpansionSize, W(".\0"));
+ wcscat_s(currentExpansion, currentExpansionSize, mdName);
+
+ ToRelease<ICorDebugValue> pFieldVal;
+ if(fieldAttr & fdLiteral)
+ {
+ //TODO: Is it worth it??
+ //ExtOut(" |- const %S", mdName);
+ }
+ else
+ {
+ for(int i = 0; i < indent; i++) ExtOut(" ");
+
+ if (fieldAttr & fdStatic)
+ pType->GetStaticFieldValue(fieldDef, pILFrame, &pFieldVal);
+ else
+ {
+ ToRelease<ICorDebugObjectValue> pObjValue;
+ if (SUCCEEDED(pValue->QueryInterface(IID_ICorDebugObjectValue, (LPVOID*) &pObjValue)))
+ pObjValue->GetFieldValue(pClass, fieldDef, &pFieldVal);
+ }
+
+ if(pFieldVal != NULL)
+ {
+ typeName[0] = L'\0';
+ GetTypeOfValue(pFieldVal, typeName, mdNameLen);
+ DMLOut(" |- %S %s", typeName, DMLManagedVar(currentExpansion, currentFrame, mdName));
+ PrintValue(pFieldVal, pILFrame, pMD, indent, varToExpand, currentExpansion, currentExpansionSize, currentFrame);
+ }
+ else if(!(fieldAttr & fdLiteral))
+ ExtOut(" |- < unknown type > %S\n", mdName);
+ }
+ }
+ }
+ pMD->CloseEnum(fEnum);
+ return S_OK;
+ }
+
+public:
+
+ // This is the main worker function used if !clrstack is called with "-i" to indicate
+ // that the public ICorDebug* should be used instead of the private DAC interface. NOTE:
+ // Currently only bParams is supported. NOTE: This is a work in progress and the
+ // following would be good to do:
+ // * More thorough testing with interesting stacks, especially with transitions into
+ // and out of managed code.
+ // * Consider interleaving this code back into the main body of !clrstack if it turns
+ // out that there's a lot of duplication of code between these two functions.
+ // (Still unclear how things will look once locals is implemented.)
+ static HRESULT ClrStackFromPublicInterface(BOOL bParams, BOOL bLocals, BOOL bSuppressLines, __in_z WCHAR* varToExpand = NULL, int onlyShowFrame = -1)
+ {
+ HRESULT Status;
+
+ IfFailRet(InitCorDebugInterface());
+
+ ExtOut("\n\n\nDumping managed stack and managed variables using ICorDebug.\n");
+ ExtOut("=============================================================================\n");
+
+ ToRelease<ICorDebugThread> pThread;
+ ToRelease<ICorDebugThread3> pThread3;
+ ToRelease<ICorDebugStackWalk> pStackWalk;
+ ULONG ulThreadID = 0;
+ g_ExtSystem->GetCurrentThreadSystemId(&ulThreadID);
+
+ IfFailRet(g_pCorDebugProcess->GetThread(ulThreadID, &pThread));
+ IfFailRet(pThread->QueryInterface(IID_ICorDebugThread3, (LPVOID *) &pThread3));
+ IfFailRet(pThread3->CreateStackWalk(&pStackWalk));
+
+ InternalFrameManager internalFrameManager;
+ IfFailRet(internalFrameManager.Init(pThread3));
+
+ #if defined(_AMD64_)
+ ExtOut("%-16s %-16s %s\n", "Child SP", "IP", "Call Site");
+ #elif defined(_X86_)
+ ExtOut("%-8s %-8s %s\n", "Child SP", "IP", "Call Site");
+ #endif
+
+ int currentFrame = -1;
+
+ for (Status = S_OK; ; Status = pStackWalk->Next())
+ {
+ currentFrame++;
+
+ if (Status == CORDBG_S_AT_END_OF_STACK)
+ {
+ ExtOut("Stack walk complete.\n");
+ break;
+ }
+ IfFailRet(Status);
+
+ if (IsInterrupt())
+ {
+ ExtOut("<interrupted>\n");
+ break;
+ }
+
+ CROSS_PLATFORM_CONTEXT context;
+ ULONG32 cbContextActual;
+ if ((Status=pStackWalk->GetContext(
+ DT_CONTEXT_FULL,
+ sizeof(context),
+ &cbContextActual,
+ (BYTE *)&context))!=S_OK)
+ {
+ ExtOut("GetFrameContext failed: %lx\n",Status);
+ break;
+ }
+
+ // First find the info for the Frame object, if the current frame has an associated clr!Frame.
+ CLRDATA_ADDRESS sp = GetSP(context);
+ CLRDATA_ADDRESS ip = GetIP(context);
+
+ ToRelease<ICorDebugFrame> pFrame;
+ IfFailRet(pStackWalk->GetFrame(&pFrame));
+ if (Status == S_FALSE)
+ {
+ DMLOut("%p %s [NativeStackFrame]\n", SOS_PTR(sp), DMLIP(ip));
+ continue;
+ }
+
+ // TODO: What about internal frames preceding the above native stack frame?
+ // Should I just exclude the above native stack frame from the output?
+ // TODO: Compare caller frame (instead of current frame) against internal frame,
+ // to deal with issues of current frame's current SP being closer to leaf than
+ // EE Frames it pushes. By "caller" I mean not just managed caller, but the
+ // very next non-internal frame dbi would return (native or managed). OR...
+ // perhaps I should use GetStackRange() instead, to see if the internal frame
+ // appears leafier than the base-part of the range of the currently iterated
+ // stack frame? I think I like that better.
+ _ASSERTE(pFrame != NULL);
+ IfFailRet(internalFrameManager.PrintPrecedingInternalFrames(pFrame));
+
+ // Print the stack and instruction pointers.
+ DMLOut("%p %s ", SOS_PTR(sp), DMLIP(ip));
+
+ ToRelease<ICorDebugRuntimeUnwindableFrame> pRuntimeUnwindableFrame;
+ Status = pFrame->QueryInterface(IID_ICorDebugRuntimeUnwindableFrame, (LPVOID *) &pRuntimeUnwindableFrame);
+ if (SUCCEEDED(Status))
+ {
+ ExtOut("[RuntimeUnwindableFrame]\n");
+ continue;
+ }
+
+ // Print the method/Frame info
+
+ // TODO: IS THE FOLLOWING NECESSARY, OR AM I GUARANTEED THAT ALL INTERNAL FRAMES
+ // CAN BE FOUND VIA GetActiveInternalFrames?
+ ToRelease<ICorDebugInternalFrame> pInternalFrame;
+ Status = pFrame->QueryInterface(IID_ICorDebugInternalFrame, (LPVOID *) &pInternalFrame);
+ if (SUCCEEDED(Status))
+ {
+ // This is a clr!Frame.
+ LPCWSTR pwszFrameName = W("TODO: Implement GetFrameName");
+ ExtOut("[%S: p] ", pwszFrameName);
+ }
+
+ // Print the frame's associated function info, if it has any.
+ ToRelease<ICorDebugILFrame> pILFrame;
+ HRESULT hrILFrame = pFrame->QueryInterface(IID_ICorDebugILFrame, (LPVOID*) &pILFrame);
+
+ if (SUCCEEDED(hrILFrame))
+ {
+ ToRelease<ICorDebugFunction> pFunction;
+ Status = pFrame->GetFunction(&pFunction);
+ if (FAILED(Status))
+ {
+ // We're on a JITted frame, but there's no Function for it. So it must
+ // be...
+ ExtOut("[IL Stub or LCG]\n");
+ continue;
+ }
+
+ ToRelease<ICorDebugClass> pClass;
+ ToRelease<ICorDebugModule> pModule;
+ mdMethodDef methodDef;
+ IfFailRet(pFunction->GetClass(&pClass));
+ IfFailRet(pFunction->GetModule(&pModule));
+ IfFailRet(pFunction->GetToken(&methodDef));
+
+ WCHAR wszModuleName[100];
+ ULONG32 cchModuleNameActual;
+ IfFailRet(pModule->GetName(_countof(wszModuleName), &cchModuleNameActual, wszModuleName));
+
+ ToRelease<IUnknown> pMDUnknown;
+ ToRelease<IMetaDataImport> pMD;
+ ToRelease<IMDInternalImport> pMDInternal;
+ IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
+ IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
+ IfFailRet(GetMDInternalFromImport(pMD, &pMDInternal));
+
+ mdTypeDef typeDef;
+ IfFailRet(pClass->GetToken(&typeDef));
+
+ // Note that we don't need to pretty print the class, as class name is
+ // already printed from GetMethodName below
+
+ CQuickBytes functionName;
+ // TODO: WARNING: GetMethodName() appears to include lots of unexercised
+ // code, as evidenced by some fundamental bugs I found. It should either be
+ // thoroughly reviewed, or some other more exercised code path to grab the
+ // name should be used.
+ // TODO: If we do stay with GetMethodName, it should be updated to print
+ // generics properly. Today, it does not show generic type parameters, and
+ // if any arguments have a generic type, those arguments are just shown as
+ // "__Canon", even when they're value types.
+ GetMethodName(methodDef, pMD, &functionName);
+
+ DMLOut(DMLManagedVar(W("-a"), currentFrame, (LPWSTR)functionName.Ptr()));
+ ExtOut(" (%S)\n", wszModuleName);
+
+ if (SUCCEEDED(hrILFrame) && (bParams || bLocals))
+ {
+ if(onlyShowFrame == -1 || (onlyShowFrame >= 0 && currentFrame == onlyShowFrame))
+ IfFailRet(PrintParameters(bParams, bLocals, pMD, typeDef, methodDef, pILFrame, pModule, varToExpand, currentFrame));
+ }
+ }
+ }
+ ExtOut("=============================================================================\n");
+
+#ifdef FEATURE_PAL
+ // Temporary until we get a process exit notification plumbed from lldb
+ UninitCorDebugInterface();
+#endif
+ return S_OK;
+ }
+};
+
+WString BuildRegisterOutput(const SOSStackRefData &ref, bool printObj)
+{
+ WString res;
+
+ if (ref.HasRegisterInformation)
+ {
+ WCHAR reg[32];
+ HRESULT hr = g_sos->GetRegisterName(ref.Register, _countof(reg), reg, NULL);
+ if (SUCCEEDED(hr))
+ res = reg;
+ else
+ res = W("<unknown register>");
+
+ if (ref.Offset)
+ {
+ int offset = ref.Offset;
+ if (offset > 0)
+ {
+ res += W("+");
+ }
+ else
+ {
+ res += W("-");
+ offset = -offset;
+ }
+
+ res += Hex(offset);
+ }
+
+ res += W(": ");
+ }
+
+ if (ref.Address)
+ res += WString(Pointer(ref.Address));
+
+ if (printObj)
+ {
+ if (ref.Address)
+ res += W(" -> ");
+
+ res += WString(ObjectPtr(ref.Object));
+ }
+
+ if (ref.Flags & SOSRefPinned)
+ {
+ res += W(" (pinned)");
+ }
+
+ if (ref.Flags & SOSRefInterior)
+ {
+ res += W(" (interior)");
+ }
+
+ return res;
+}
+
+void PrintRef(const SOSStackRefData &ref, TableOutput &out)
+{
+ WString res = BuildRegisterOutput(ref);
+
+ if (ref.Object && (ref.Flags & SOSRefInterior) == 0)
+ {
+ WCHAR type[128];
+ sos::BuildTypeWithExtraInfo(TO_TADDR(ref.Object), _countof(type), type);
+
+ res += WString(W(" - ")) + type;
+ }
+
+ out.WriteColumn(2, res);
+}
+
+
+class ClrStackImpl
+{
+public:
+ static void PrintThread(ULONG osID, BOOL bParams, BOOL bLocals, BOOL bSuppressLines, BOOL bGC, BOOL bFull, BOOL bDisplayRegVals)
+ {
+ // Symbols variables
+ ULONG symlines = 0; // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
+ if (!bSuppressLines && SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
+ {
+ symlines &= SYMOPT_LOAD_LINES;
+ }
+
+ if (symlines == 0)
+ bSuppressLines = TRUE;
+
+ ToRelease<IXCLRDataStackWalk> pStackWalk;
+
+ HRESULT hr = CreateStackWalk(osID, &pStackWalk);
+ if (FAILED(hr) || pStackWalk == NULL)
+ {
+ ExtOut("Failed to start stack walk: %lx\n", hr);
+ return;
+ }
+
+#ifdef _TARGET_WIN64_
+ PDEBUG_STACK_FRAME currentNativeFrame = NULL;
+ ULONG numNativeFrames = 0;
+ if (bFull)
+ {
+ hr = GetContextStackTrace(&numNativeFrames);
+ if (FAILED(hr))
+ {
+ ExtOut("Failed to get native stack frames: %lx\n", hr);
+ return;
+ }
+ currentNativeFrame = &g_Frames[0];
+ }
+#endif // _TARGET_WIN64_
+
+ unsigned int refCount = 0, errCount = 0;
+ ArrayHolder<SOSStackRefData> pRefs = NULL;
+ ArrayHolder<SOSStackRefError> pErrs = NULL;
+ if (bGC && FAILED(GetGCRefs(osID, &pRefs, &refCount, &pErrs, &errCount)))
+ refCount = 0;
+
+ TableOutput out(3, POINTERSIZE_HEX, AlignRight);
+ out.WriteRow("Child SP", "IP", "Call Site");
+
+ do
+ {
+ if (IsInterrupt())
+ {
+ ExtOut("<interrupted>\n");
+ break;
+ }
+ CLRDATA_ADDRESS ip = 0, sp = 0;
+ hr = GetFrameLocation(pStackWalk, &ip, &sp);
+
+ DacpFrameData FrameData;
+ HRESULT frameDataResult = FrameData.Request(pStackWalk);
+ if (SUCCEEDED(frameDataResult) && FrameData.frameAddr)
+ sp = FrameData.frameAddr;
+
+#ifdef _TARGET_WIN64_
+ while ((numNativeFrames > 0) && (currentNativeFrame->StackOffset <= sp))
+ {
+ if (currentNativeFrame->StackOffset != sp)
+ {
+ PrintNativeStackFrame(out, currentNativeFrame, bSuppressLines);
+ }
+ currentNativeFrame++;
+ numNativeFrames--;
+ }
+#endif // _TARGET_WIN64_
+
+ // Print the stack pointer.
+ out.WriteColumn(0, sp);
+
+ // Print the method/Frame info
+ if (SUCCEEDED(frameDataResult) && FrameData.frameAddr)
+ {
+ // Skip the instruction pointer because it doesn't really mean anything for method frames
+ out.WriteColumn(1, bFull ? String("") : NativePtr(ip));
+
+ // This is a clr!Frame.
+ out.WriteColumn(2, GetFrameFromAddress(TO_TADDR(FrameData.frameAddr), pStackWalk, bFull));
+
+ // Print out gc references for the Frame.
+ for (unsigned int i = 0; i < refCount; ++i)
+ if (pRefs[i].Source == sp)
+ PrintRef(pRefs[i], out);
+
+ // Print out an error message if we got one.
+ for (unsigned int i = 0; i < errCount; ++i)
+ if (pErrs[i].Source == sp)
+ out.WriteColumn(2, "Failed to enumerate GC references.");
+ }
+ else
+ {
+ out.WriteColumn(1, InstructionPtr(ip));
+ out.WriteColumn(2, MethodNameFromIP(ip, bSuppressLines, bFull, bFull));
+
+ // Print out gc references. refCount will be zero if bGC is false (or if we
+ // failed to fetch gc reference information).
+ for (unsigned int i = 0; i < refCount; ++i)
+ if (pRefs[i].Source == ip && pRefs[i].StackPointer == sp)
+ PrintRef(pRefs[i], out);
+
+ // Print out an error message if we got one.
+ for (unsigned int i = 0; i < errCount; ++i)
+ if (pErrs[i].Source == sp)
+ out.WriteColumn(2, "Failed to enumerate GC references.");
+
+ if (bParams || bLocals)
+ PrintArgsAndLocals(pStackWalk, bParams, bLocals);
+ }
+
+ if (bDisplayRegVals)
+ PrintManagedFrameContext(pStackWalk);
+
+ } while (pStackWalk->Next() == S_OK);
+
+#ifdef _TARGET_WIN64_
+ while (numNativeFrames > 0)
+ {
+ PrintNativeStackFrame(out, currentNativeFrame, bSuppressLines);
+ currentNativeFrame++;
+ numNativeFrames--;
+ }
+#endif // _TARGET_WIN64_
+ }
+
+ static HRESULT PrintManagedFrameContext(IXCLRDataStackWalk *pStackWalk)
+ {
+ CROSS_PLATFORM_CONTEXT context;
+ HRESULT hr = pStackWalk->GetContext(DT_CONTEXT_FULL, g_targetMachine->GetContextSize(), NULL, (BYTE *)&context);
+ if (FAILED(hr) || hr == S_FALSE)
+ {
+ // GetFrameContext returns S_FALSE if the frame iterator is invalid. That's basically an error for us.
+ ExtOut("GetFrameContext failed: %lx\n", hr);
+ return E_FAIL;
+ }
+
+#if defined(SOS_TARGET_AMD64)
+ String outputFormat3 = " %3s=%016x %3s=%016x %3s=%016x\n";
+ String outputFormat2 = " %3s=%016x %3s=%016x\n";
+ ExtOut(outputFormat3, "rsp", context.Amd64Context.Rsp, "rbp", context.Amd64Context.Rbp, "rip", context.Amd64Context.Rip);
+ ExtOut(outputFormat3, "rax", context.Amd64Context.Rax, "rbx", context.Amd64Context.Rbx, "rcx", context.Amd64Context.Rcx);
+ ExtOut(outputFormat3, "rdx", context.Amd64Context.Rdx, "rsi", context.Amd64Context.Rsi, "rdi", context.Amd64Context.Rdi);
+ ExtOut(outputFormat3, "r8", context.Amd64Context.R8, "r9", context.Amd64Context.R9, "r10", context.Amd64Context.R10);
+ ExtOut(outputFormat3, "r11", context.Amd64Context.R11, "r12", context.Amd64Context.R12, "r13", context.Amd64Context.R13);
+ ExtOut(outputFormat2, "r14", context.Amd64Context.R14, "r15", context.Amd64Context.R15);
+#elif defined(SOS_TARGET_X86)
+ String outputFormat3 = " %3s=%08x %3s=%08x %3s=%08x\n";
+ String outputFormat2 = " %3s=%08x %3s=%08x\n";
+ ExtOut(outputFormat3, "esp", context.X86Context.Esp, "ebp", context.X86Context.Ebp, "eip", context.X86Context.Eip);
+ ExtOut(outputFormat3, "eax", context.X86Context.Eax, "ebx", context.X86Context.Ebx, "ecx", context.X86Context.Ecx);
+ ExtOut(outputFormat3, "edx", context.X86Context.Edx, "esi", context.X86Context.Esi, "edi", context.X86Context.Edi);
+#elif defined(SOS_TARGET_ARM)
+ String outputFormat3 = " %3s=%08x %3s=%08x %3s=%08x\n";
+ String outputFormat2 = " %s=%08x %s=%08x\n";
+ String outputFormat1 = " %s=%08x\n";
+ ExtOut(outputFormat3, "r0", context.ArmContext.R0, "r1", context.ArmContext.R1, "r2", context.ArmContext.R2);
+ ExtOut(outputFormat3, "r3", context.ArmContext.R3, "r4", context.ArmContext.R4, "r5", context.ArmContext.R5);
+ ExtOut(outputFormat3, "r6", context.ArmContext.R6, "r7", context.ArmContext.R7, "r8", context.ArmContext.R8);
+ ExtOut(outputFormat3, "r9", context.ArmContext.R9, "r10", context.ArmContext.R10, "r11", context.ArmContext.R11);
+ ExtOut(outputFormat1, "r12", context.ArmContext.R12);
+ ExtOut(outputFormat3, "sp", context.ArmContext.Sp, "lr", context.ArmContext.Lr, "pc", context.ArmContext.Pc);
+ ExtOut(outputFormat2, "cpsr", context.ArmContext.Cpsr, "fpsr", context.ArmContext.Fpscr);
+#elif defined(SOS_TARGET_ARM64)
+ String outputXRegFormat3 = " x%d=%016x x%d=%016x x%d=%016x\n";
+ String outputXRegFormat1 = " x%d=%016x\n";
+ String outputFormat3 = " %s=%016x %s=%016x %s=%016x\n";
+ String outputFormat2 = " %s=%08x %s=%08x\n";
+ DWORD64 *X = context.Arm64Context.X;
+ for (int i = 0; i < 9; i++)
+ {
+ ExtOut(outputXRegFormat3, i + 0, X[i + 0], i + 1, X[i + 1], i + 2, X[i + 2]);
+ }
+ ExtOut(outputXRegFormat1, 28, X[28]);
+ ExtOut(outputFormat3, "sp", context.ArmContext.Sp, "lr", context.ArmContext.Lr, "pc", context.ArmContext.Pc);
+ ExtOut(outputFormat2, "cpsr", context.ArmContext.Cpsr, "fpsr", context.ArmContext.Fpscr);
+#else
+ ExtOut("Can't display register values for this platform\n");
+#endif
+ return S_OK;
+
+ }
+
+ static HRESULT GetFrameLocation(IXCLRDataStackWalk *pStackWalk, CLRDATA_ADDRESS *ip, CLRDATA_ADDRESS *sp)
+ {
+ CROSS_PLATFORM_CONTEXT context;
+ HRESULT hr = pStackWalk->GetContext(DT_CONTEXT_FULL, g_targetMachine->GetContextSize(), NULL, (BYTE *)&context);
+ if (FAILED(hr) || hr == S_FALSE)
+ {
+ // GetFrameContext returns S_FALSE if the frame iterator is invalid. That's basically an error for us.
+ ExtOut("GetFrameContext failed: %lx\n", hr);
+ return E_FAIL;
+ }
+
+ // First find the info for the Frame object, if the current frame has an associated clr!Frame.
+ *ip = GetIP(context);
+ *sp = GetSP(context);
+
+ if (IsDbgTargetArm())
+ *ip = *ip & ~THUMB_CODE;
+
+ return S_OK;
+ }
+
+ static void PrintNativeStackFrame(TableOutput out, PDEBUG_STACK_FRAME frame, BOOL bSuppressLines)
+ {
+ char filename[MAX_LONGPATH + 1];
+ char symbol[1024];
+ ULONG64 displacement;
+
+ ULONG64 ip = frame->InstructionOffset;
+
+ out.WriteColumn(0, frame->StackOffset);
+ out.WriteColumn(1, NativePtr(ip));
+
+ HRESULT hr = g_ExtSymbols->GetNameByOffset(TO_CDADDR(ip), symbol, _countof(symbol), NULL, &displacement);
+ if (SUCCEEDED(hr) && symbol[0] != '\0')
+ {
+ String frameOutput;
+ frameOutput += symbol;
+
+ if (displacement)
+ {
+ frameOutput += " + ";
+ frameOutput += Decimal(displacement);
+ }
+
+ if (!bSuppressLines)
+ {
+ ULONG line;
+ hr = g_ExtSymbols->GetLineByOffset(TO_CDADDR(ip), &line, filename, _countof(filename), NULL, NULL);
+ if (SUCCEEDED(hr))
+ {
+ frameOutput += " at ";
+ frameOutput += filename;
+ frameOutput += ":";
+ frameOutput += Decimal(line);
+ }
+ }
+
+ out.WriteColumn(2, frameOutput);
+ }
+ else
+ {
+ out.WriteColumn(2, "");
+ }
+ }
+
+ static void PrintCurrentThread(BOOL bParams, BOOL bLocals, BOOL bSuppressLines, BOOL bGC, BOOL bNative, BOOL bDisplayRegVals)
+ {
+ ULONG id = 0;
+ ULONG osid = 0;
+
+ g_ExtSystem->GetCurrentThreadSystemId(&osid);
+ ExtOut("OS Thread Id: 0x%x ", osid);
+ g_ExtSystem->GetCurrentThreadId(&id);
+ ExtOut("(%d)\n", id);
+
+ PrintThread(osid, bParams, bLocals, bSuppressLines, bGC, bNative, bDisplayRegVals);
+ }
+private:
+
+ static HRESULT CreateStackWalk(ULONG osID, IXCLRDataStackWalk **ppStackwalk)
+ {
+ HRESULT hr = S_OK;
+ ToRelease<IXCLRDataTask> pTask;
+
+ if ((hr = g_ExtSystem->GetCurrentThreadSystemId(&osID)) != S_OK ||
+ (hr = g_clrData->GetTaskByOSThreadID(osID, &pTask)) != S_OK)
+ {
+ ExtOut("Unable to walk the managed stack. The current thread is likely not a \n");
+ ExtOut("managed thread. You can run !threads to get a list of managed threads in\n");
+ ExtOut("the process\n");
+ return hr;
+ }
+
+ return pTask->CreateStackWalk(CLRDATA_SIMPFRAME_UNRECOGNIZED |
+ CLRDATA_SIMPFRAME_MANAGED_METHOD |
+ CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE |
+ CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE,
+ ppStackwalk);
+ }
+
+ /* Prints the args and locals of for a thread's stack.
+ * Params:
+ * pStackWalk - the stack we are printing
+ * bArgs - whether to print args
+ * bLocals - whether to print locals
+ */
+ static void PrintArgsAndLocals(IXCLRDataStackWalk *pStackWalk, BOOL bArgs, BOOL bLocals)
+ {
+ ToRelease<IXCLRDataFrame> pFrame;
+ ToRelease<IXCLRDataValue> pVal;
+ ULONG32 argCount = 0;
+ ULONG32 localCount = 0;
+ HRESULT hr = S_OK;
+
+ hr = pStackWalk->GetFrame(&pFrame);
+
+ // Print arguments
+ if (SUCCEEDED(hr) && bArgs)
+ hr = pFrame->GetNumArguments(&argCount);
+
+ if (SUCCEEDED(hr) && bArgs)
+ hr = ShowArgs(argCount, pFrame, pVal);
+
+ // Print locals
+ if (SUCCEEDED(hr) && bLocals)
+ hr = pFrame->GetNumLocalVariables(&localCount);
+
+ if (SUCCEEDED(hr) && bLocals)
+ ShowLocals(localCount, pFrame, pVal);
+
+ ExtOut("\n");
+ }
+
+
+
+ /* Displays the arguments to a function
+ * Params:
+ * argy - the number of arguments the function has
+ * pFramey - the frame we are inspecting
+ * pVal - a pointer to the CLRDataValue we use to query for info about the args
+ */
+ static HRESULT ShowArgs(ULONG32 argy, IXCLRDataFrame *pFramey, IXCLRDataValue *pVal)
+ {
+ CLRDATA_ADDRESS addr = 0;
+ BOOL fPrintedLocation = FALSE;
+ ULONG64 outVar = 0;
+ ULONG32 tmp;
+ HRESULT hr = S_OK;
+
+ ArrayHolder<WCHAR> argName = new NOTHROW WCHAR[mdNameLen];
+ if (!argName)
+ {
+ ReportOOM();
+ return E_FAIL;
+ }
+
+ for (ULONG32 i=0; i < argy; i++)
+ {
+ if (i == 0)
+ {
+ ExtOut(" PARAMETERS:\n");
+ }
+
+ hr = pFramey->GetArgumentByIndex(i,
+ &pVal,
+ mdNameLen,
+ &tmp,
+ argName);
+
+ if (FAILED(hr))
+ return hr;
+
+ ExtOut(" ");
+
+ if (argName[0] != L'\0')
+ {
+ ExtOut("%S ", argName.GetPtr());
+ }
+
+ // At times we cannot print the value of a parameter (most
+ // common case being a non-primitive value type). In these
+ // cases we need to print the location of the parameter,
+ // so that we can later examine it (e.g. using !dumpvc)
+ {
+ bool result = SUCCEEDED(pVal->GetNumLocations(&tmp)) && tmp == 1;
+ if (result)
+ result = SUCCEEDED(pVal->GetLocationByIndex(0, &tmp, &addr));
+
+ if (result)
+ {
+ if (tmp == CLRDATA_VLOC_REGISTER)
+ {
+ ExtOut("(<CLR reg>) ");
+ }
+ else
+ {
+ ExtOut("(0x%p) ", SOS_PTR(CDA_TO_UL64(addr)));
+ }
+ fPrintedLocation = TRUE;
+ }
+ }
+
+ if (argName[0] != L'\0' || fPrintedLocation)
+ {
+ ExtOut("= ");
+ }
+
+ if (HRESULT_CODE(pVal->GetBytes(0,&tmp,NULL)) == ERROR_BUFFER_OVERFLOW)
+ {
+ ArrayHolder<BYTE> pByte = new NOTHROW BYTE[tmp + 1];
+ if (pByte == NULL)
+ {
+ ReportOOM();
+ return E_FAIL;
+ }
+
+ hr = pVal->GetBytes(tmp, &tmp, pByte);
+
+ if (FAILED(hr))
+ {
+ ExtOut("<unable to retrieve data>\n");
+ }
+ else
+ {
+ switch(tmp)
+ {
+ case 1: outVar = *((BYTE *)pByte.GetPtr()); break;
+ case 2: outVar = *((short *)pByte.GetPtr()); break;
+ case 4: outVar = *((DWORD *)pByte.GetPtr()); break;
+ case 8: outVar = *((ULONG64 *)pByte.GetPtr()); break;
+ default: outVar = 0;
+ }
+
+ if (outVar)
+ DMLOut("0x%s\n", DMLObject(outVar));
+ else
+ ExtOut("0x%p\n", SOS_PTR(outVar));
+ }
+
+ }
+ else
+ {
+ ExtOut("<no data>\n");
+ }
+
+ pVal->Release();
+ }
+
+ return S_OK;
+ }
+
+
+ /* Prints the locals of a frame.
+ * Params:
+ * localy - the number of locals in the frame
+ * pFramey - the frame we are inspecting
+ * pVal - a pointer to the CLRDataValue we use to query for info about the args
+ */
+ static HRESULT ShowLocals(ULONG32 localy, IXCLRDataFrame *pFramey, IXCLRDataValue *pVal)
+ {
+ for (ULONG32 i=0; i < localy; i++)
+ {
+ if (i == 0)
+ ExtOut(" LOCALS:\n");
+
+ HRESULT hr;
+ ExtOut(" ");
+
+ // local names don't work in Whidbey.
+ hr = pFramey->GetLocalVariableByIndex(i, &pVal, mdNameLen, NULL, g_mdName);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ ULONG32 numLocations;
+ if (SUCCEEDED(pVal->GetNumLocations(&numLocations)) &&
+ numLocations == 1)
+ {
+ ULONG32 flags;
+ CLRDATA_ADDRESS addr;
+ if (SUCCEEDED(pVal->GetLocationByIndex(0, &flags, &addr)))
+ {
+ if (flags == CLRDATA_VLOC_REGISTER)
+ {
+ ExtOut("<CLR reg> ");
+ }
+ else
+ {
+ ExtOut("0x%p ", SOS_PTR(CDA_TO_UL64(addr)));
+ }
+ }
+
+ // Can I get a name for the item?
+
+ ExtOut("= ");
+ }
+ ULONG32 dwSize = 0;
+ hr = pVal->GetBytes(0, &dwSize, NULL);
+
+ if (HRESULT_CODE(hr) == ERROR_BUFFER_OVERFLOW)
+ {
+ ArrayHolder<BYTE> pByte = new NOTHROW BYTE[dwSize + 1];
+ if (pByte == NULL)
+ {
+ ReportOOM();
+ return E_FAIL;
+ }
+
+ hr = pVal->GetBytes(dwSize,&dwSize,pByte);
+
+ if (FAILED(hr))
+ {
+ ExtOut("<unable to retrieve data>\n");
+ }
+ else
+ {
+ ULONG64 outVar = 0;
+ switch(dwSize)
+ {
+ case 1: outVar = *((BYTE *) pByte.GetPtr()); break;
+ case 2: outVar = *((short *) pByte.GetPtr()); break;
+ case 4: outVar = *((DWORD *) pByte.GetPtr()); break;
+ case 8: outVar = *((ULONG64 *) pByte.GetPtr()); break;
+ default: outVar = 0;
+ }
+
+ if (outVar)
+ DMLOut("0x%s\n", DMLObject(outVar));
+ else
+ ExtOut("0x%p\n", SOS_PTR(outVar));
+ }
+ }
+ else
+ {
+ ExtOut("<no data>\n");
+ }
+
+ pVal->Release();
+ }
+
+ return S_OK;
+ }
+
+};
+
+#ifndef FEATURE_PAL
+
+WatchCmd g_watchCmd;
+
+// The grand new !Watch command, private to Apollo for now
+DECLARE_API(Watch)
+{
+ INIT_API_NOEE();
+ BOOL bExpression = FALSE;
+ StringHolder addExpression;
+ StringHolder aExpression;
+ StringHolder saveName;
+ StringHolder sName;
+ StringHolder expression;
+ StringHolder filterName;
+ StringHolder renameOldName;
+ size_t expandIndex = -1;
+ size_t removeIndex = -1;
+ BOOL clear = FALSE;
+
+ size_t nArg = 0;
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-add", &addExpression.data, COSTRING, TRUE},
+ {"-a", &aExpression.data, COSTRING, TRUE},
+ {"-save", &saveName.data, COSTRING, TRUE},
+ {"-s", &sName.data, COSTRING, TRUE},
+ {"-clear", &clear, COBOOL, FALSE},
+ {"-c", &clear, COBOOL, FALSE},
+ {"-expand", &expandIndex, COSIZE_T, TRUE},
+ {"-filter", &filterName.data, COSTRING, TRUE},
+ {"-r", &removeIndex, COSIZE_T, TRUE},
+ {"-remove", &removeIndex, COSIZE_T, TRUE},
+ {"-rename", &renameOldName.data, COSTRING, TRUE},
+ };
+
+ CMDValue arg[] =
+ { // vptr, type
+ {&expression.data, COSTRING}
+ };
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ if(addExpression.data != NULL || aExpression.data != NULL)
+ {
+ WCHAR pAddExpression[MAX_EXPRESSION];
+ swprintf_s(pAddExpression, MAX_EXPRESSION, W("%S"), addExpression.data != NULL ? addExpression.data : aExpression.data);
+ Status = g_watchCmd.Add(pAddExpression);
+ }
+ else if(removeIndex != -1)
+ {
+ if(removeIndex <= 0)
+ {
+ ExtOut("Index must be a postive decimal number\n");
+ }
+ else
+ {
+ Status = g_watchCmd.Remove((int)removeIndex);
+ if(Status == S_OK)
+ ExtOut("Watch expression #%d has been removed\n", removeIndex);
+ else if(Status == S_FALSE)
+ ExtOut("There is no watch expression with index %d\n", removeIndex);
+ else
+ ExtOut("Unknown failure 0x%x removing watch expression\n", Status);
+ }
+ }
+ else if(saveName.data != NULL || sName.data != NULL)
+ {
+ WCHAR pSaveName[MAX_EXPRESSION];
+ swprintf_s(pSaveName, MAX_EXPRESSION, W("%S"), saveName.data != NULL ? saveName.data : sName.data);
+ Status = g_watchCmd.SaveList(pSaveName);
+ }
+ else if(clear)
+ {
+ g_watchCmd.Clear();
+ }
+ else if(renameOldName.data != NULL)
+ {
+ if(nArg != 1)
+ {
+ ExtOut("Must provide an old and new name. Usage: !watch -rename <old_name> <new_name>.\n");
+ return S_FALSE;
+ }
+ WCHAR pOldName[MAX_EXPRESSION];
+ swprintf_s(pOldName, MAX_EXPRESSION, W("%S"), renameOldName.data);
+ WCHAR pNewName[MAX_EXPRESSION];
+ swprintf_s(pNewName, MAX_EXPRESSION, W("%S"), expression.data);
+ g_watchCmd.RenameList(pOldName, pNewName);
+ }
+ // print the tree, possibly with filtering and/or expansion
+ else if(expandIndex != -1 || expression.data == NULL)
+ {
+ WCHAR pExpression[MAX_EXPRESSION];
+ pExpression[0] = '\0';
+
+ if(expandIndex != -1)
+ {
+ if(expression.data != NULL)
+ {
+ swprintf_s(pExpression, MAX_EXPRESSION, W("%S"), expression.data);
+ }
+ else
+ {
+ ExtOut("No expression was provided. Usage !watch -expand <index> <expression>\n");
+ return S_FALSE;
+ }
+ }
+ WCHAR pFilterName[MAX_EXPRESSION];
+ pFilterName[0] = '\0';
+
+ if(filterName.data != NULL)
+ {
+ swprintf_s(pFilterName, MAX_EXPRESSION, W("%S"), filterName.data);
+ }
+
+ g_watchCmd.Print((int)expandIndex, pExpression, pFilterName);
+ }
+ else
+ {
+ ExtOut("Unrecognized argument: %s\n", expression.data);
+ }
+
+ return Status;
+}
+
+#endif // FEATURE_PAL
+
+DECLARE_API(ClrStack)
+{
+ INIT_API();
+
+ BOOL bAll = FALSE;
+ BOOL bParams = FALSE;
+ BOOL bLocals = FALSE;
+ BOOL bSuppressLines = FALSE;
+ BOOL bICorDebug = FALSE;
+ BOOL bGC = FALSE;
+ BOOL dml = FALSE;
+ BOOL bFull = FALSE;
+ BOOL bDisplayRegVals = FALSE;
+ DWORD frameToDumpVariablesFor = -1;
+ StringHolder cvariableName;
+ ArrayHolder<WCHAR> wvariableName = new NOTHROW WCHAR[mdNameLen];
+ if (wvariableName == NULL)
+ {
+ ReportOOM();
+ return E_OUTOFMEMORY;
+ }
+
+ memset(wvariableName, 0, sizeof(wvariableName));
+
+ size_t nArg = 0;
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-a", &bAll, COBOOL, FALSE},
+ {"-p", &bParams, COBOOL, FALSE},
+ {"-l", &bLocals, COBOOL, FALSE},
+ {"-n", &bSuppressLines, COBOOL, FALSE},
+ {"-i", &bICorDebug, COBOOL, FALSE},
+ {"-gc", &bGC, COBOOL, FALSE},
+ {"-f", &bFull, COBOOL, FALSE},
+ {"-r", &bDisplayRegVals, COBOOL, FALSE },
+#ifndef FEATURE_PAL
+ {"/d", &dml, COBOOL, FALSE},
+#endif
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&cvariableName.data, COSTRING},
+ {&frameToDumpVariablesFor, COSIZE_T},
+ };
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ if (bAll || bParams || bLocals)
+ {
+ // No parameter or local supports for minidump case!
+ MINIDUMP_NOT_SUPPORTED();
+ }
+
+ if (bAll)
+ {
+ bParams = bLocals = TRUE;
+ }
+
+ if (bICorDebug)
+ {
+ if(nArg > 0)
+ {
+ bool firstParamIsNumber = true;
+ for(DWORD i = 0; i < strlen(cvariableName.data); i++)
+ firstParamIsNumber = firstParamIsNumber && isdigit(cvariableName.data[i]);
+
+ if(firstParamIsNumber && nArg == 1)
+ {
+ frameToDumpVariablesFor = (DWORD)GetExpression(cvariableName.data);
+ cvariableName.data[0] = '\0';
+ }
+ }
+ if(cvariableName.data != NULL && strlen(cvariableName.data) > 0)
+ swprintf_s(wvariableName, mdNameLen, W("%S\0"), cvariableName.data);
+
+ if(_wcslen(wvariableName) > 0)
+ bParams = bLocals = TRUE;
+
+ EnableDMLHolder dmlHolder(TRUE);
+ return ClrStackImplWithICorDebug::ClrStackFromPublicInterface(bParams, bLocals, FALSE, wvariableName, frameToDumpVariablesFor);
+ }
+
+ ClrStackImpl::PrintCurrentThread(bParams, bLocals, bSuppressLines, bGC, bFull, bDisplayRegVals);
+
+ return S_OK;
+}
+
+#ifndef FEATURE_PAL
+
+BOOL IsMemoryInfoAvailable()
+{
+ ULONG Class;
+ ULONG Qualifier;
+ g_ExtControl->GetDebuggeeType(&Class,&Qualifier);
+ if (Qualifier == DEBUG_DUMP_SMALL)
+ {
+ g_ExtControl->GetDumpFormatFlags(&Qualifier);
+ if ((Qualifier & DEBUG_FORMAT_USER_SMALL_FULL_MEMORY) == 0)
+ {
+ if ((Qualifier & DEBUG_FORMAT_USER_SMALL_FULL_MEMORY_INFO) == 0)
+ {
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+DECLARE_API( VMMap )
+{
+ INIT_API();
+
+ if (IsMiniDumpFile() || !IsMemoryInfoAvailable())
+ {
+ ExtOut("!VMMap requires a full memory dump (.dump /ma) or a live process.\n");
+ }
+ else
+ {
+ vmmap();
+ }
+
+ return Status;
+} // DECLARE_API( vmmap )
+
+DECLARE_API( SOSFlush )
+{
+ INIT_API();
+
+ g_clrData->Flush();
+
+ return Status;
+} // DECLARE_API( SOSFlush )
+
+DECLARE_API( VMStat )
+{
+ INIT_API();
+
+ if (IsMiniDumpFile() || !IsMemoryInfoAvailable())
+ {
+ ExtOut("!VMStat requires a full memory dump (.dump /ma) or a live process.\n");
+ }
+ else
+ {
+ vmstat();
+ }
+
+ return Status;
+} // DECLARE_API( vmmap )
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function saves a dll to a file. *
+* *
+\**********************************************************************/
+DECLARE_API(SaveModule)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ StringHolder Location;
+ DWORD_PTR moduleAddr = NULL;
+ BOOL bIsImage;
+
+ CMDValue arg[] =
+ { // vptr, type
+ {&moduleAddr, COHEX},
+ {&Location.data, COSTRING}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+ if (nArg != 2)
+ {
+ ExtOut("Usage: SaveModule <address> <file to save>\n");
+ return Status;
+ }
+ if (moduleAddr == 0) {
+ ExtOut ("Invalid arg\n");
+ return Status;
+ }
+
+ char* ptr = Location.data;
+
+ DWORD_PTR dllBase = 0;
+ ULONG64 base;
+ if (g_ExtSymbols->GetModuleByOffset(TO_CDADDR(moduleAddr),0,NULL,&base) == S_OK)
+ {
+ dllBase = TO_TADDR(base);
+ }
+ else if (IsModule(moduleAddr))
+ {
+ DacpModuleData module;
+ module.Request(g_sos, TO_CDADDR(moduleAddr));
+ dllBase = TO_TADDR(module.ilBase);
+ if (dllBase == 0)
+ {
+ ExtOut ("Module does not have base address\n");
+ return Status;
+ }
+ }
+ else
+ {
+ ExtOut ("%p is not a Module or base address\n", SOS_PTR(moduleAddr));
+ return Status;
+ }
+
+ MEMORY_BASIC_INFORMATION64 mbi;
+ if (FAILED(g_ExtData2->QueryVirtual(TO_CDADDR(dllBase), &mbi)))
+ {
+ ExtOut("Failed to retrieve information about segment %p", SOS_PTR(dllBase));
+ return Status;
+ }
+
+ // module loaded as an image or mapped as a flat file?
+ bIsImage = (mbi.Type == MEM_IMAGE);
+
+ IMAGE_DOS_HEADER DosHeader;
+ if (g_ExtData->ReadVirtual(TO_CDADDR(dllBase), &DosHeader, sizeof(DosHeader), NULL) != S_OK)
+ return S_FALSE;
+
+ IMAGE_NT_HEADERS Header;
+ if (g_ExtData->ReadVirtual(TO_CDADDR(dllBase + DosHeader.e_lfanew), &Header, sizeof(Header), NULL) != S_OK)
+ return S_FALSE;
+
+ DWORD_PTR sectionAddr = dllBase + DosHeader.e_lfanew + offsetof(IMAGE_NT_HEADERS,OptionalHeader)
+ + Header.FileHeader.SizeOfOptionalHeader;
+
+ IMAGE_SECTION_HEADER section;
+ struct MemLocation
+ {
+ DWORD_PTR VAAddr;
+ DWORD_PTR VASize;
+ DWORD_PTR FileAddr;
+ DWORD_PTR FileSize;
+ };
+
+ int nSection = Header.FileHeader.NumberOfSections;
+ ExtOut("%u sections in file\n",nSection);
+ MemLocation *memLoc = (MemLocation*)_alloca(nSection*sizeof(MemLocation));
+ int indxSec = -1;
+ int slot;
+ for (int n = 0; n < nSection; n++)
+ {
+ if (g_ExtData->ReadVirtual(TO_CDADDR(sectionAddr), &section, sizeof(section), NULL) == S_OK)
+ {
+ for (slot = 0; slot <= indxSec; slot ++)
+ if (section.PointerToRawData < memLoc[slot].FileAddr)
+ break;
+
+ for (int k = indxSec; k >= slot; k --)
+ memcpy(&memLoc[k+1], &memLoc[k], sizeof(MemLocation));
+
+ memLoc[slot].VAAddr = section.VirtualAddress;
+ memLoc[slot].VASize = section.Misc.VirtualSize;
+ memLoc[slot].FileAddr = section.PointerToRawData;
+ memLoc[slot].FileSize = section.SizeOfRawData;
+ ExtOut("section %d - VA=%x, VASize=%x, FileAddr=%x, FileSize=%x\n",
+ n, memLoc[slot].VAAddr,memLoc[slot]. VASize,memLoc[slot].FileAddr,
+ memLoc[slot].FileSize);
+ indxSec ++;
+ }
+ else
+ {
+ ExtOut("Fail to read PE section info\n");
+ return Status;
+ }
+ sectionAddr += sizeof(section);
+ }
+
+ if (ptr[0] == '\0')
+ {
+ ExtOut ("File not specified\n");
+ return Status;
+ }
+
+ PCSTR file = ptr;
+ ptr += strlen(ptr)-1;
+ while (isspace(*ptr))
+ {
+ *ptr = '\0';
+ ptr --;
+ }
+
+ HANDLE hFile = CreateFileA(file,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,0,NULL);
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ ExtOut ("Fail to create file %s\n", file);
+ return Status;
+ }
+
+ ULONG pageSize = OSPageSize();
+ char *buffer = (char *)_alloca(pageSize);
+ DWORD nRead;
+ DWORD nWrite;
+
+ // NT PE Headers
+ TADDR dwAddr = dllBase;
+ TADDR dwEnd = dllBase + Header.OptionalHeader.SizeOfHeaders;
+ while (dwAddr < dwEnd)
+ {
+ nRead = pageSize;
+ if (dwEnd - dwAddr < nRead)
+ nRead = (ULONG)(dwEnd - dwAddr);
+
+ if (g_ExtData->ReadVirtual(TO_CDADDR(dwAddr), buffer, nRead, &nRead) == S_OK)
+ {
+ WriteFile(hFile,buffer,nRead,&nWrite,NULL);
+ }
+ else
+ {
+ ExtOut ("Fail to read memory\n");
+ goto end;
+ }
+ dwAddr += nRead;
+ }
+
+ for (slot = 0; slot <= indxSec; slot ++)
+ {
+ dwAddr = dllBase + (bIsImage ? memLoc[slot].VAAddr : memLoc[slot].FileAddr);
+ dwEnd = memLoc[slot].FileSize + dwAddr - 1;
+
+ while (dwAddr <= dwEnd)
+ {
+ nRead = pageSize;
+ if (dwEnd - dwAddr + 1 < pageSize)
+ nRead = (ULONG)(dwEnd - dwAddr + 1);
+
+ if (g_ExtData->ReadVirtual(TO_CDADDR(dwAddr), buffer, nRead, &nRead) == S_OK)
+ {
+ WriteFile(hFile,buffer,nRead,&nWrite,NULL);
+ }
+ else
+ {
+ ExtOut ("Fail to read memory\n");
+ goto end;
+ }
+ dwAddr += pageSize;
+ }
+ }
+end:
+ CloseHandle (hFile);
+ return Status;
+}
+
+#ifdef _DEBUG
+DECLARE_API(dbgout)
+{
+ INIT_API();
+
+ BOOL bOff = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-off", &bOff, COBOOL, FALSE},
+ };
+
+ if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
+ {
+ return Status;
+ }
+
+ Output::SetDebugOutputEnabled(!bOff);
+ return Status;
+}
+DECLARE_API(filthint)
+{
+ INIT_API();
+
+ BOOL bOff = FALSE;
+ DWORD_PTR filter = 0;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-off", &bOff, COBOOL, FALSE},
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&filter, COHEX}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option),
+ arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+ if (bOff)
+ {
+ g_filterHint = 0;
+ return Status;
+ }
+
+ g_filterHint = filter;
+ return Status;
+}
+#endif // _DEBUG
+
+#endif // FEATURE_PAL
+
+static HRESULT DumpMDInfoBuffer(DWORD_PTR dwStartAddr, DWORD Flags, ULONG64 Esp,
+ ULONG64 IPAddr, StringOutput& so)
+{
+#define DOAPPEND(str) \
+ do { \
+ if (!so.Append((str))) { \
+ return E_OUTOFMEMORY; \
+ }} while (0)
+
+ // Should we skip explicit frames? They are characterized by Esp = 0, && Eip = 0 or 1.
+ // See comment in FormatGeneratedException() for explanation why on non_IA64 Eip is 1, and not 0
+ if (!(Flags & SOS_STACKTRACE_SHOWEXPLICITFRAMES) && (Esp == 0) && (IPAddr == 1))
+ {
+ return S_FALSE;
+ }
+
+ DacpMethodDescData MethodDescData;
+ if (MethodDescData.Request(g_sos, TO_CDADDR(dwStartAddr)) != S_OK)
+ {
+ return E_FAIL;
+ }
+
+ ArrayHolder<WCHAR> wszNameBuffer = new WCHAR[MAX_LONGPATH+1];
+
+ if (Flags & SOS_STACKTRACE_SHOWADDRESSES)
+ {
+ _snwprintf_s(wszNameBuffer, MAX_LONGPATH, MAX_LONGPATH, W("%p %p "), (void*)(size_t) Esp, (void*)(size_t) IPAddr); // _TRUNCATE
+ DOAPPEND(wszNameBuffer);
+ }
+
+ DacpModuleData dmd;
+ BOOL bModuleNameWorked = FALSE;
+ ULONG64 addrInModule = IPAddr;
+ if (dmd.Request(g_sos, MethodDescData.ModulePtr) == S_OK)
+ {
+ CLRDATA_ADDRESS base = 0;
+ if (g_sos->GetPEFileBase(dmd.File, &base) == S_OK)
+ {
+ if (base)
+ {
+ addrInModule = base;
+ }
+ }
+ }
+ ULONG Index;
+ ULONG64 base;
+ if (g_ExtSymbols->GetModuleByOffset(UL64_TO_CDA(addrInModule), 0, &Index, &base) == S_OK)
+ {
+ ArrayHolder<char> szModuleName = new char[MAX_LONGPATH+1];
+ if (g_ExtSymbols->GetModuleNames(Index, base, NULL, 0, NULL, szModuleName, MAX_LONGPATH, NULL, NULL, 0, NULL) == S_OK)
+ {
+ MultiByteToWideChar (CP_ACP, 0, szModuleName, MAX_LONGPATH, wszNameBuffer, MAX_LONGPATH);
+ DOAPPEND (wszNameBuffer);
+ bModuleNameWorked = TRUE;
+ }
+ }
+#ifdef FEATURE_PAL
+ else
+ {
+ if (g_sos->GetPEFileName(dmd.File, MAX_LONGPATH, wszNameBuffer, NULL) == S_OK)
+ {
+ if (wszNameBuffer[0] != W('\0'))
+ {
+ WCHAR *pJustName = _wcsrchr(wszNameBuffer, DIRECTORY_SEPARATOR_CHAR_W);
+ if (pJustName == NULL)
+ pJustName = wszNameBuffer - 1;
+
+ DOAPPEND(pJustName + 1);
+ bModuleNameWorked = TRUE;
+ }
+ }
+ }
+#endif // FEATURE_PAL
+
+ // Under certain circumstances DacpMethodDescData::GetMethodDescName()
+ // returns a module qualified method name
+ HRESULT hr = g_sos->GetMethodDescName(dwStartAddr, MAX_LONGPATH, wszNameBuffer, NULL);
+
+ WCHAR* pwszMethNameBegin = (hr != S_OK ? NULL : _wcschr(wszNameBuffer, L'!'));
+ if (!bModuleNameWorked && hr == S_OK && pwszMethNameBegin != NULL)
+ {
+ // if we weren't able to get the module name, but GetMethodDescName returned
+ // the module as part of the returned method name, use this data
+ DOAPPEND(wszNameBuffer);
+ }
+ else
+ {
+ if (!bModuleNameWorked)
+ {
+ DOAPPEND (W("UNKNOWN"));
+ }
+ DOAPPEND(W("!"));
+ if (hr == S_OK)
+ {
+ // the module name we retrieved above from debugger will take
+ // precedence over the name possibly returned by GetMethodDescName()
+ DOAPPEND(pwszMethNameBegin != NULL ? (pwszMethNameBegin+1) : (WCHAR *)wszNameBuffer);
+ }
+ else
+ {
+ DOAPPEND(W("UNKNOWN"));
+ }
+ }
+
+ ULONG64 Displacement = (IPAddr - MethodDescData.NativeCodeAddr);
+ if (Displacement)
+ {
+ _snwprintf_s(wszNameBuffer, MAX_LONGPATH, MAX_LONGPATH, W("+%#x"), Displacement); // _TRUNCATE
+ DOAPPEND (wszNameBuffer);
+ }
+
+ return S_OK;
+#undef DOAPPEND
+}
+
+BOOL AppendContext(LPVOID pTransitionContexts, size_t maxCount, size_t *pcurCount, size_t uiSizeOfContext,
+ CROSS_PLATFORM_CONTEXT *context)
+{
+ if (pTransitionContexts == NULL || *pcurCount >= maxCount)
+ {
+ ++(*pcurCount);
+ return FALSE;
+ }
+ if (uiSizeOfContext == sizeof(StackTrace_SimpleContext))
+ {
+ StackTrace_SimpleContext *pSimple = (StackTrace_SimpleContext *) pTransitionContexts;
+ g_targetMachine->FillSimpleContext(&pSimple[*pcurCount], context);
+ }
+ else if (uiSizeOfContext == g_targetMachine->GetContextSize())
+ {
+ // FillTargetContext ensures we only write uiSizeOfContext bytes in pTransitionContexts
+ // and not sizeof(CROSS_PLATFORM_CONTEXT) bytes (which would overrun).
+ g_targetMachine->FillTargetContext(pTransitionContexts, context, (int)(*pcurCount));
+ }
+ else
+ {
+ return FALSE;
+ }
+ ++(*pcurCount);
+ return TRUE;
+}
+
+HRESULT CALLBACK ImplementEFNStackTrace(
+ PDEBUG_CLIENT client,
+ __out_ecount_opt(*puiTextLength) WCHAR wszTextOut[],
+ size_t *puiTextLength,
+ LPVOID pTransitionContexts,
+ size_t *puiTransitionContextCount,
+ size_t uiSizeOfContext,
+ DWORD Flags)
+{
+
+#define DOAPPEND(str) if (!so.Append((str))) { \
+ Status = E_OUTOFMEMORY; \
+ goto Exit; \
+}
+
+ HRESULT Status = E_FAIL;
+ StringOutput so;
+ size_t transitionContextCount = 0;
+
+ if (puiTextLength == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ if (pTransitionContexts)
+ {
+ if (puiTransitionContextCount == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ // Do error checking on context size
+ if ((uiSizeOfContext != g_targetMachine->GetContextSize()) &&
+ (uiSizeOfContext != sizeof(StackTrace_SimpleContext)))
+ {
+ return E_INVALIDARG;
+ }
+ }
+
+ IXCLRDataStackWalk *pStackWalk = NULL;
+ IXCLRDataTask* Task;
+ ULONG ThreadId;
+
+ if ((Status = g_ExtSystem->GetCurrentThreadSystemId(&ThreadId)) != S_OK ||
+ (Status = g_clrData->GetTaskByOSThreadID(ThreadId, &Task)) != S_OK)
+ {
+ // Not a managed thread.
+ return SOS_E_NOMANAGEDCODE;
+ }
+
+ Status = Task->CreateStackWalk(CLRDATA_SIMPFRAME_UNRECOGNIZED |
+ CLRDATA_SIMPFRAME_MANAGED_METHOD |
+ CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE |
+ CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE,
+ &pStackWalk);
+
+ Task->Release();
+
+ if (Status != S_OK)
+ {
+ if (Status == E_FAIL)
+ {
+ return SOS_E_NOMANAGEDCODE;
+ }
+ return Status;
+ }
+
+#ifdef _TARGET_WIN64_
+ ULONG numFrames = 0;
+ BOOL bInNative = TRUE;
+
+ Status = GetContextStackTrace(&numFrames);
+ if (FAILED(Status))
+ {
+ goto Exit;
+ }
+
+ for (ULONG i = 0; i < numFrames; i++)
+ {
+ PDEBUG_STACK_FRAME pCur = g_Frames + i;
+
+ CLRDATA_ADDRESS pMD;
+ if (g_sos->GetMethodDescPtrFromIP(pCur->InstructionOffset, &pMD) == S_OK)
+ {
+ if (bInNative || transitionContextCount==0)
+ {
+ // We only want to list one transition frame if there are multiple frames.
+ bInNative = FALSE;
+
+ DOAPPEND (W("(TransitionMU)\n"));
+ // For each transition, we need to store the context information
+ if (puiTransitionContextCount)
+ {
+ // below we cast the i-th AMD64_CONTEXT to CROSS_PLATFORM_CONTEXT
+ AppendContext (pTransitionContexts, *puiTransitionContextCount,
+ &transitionContextCount, uiSizeOfContext, (CROSS_PLATFORM_CONTEXT*)(&(g_X64FrameContexts[i])));
+ }
+ else
+ {
+ transitionContextCount++;
+ }
+ }
+
+ Status = DumpMDInfoBuffer((DWORD_PTR) pMD, Flags,
+ pCur->StackOffset, pCur->InstructionOffset, so);
+ if (FAILED(Status))
+ {
+ goto Exit;
+ }
+ else if (Status == S_OK)
+ {
+ DOAPPEND (W("\n"));
+ }
+ // for S_FALSE do not append anything
+
+ }
+ else
+ {
+ if (!bInNative)
+ {
+ // We only want to list one transition frame if there are multiple frames.
+ bInNative = TRUE;
+
+ DOAPPEND (W("(TransitionUM)\n"));
+ // For each transition, we need to store the context information
+ if (puiTransitionContextCount)
+ {
+ AppendContext (pTransitionContexts, *puiTransitionContextCount,
+ &transitionContextCount, uiSizeOfContext, (CROSS_PLATFORM_CONTEXT*)(&(g_X64FrameContexts[i])));
+ }
+ else
+ {
+ transitionContextCount++;
+ }
+ }
+ }
+ }
+
+Exit:
+#else // _TARGET_WIN64_
+
+#ifdef _DEBUG
+ size_t prevLength = 0;
+ static WCHAR wszNameBuffer[1024]; // should be large enough
+ wcscpy_s(wszNameBuffer, 1024, W("Frame")); // default value
+#endif
+
+ BOOL bInNative = TRUE;
+
+ UINT frameCount = 0;
+ do
+ {
+ DacpFrameData FrameData;
+ if ((Status = FrameData.Request(pStackWalk)) != S_OK)
+ {
+ goto Exit;
+ }
+
+ CROSS_PLATFORM_CONTEXT context;
+ if ((Status=pStackWalk->GetContext(DT_CONTEXT_FULL, g_targetMachine->GetContextSize(),
+ NULL, (BYTE *)&context))!=S_OK)
+ {
+ goto Exit;
+ }
+
+ ExtDbgOut ( " * Ctx[BSI]: %08x %08x %08x ", GetBP(context), GetSP(context), GetIP(context) );
+
+ CLRDATA_ADDRESS pMD;
+ if (!FrameData.frameAddr)
+ {
+ if (bInNative || transitionContextCount==0)
+ {
+ // We only want to list one transition frame if there are multiple frames.
+ bInNative = FALSE;
+
+ DOAPPEND (W("(TransitionMU)\n"));
+ // For each transition, we need to store the context information
+ if (puiTransitionContextCount)
+ {
+ AppendContext (pTransitionContexts, *puiTransitionContextCount,
+ &transitionContextCount, uiSizeOfContext, &context);
+ }
+ else
+ {
+ transitionContextCount++;
+ }
+ }
+
+ // we may have a method, try to get the methoddesc
+ if (g_sos->GetMethodDescPtrFromIP(GetIP(context), &pMD)==S_OK)
+ {
+ Status = DumpMDInfoBuffer((DWORD_PTR) pMD, Flags,
+ GetSP(context), GetIP(context), so);
+ if (FAILED(Status))
+ {
+ goto Exit;
+ }
+ else if (Status == S_OK)
+ {
+ DOAPPEND (W("\n"));
+ }
+ // for S_FALSE do not append anything
+ }
+ }
+ else
+ {
+#ifdef _DEBUG
+ if (Output::IsDebugOutputEnabled())
+ {
+ DWORD_PTR vtAddr;
+ MOVE(vtAddr, TO_TADDR(FrameData.frameAddr));
+ if (g_sos->GetFrameName(TO_CDADDR(vtAddr), 1024, wszNameBuffer, NULL) == S_OK)
+ ExtDbgOut("[%ls: %08x] ", wszNameBuffer, FrameData.frameAddr);
+ else
+ ExtDbgOut("[Frame: %08x] ", FrameData.frameAddr);
+ }
+#endif
+ if (!bInNative)
+ {
+ // We only want to list one transition frame if there are multiple frames.
+ bInNative = TRUE;
+
+ DOAPPEND (W("(TransitionUM)\n"));
+ // For each transition, we need to store the context information
+ if (puiTransitionContextCount)
+ {
+ AppendContext (pTransitionContexts, *puiTransitionContextCount,
+ &transitionContextCount, uiSizeOfContext, &context);
+ }
+ else
+ {
+ transitionContextCount++;
+ }
+ }
+ }
+
+#ifdef _DEBUG
+ if (so.Length() > prevLength)
+ {
+ ExtDbgOut ( "%ls", so.String()+prevLength );
+ prevLength = so.Length();
+ }
+ else
+ ExtDbgOut ( "\n" );
+#endif
+
+ }
+ while ((frameCount++) < MAX_STACK_FRAMES && pStackWalk->Next()==S_OK);
+
+ Status = S_OK;
+
+Exit:
+#endif // _TARGET_WIN64_
+
+ if (pStackWalk)
+ {
+ pStackWalk->Release();
+ pStackWalk = NULL;
+ }
+
+ // We have finished. Does the user want to copy this data to a buffer?
+ if (Status == S_OK)
+ {
+ if(wszTextOut)
+ {
+ // They want at least partial output
+ wcsncpy_s (wszTextOut, *puiTextLength, so.String(), *puiTextLength-1); // _TRUNCATE
+ }
+ else
+ {
+ *puiTextLength = _wcslen (so.String()) + 1;
+ }
+
+ if (puiTransitionContextCount)
+ {
+ *puiTransitionContextCount = transitionContextCount;
+ }
+ }
+
+ return Status;
+}
+
+#ifdef FEATURE_PAL
+#define PAL_TRY_NAKED PAL_CPP_TRY
+#define PAL_EXCEPT_NAKED(disp) PAL_CPP_CATCH_ALL
+#define PAL_ENDTRY_NAKED PAL_CPP_ENDTRY
+#endif
+
+// TODO: Convert PAL_TRY_NAKED to something that works on the Mac.
+HRESULT CALLBACK ImplementEFNStackTraceTry(
+ PDEBUG_CLIENT client,
+ __out_ecount_opt(*puiTextLength) WCHAR wszTextOut[],
+ size_t *puiTextLength,
+ LPVOID pTransitionContexts,
+ size_t *puiTransitionContextCount,
+ size_t uiSizeOfContext,
+ DWORD Flags)
+{
+ HRESULT Status = E_FAIL;
+
+ PAL_TRY_NAKED
+ {
+ Status = ImplementEFNStackTrace(client, wszTextOut, puiTextLength,
+ pTransitionContexts, puiTransitionContextCount,
+ uiSizeOfContext, Flags);
+ }
+ PAL_EXCEPT_NAKED (EXCEPTION_EXECUTE_HANDLER)
+ {
+ }
+ PAL_ENDTRY_NAKED
+
+ return Status;
+}
+
+// See sos_stacktrace.h for the contract with the callers regarding the LPVOID arguments.
+HRESULT CALLBACK _EFN_StackTrace(
+ PDEBUG_CLIENT client,
+ __out_ecount_opt(*puiTextLength) WCHAR wszTextOut[],
+ size_t *puiTextLength,
+ __out_bcount_opt(uiSizeOfContext*(*puiTransitionContextCount)) LPVOID pTransitionContexts,
+ size_t *puiTransitionContextCount,
+ size_t uiSizeOfContext,
+ DWORD Flags)
+{
+ INIT_API();
+
+ Status = ImplementEFNStackTraceTry(client, wszTextOut, puiTextLength,
+ pTransitionContexts, puiTransitionContextCount,
+ uiSizeOfContext, Flags);
+
+ return Status;
+}
+
+
+BOOL FormatFromRemoteString(DWORD_PTR strObjPointer, __out_ecount(cchString) PWSTR wszBuffer, ULONG cchString)
+{
+ BOOL bRet = FALSE;
+
+ wszBuffer[0] = L'\0';
+
+ DacpObjectData objData;
+ if (objData.Request(g_sos, TO_CDADDR(strObjPointer))!=S_OK)
+ {
+ return bRet;
+ }
+
+ strobjInfo stInfo;
+
+ if (MOVE(stInfo, strObjPointer) != S_OK)
+ {
+ return bRet;
+ }
+
+ DWORD dwBufLength = 0;
+ if (!ClrSafeInt<DWORD>::addition(stInfo.m_StringLength, 1, dwBufLength))
+ {
+ ExtOut("<integer overflow>\n");
+ return bRet;
+ }
+
+ LPWSTR pwszBuf = new NOTHROW WCHAR[dwBufLength];
+ if (pwszBuf == NULL)
+ {
+ return bRet;
+ }
+
+ if (g_sos->GetObjectStringData(TO_CDADDR(strObjPointer), stInfo.m_StringLength+1, pwszBuf, NULL)!=S_OK)
+ {
+ delete [] pwszBuf;
+ return bRet;
+ }
+
+ // String is in format
+ // <SP><SP><SP>at <function name>(args,...)\n
+ // ...
+ // Parse and copy just <function name>(args,...)
+
+ LPWSTR pwszPointer = pwszBuf;
+
+ WCHAR PSZSEP[] = W(" at ");
+
+ UINT Length = 0;
+ while(1)
+ {
+ if (_wcsncmp(pwszPointer, PSZSEP, _countof(PSZSEP)-1) != 0)
+ {
+ delete [] pwszBuf;
+ return bRet;
+ }
+
+ pwszPointer += _wcslen(PSZSEP);
+ LPWSTR nextPos = _wcsstr(pwszPointer, PSZSEP);
+ if (nextPos == NULL)
+ {
+ // Done! Note that we are leaving the function before we add the last
+ // line of stack trace to the output string. This is on purpose because
+ // this string needs to be merged with a real trace, and the last line
+ // of the trace will be common to the real trace.
+ break;
+ }
+ WCHAR c = *nextPos;
+ *nextPos = L'\0';
+
+ // Buffer is calculated for sprintf below (" %p %p %S\n");
+ WCHAR wszLineBuffer[mdNameLen + 8 + sizeof(size_t)*2];
+
+ // Note that we don't add a newline because we have this embedded in wszLineBuffer
+ swprintf_s(wszLineBuffer, _countof(wszLineBuffer), W(" %p %p %s"), (void*)(size_t)-1, (void*)(size_t)-1, pwszPointer);
+ Length += (UINT)_wcslen(wszLineBuffer);
+
+ if (wszBuffer)
+ {
+ wcsncat_s(wszBuffer, cchString, wszLineBuffer, _TRUNCATE);
+ }
+
+ *nextPos = c;
+ // Move to the next line.
+ pwszPointer = nextPos;
+ }
+
+ delete [] pwszBuf;
+
+ // Return TRUE only if the stack string had any information that was successfully parsed.
+ // (Length > 0) is a good indicator of that.
+ bRet = (Length > 0);
+ return bRet;
+}
+
+HRESULT AppendExceptionInfo(CLRDATA_ADDRESS cdaObj,
+ __out_ecount(cchString) PWSTR wszStackString,
+ ULONG cchString,
+ BOOL bNestedCase) // If bNestedCase is TRUE, the last frame of the computed stack is left off
+{
+ DacpObjectData objData;
+ if (objData.Request(g_sos, cdaObj) != S_OK)
+ {
+ return E_FAIL;
+ }
+
+ // Make sure it is an exception object, and get the MT of Exception
+ CLRDATA_ADDRESS exceptionMT = isExceptionObj(objData.MethodTable);
+ if (exceptionMT == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ // First try to get exception object data using ISOSDacInterface2
+ DacpExceptionObjectData excData;
+ BOOL bGotExcData = SUCCEEDED(excData.Request(g_sos, cdaObj));
+
+ int iOffset;
+ // Is there a _remoteStackTraceString? We'll want to prepend that data.
+ // We only have string data, so IP/SP info has to be set to -1.
+ DWORD_PTR strPointer;
+ if (bGotExcData)
+ {
+ strPointer = TO_TADDR(excData.RemoteStackTraceString);
+ }
+ else
+ {
+ iOffset = GetObjFieldOffset (cdaObj, objData.MethodTable, W("_remoteStackTraceString"));
+ MOVE (strPointer, TO_TADDR(cdaObj) + iOffset);
+ }
+ if (strPointer)
+ {
+ WCHAR *pwszBuffer = new NOTHROW WCHAR[cchString];
+ if (pwszBuffer == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ if (FormatFromRemoteString(strPointer, pwszBuffer, cchString))
+ {
+ // Prepend this stuff to the string for the user
+ wcsncat_s(wszStackString, cchString, pwszBuffer, _TRUNCATE);
+ }
+ delete[] pwszBuffer;
+ }
+
+ BOOL bAsync = bGotExcData ? IsAsyncException(excData)
+ : IsAsyncException(TO_TADDR(cdaObj), TO_TADDR(objData.MethodTable));
+
+ DWORD_PTR arrayPtr;
+ if (bGotExcData)
+ {
+ arrayPtr = TO_TADDR(excData.StackTrace);
+ }
+ else
+ {
+ iOffset = GetObjFieldOffset (cdaObj, objData.MethodTable, W("_stackTrace"));
+ MOVE (arrayPtr, TO_TADDR(cdaObj) + iOffset);
+ }
+
+ if (arrayPtr)
+ {
+ DWORD arrayLen;
+ MOVE (arrayLen, arrayPtr + sizeof(DWORD_PTR));
+
+ if (arrayLen)
+ {
+#ifdef _TARGET_WIN64_
+ DWORD_PTR dataPtr = arrayPtr + sizeof(DWORD_PTR) + sizeof(DWORD) + sizeof(DWORD);
+#else
+ DWORD_PTR dataPtr = arrayPtr + sizeof(DWORD_PTR) + sizeof(DWORD);
+#endif // _TARGET_WIN64_
+ size_t stackTraceSize = 0;
+ MOVE (stackTraceSize, dataPtr); // data length is stored at the beginning of the array in this case
+
+ DWORD cbStackSize = static_cast<DWORD>(stackTraceSize * sizeof(StackTraceElement));
+ dataPtr += sizeof(size_t) + sizeof(size_t); // skip the array header, then goes the data
+
+ if (stackTraceSize != 0)
+ {
+ size_t iLength = FormatGeneratedException (dataPtr, cbStackSize, NULL, 0, bAsync, bNestedCase);
+ WCHAR *pwszBuffer = new NOTHROW WCHAR[iLength + 1];
+ if (pwszBuffer)
+ {
+ FormatGeneratedException(dataPtr, cbStackSize, pwszBuffer, iLength + 1, bAsync, bNestedCase);
+ wcsncat_s(wszStackString, cchString, pwszBuffer, _TRUNCATE);
+ delete[] pwszBuffer;
+ }
+ else
+ {
+ return E_OUTOFMEMORY;
+ }
+ }
+ }
+ }
+ return S_OK;
+}
+
+HRESULT ImplementEFNGetManagedExcepStack(
+ CLRDATA_ADDRESS cdaStackObj,
+ __out_ecount(cchString) PWSTR wszStackString,
+ ULONG cchString)
+{
+ HRESULT Status = E_FAIL;
+
+ if (wszStackString == NULL || cchString == 0)
+ {
+ return E_INVALIDARG;
+ }
+
+ CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
+ DacpThreadData Thread;
+ BOOL bCanUseThreadContext = TRUE;
+
+ ZeroMemory(&Thread, sizeof(DacpThreadData));
+
+ if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
+ {
+ // The current thread is unmanaged
+ bCanUseThreadContext = FALSE;
+ }
+
+ if (cdaStackObj == NULL)
+ {
+ if (!bCanUseThreadContext)
+ {
+ return E_INVALIDARG;
+ }
+
+ TADDR taLTOH = NULL;
+ if ((!SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle),
+ &taLTOH,
+ sizeof(taLTOH), NULL)) || (taLTOH==NULL))
+ {
+ return Status;
+ }
+ else
+ {
+ cdaStackObj = TO_CDADDR(taLTOH);
+ }
+ }
+
+ // Put the stack trace header on
+ AddExceptionHeader(wszStackString, cchString);
+
+ // First is there a nested exception?
+ if (bCanUseThreadContext && Thread.firstNestedException)
+ {
+ CLRDATA_ADDRESS obj = 0, next = 0;
+ CLRDATA_ADDRESS currentNested = Thread.firstNestedException;
+ do
+ {
+ Status = g_sos->GetNestedExceptionData(currentNested, &obj, &next);
+
+ // deal with the inability to read a nested exception gracefully
+ if (Status != S_OK)
+ {
+ break;
+ }
+
+ Status = AppendExceptionInfo(obj, wszStackString, cchString, TRUE);
+ currentNested = next;
+ }
+ while(currentNested != NULL);
+ }
+
+ Status = AppendExceptionInfo(cdaStackObj, wszStackString, cchString, FALSE);
+
+ return Status;
+}
+
+// TODO: Enable this when ImplementEFNStackTraceTry is fixed.
+// This function, like VerifyDAC, exists for the purpose of testing
+// hard-to-get-to SOS APIs.
+DECLARE_API(VerifyStackTrace)
+{
+ INIT_API();
+
+ BOOL bVerifyManagedExcepStack = FALSE;
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-ManagedExcepStack", &bVerifyManagedExcepStack, COBOOL, FALSE},
+ };
+
+ if (!GetCMDOption(args, option, _countof(option), NULL,0,NULL))
+ {
+ return Status;
+ }
+
+ if (bVerifyManagedExcepStack)
+ {
+ CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
+ DacpThreadData Thread;
+
+ TADDR taExc = NULL;
+ if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
+ {
+ ExtOut("The current thread is unmanaged\n");
+ return Status;
+ }
+
+ TADDR taLTOH = NULL;
+ if ((!SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle),
+ &taLTOH,
+ sizeof(taLTOH), NULL)) || (taLTOH == NULL))
+ {
+ ExtOut("There is no current managed exception on this thread\n");
+ return Status;
+ }
+ else
+ {
+ taExc = taLTOH;
+ }
+
+ const SIZE_T cchStr = 4096;
+ WCHAR *wszStr = (WCHAR *)alloca(cchStr * sizeof(WCHAR));
+ if (ImplementEFNGetManagedExcepStack(TO_CDADDR(taExc), wszStr, cchStr) != S_OK)
+ {
+ ExtOut("Error!\n");
+ return Status;
+ }
+
+ ExtOut("_EFN_GetManagedExcepStack(%P, wszStr, sizeof(wszStr)) returned:\n", SOS_PTR(taExc));
+ ExtOut("%S\n", wszStr);
+
+ if (ImplementEFNGetManagedExcepStack((ULONG64)NULL, wszStr, cchStr) != S_OK)
+ {
+ ExtOut("Error!\n");
+ return Status;
+ }
+
+ ExtOut("_EFN_GetManagedExcepStack(NULL, wszStr, sizeof(wszStr)) returned:\n");
+ ExtOut("%S\n", wszStr);
+ }
+ else
+ {
+ size_t textLength = 0;
+ size_t contextLength = 0;
+ Status = ImplementEFNStackTraceTry(client,
+ NULL,
+ &textLength,
+ NULL,
+ &contextLength,
+ 0,
+ 0);
+
+ if (Status != S_OK)
+ {
+ ExtOut("Error: %lx\n", Status);
+ return Status;
+ }
+
+ ExtOut("Number of characters requested: %d\n", textLength);
+ WCHAR *wszBuffer = new NOTHROW WCHAR[textLength + 1];
+ if (wszBuffer == NULL)
+ {
+ ReportOOM();
+ return Status;
+ }
+
+ // For the transition contexts buffer the callers are expected to allocate
+ // contextLength * sizeof(TARGET_CONTEXT), and not
+ // contextLength * sizeof(CROSS_PLATFORM_CONTEXT). See sos_stacktrace.h for
+ // details.
+ LPBYTE pContexts = new NOTHROW BYTE[contextLength * g_targetMachine->GetContextSize()];
+
+ if (pContexts == NULL)
+ {
+ ReportOOM();
+ delete[] wszBuffer;
+ return Status;
+ }
+
+ Status = ImplementEFNStackTrace(client,
+ wszBuffer,
+ &textLength,
+ pContexts,
+ &contextLength,
+ g_targetMachine->GetContextSize(),
+ 0);
+
+ if (Status != S_OK)
+ {
+ ExtOut("Error: %lx\n", Status);
+ delete[] wszBuffer;
+ delete [] pContexts;
+ return Status;
+ }
+
+ ExtOut("%S\n", wszBuffer);
+
+ ExtOut("Context information:\n");
+ if (IsDbgTargetX86())
+ {
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
+ "Ebp", "Esp", "Eip");
+ }
+ else if (IsDbgTargetAmd64())
+ {
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
+ "Rbp", "Rsp", "Rip");
+ }
+ else if (IsDbgTargetArm())
+ {
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
+ "FP", "SP", "PC");
+ }
+ else
+ {
+ ExtOut("Unsupported platform");
+ delete [] pContexts;
+ delete[] wszBuffer;
+ return S_FALSE;
+ }
+
+ for (size_t j=0; j < contextLength; j++)
+ {
+ CROSS_PLATFORM_CONTEXT *pCtx = (CROSS_PLATFORM_CONTEXT*)(pContexts + j*g_targetMachine->GetContextSize());
+ ExtOut("%p %p %p\n", GetBP(*pCtx), GetSP(*pCtx), GetIP(*pCtx));
+ }
+
+ delete [] pContexts;
+
+ StackTrace_SimpleContext *pSimple = new NOTHROW StackTrace_SimpleContext[contextLength];
+ if (pSimple == NULL)
+ {
+ ReportOOM();
+ delete[] wszBuffer;
+ return Status;
+ }
+
+ Status = ImplementEFNStackTrace(client,
+ wszBuffer,
+ &textLength,
+ pSimple,
+ &contextLength,
+ sizeof(StackTrace_SimpleContext),
+ 0);
+
+ if (Status != S_OK)
+ {
+ ExtOut("Error: %lx\n", Status);
+ delete[] wszBuffer;
+ delete [] pSimple;
+ return Status;
+ }
+
+ ExtOut("Simple Context information:\n");
+ if (IsDbgTargetX86())
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
+ "Ebp", "Esp", "Eip");
+ else if (IsDbgTargetAmd64())
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
+ "Rbp", "Rsp", "Rip");
+ else if (IsDbgTargetArm())
+ ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
+ "FP", "SP", "PC");
+ else
+ {
+ ExtOut("Unsupported platform");
+ delete[] wszBuffer;
+ delete [] pSimple;
+ return S_FALSE;
+ }
+ for (size_t j=0; j < contextLength; j++)
+ {
+ ExtOut("%p %p %p\n", SOS_PTR(pSimple[j].FrameOffset),
+ SOS_PTR(pSimple[j].StackOffset),
+ SOS_PTR(pSimple[j].InstructionOffset));
+ }
+ delete [] pSimple;
+ delete[] wszBuffer;
+ }
+
+ return Status;
+}
+
+#ifndef FEATURE_PAL
+
+// This is an internal-only Apollo extension to de-optimize the code
+DECLARE_API(SuppressJitOptimization)
+{
+ INIT_API_NOEE();
+ MINIDUMP_NOT_SUPPORTED();
+
+ StringHolder onOff;
+ CMDValue arg[] =
+ { // vptr, type
+ {&onOff.data, COSTRING},
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ {
+ return E_FAIL;
+ }
+
+ if(nArg == 1 && (_stricmp(onOff.data, "On") == 0))
+ {
+ // if CLR is already loaded, try to change the flags now
+ if(CheckEEDll() == S_OK)
+ {
+ SetNGENCompilerFlags(CORDEBUG_JIT_DISABLE_OPTIMIZATION);
+ }
+
+ if(!g_fAllowJitOptimization)
+ ExtOut("JIT optimization is already suppressed\n");
+ else
+ {
+ g_fAllowJitOptimization = FALSE;
+ g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "sxe -c \"!HandleCLRN\" clrn", 0);
+ ExtOut("JIT optimization will be suppressed\n");
+ }
+
+
+ }
+ else if(nArg == 1 && (_stricmp(onOff.data, "Off") == 0))
+ {
+ // if CLR is already loaded, try to change the flags now
+ if(CheckEEDll() == S_OK)
+ {
+ SetNGENCompilerFlags(CORDEBUG_JIT_DEFAULT);
+ }
+
+ if(g_fAllowJitOptimization)
+ ExtOut("JIT optimization is already permitted\n");
+ else
+ {
+ g_fAllowJitOptimization = TRUE;
+ ExtOut("JIT optimization will be permitted\n");
+ }
+ }
+ else
+ {
+ ExtOut("Usage: !SuppressJitOptimization <on|off>\n");
+ }
+
+ return S_OK;
+}
+
+// Uses ICorDebug to set the state of desired NGEN compiler flags. This can suppress pre-jitted optimized
+// code
+HRESULT SetNGENCompilerFlags(DWORD flags)
+{
+ HRESULT hr;
+
+ ToRelease<ICorDebugProcess2> proc2;
+ if(FAILED(hr = InitCorDebugInterface()))
+ {
+ ExtOut("SOS: warning, prejitted code optimizations could not be changed. Failed to load ICorDebug HR = 0x%x\n", hr);
+ }
+ else if(FAILED(g_pCorDebugProcess->QueryInterface(__uuidof(ICorDebugProcess2), (void**) &proc2)))
+ {
+ if(flags != CORDEBUG_JIT_DEFAULT)
+ {
+ ExtOut("SOS: warning, prejitted code optimizations could not be changed. This CLR version doesn't support the functionality\n");
+ }
+ else
+ {
+ hr = S_OK;
+ }
+ }
+ else if(FAILED(hr = proc2->SetDesiredNGENCompilerFlags(flags)))
+ {
+ // Versions of CLR that don't have SetDesiredNGENCompilerFlags DAC-ized will return E_FAIL.
+ // This was first supported in the clr_triton branch around 4/1/12, Apollo release
+ // It will likely be supported in desktop CLR during Dev12
+ if(hr == E_FAIL)
+ {
+ if(flags != CORDEBUG_JIT_DEFAULT)
+ {
+ ExtOut("SOS: warning, prejitted code optimizations could not be changed. This CLR version doesn't support the functionality\n");
+ }
+ else
+ {
+ hr = S_OK;
+ }
+ }
+ else if(hr == CORDBG_E_NGEN_NOT_SUPPORTED)
+ {
+ if(flags != CORDEBUG_JIT_DEFAULT)
+ {
+ ExtOut("SOS: warning, prejitted code optimizations could not be changed. This CLR version doesn't support NGEN\n");
+ }
+ else
+ {
+ hr = S_OK;
+ }
+ }
+ else if(hr == CORDBG_E_MUST_BE_IN_CREATE_PROCESS)
+ {
+ DWORD currentFlags = 0;
+ if(FAILED(hr = proc2->GetDesiredNGENCompilerFlags(&currentFlags)))
+ {
+ ExtOut("SOS: warning, prejitted code optimizations could not be changed. GetDesiredNGENCompilerFlags failed hr=0x%x\n", hr);
+ }
+ else if(currentFlags != flags)
+ {
+ ExtOut("SOS: warning, prejitted code optimizations could not be changed at this time. This setting is fixed once CLR starts\n");
+ }
+ else
+ {
+ hr = S_OK;
+ }
+ }
+ else
+ {
+ ExtOut("SOS: warning, prejitted code optimizations could not be changed at this time. SetDesiredNGENCompilerFlags hr = 0x%x\n", hr);
+ }
+ }
+
+ return hr;
+}
+
+
+// This is an internal-only Apollo extension to save breakpoint/watch state
+DECLARE_API(SaveState)
+{
+ INIT_API_NOEE();
+ MINIDUMP_NOT_SUPPORTED();
+
+ StringHolder filePath;
+ CMDValue arg[] =
+ { // vptr, type
+ {&filePath.data, COSTRING},
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ {
+ return E_FAIL;
+ }
+
+ if(nArg == 0)
+ {
+ ExtOut("Usage: !SaveState <file_path>\n");
+ }
+
+ FILE* pFile;
+ errno_t error = fopen_s(&pFile, filePath.data, "w");
+ if(error != 0)
+ {
+ ExtOut("Failed to open file %s, error=0x%x\n", filePath.data, error);
+ return E_FAIL;
+ }
+
+ g_bpoints.SaveBreakpoints(pFile);
+ g_watchCmd.SaveListToFile(pFile);
+
+ fclose(pFile);
+ ExtOut("Session breakpoints and watch expressions saved to %s\n", filePath.data);
+ return S_OK;
+}
+
+#endif // FEATURE_PAL
+
+DECLARE_API(StopOnCatch)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ g_stopOnNextCatch = TRUE;
+ ULONG32 flags = 0;
+ g_clrData->GetOtherNotificationFlags(&flags);
+ flags |= CLRDATA_NOTIFY_ON_EXCEPTION_CATCH_ENTER;
+ g_clrData->SetOtherNotificationFlags(flags);
+ ExtOut("Debuggee will break the next time a managed exception is caught during execution\n");
+ return S_OK;
+}
+
+// This is an undocumented SOS extension command intended to help test SOS
+// It causes the Dml output to be printed to the console uninterpretted so
+// that a test script can read the commands which are hidden in the markup
+DECLARE_API(ExposeDML)
+{
+ Output::SetDMLExposed(true);
+ return S_OK;
+}
+
+// According to kksharma the Windows debuggers always sign-extend
+// arguments when calling externally, therefore StackObjAddr
+// conforms to CLRDATA_ADDRESS contract.
+HRESULT CALLBACK
+_EFN_GetManagedExcepStack(
+ PDEBUG_CLIENT client,
+ ULONG64 StackObjAddr,
+ __out_ecount (cbString) PSTR szStackString,
+ ULONG cbString
+ )
+{
+ INIT_API();
+
+ ArrayHolder<WCHAR> tmpStr = new NOTHROW WCHAR[cbString];
+ if (tmpStr == NULL)
+ {
+ ReportOOM();
+ return E_OUTOFMEMORY;
+ }
+
+ if (FAILED(Status = ImplementEFNGetManagedExcepStack(StackObjAddr, tmpStr, cbString)))
+ {
+ return Status;
+ }
+
+ if (WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, tmpStr, -1, szStackString, cbString, NULL, NULL) == 0)
+ {
+ return E_FAIL;
+ }
+
+ return S_OK;
+}
+
+// same as _EFN_GetManagedExcepStack, but returns the stack as a wide string.
+HRESULT CALLBACK
+_EFN_GetManagedExcepStackW(
+ PDEBUG_CLIENT client,
+ ULONG64 StackObjAddr,
+ __out_ecount(cchString) PWSTR wszStackString,
+ ULONG cchString
+ )
+{
+ INIT_API();
+
+ return ImplementEFNGetManagedExcepStack(StackObjAddr, wszStackString, cchString);
+}
+
+// According to kksharma the Windows debuggers always sign-extend
+// arguments when calling externally, therefore objAddr
+// conforms to CLRDATA_ADDRESS contract.
+HRESULT CALLBACK
+_EFN_GetManagedObjectName(
+ PDEBUG_CLIENT client,
+ ULONG64 objAddr,
+ __out_ecount (cbName) PSTR szName,
+ ULONG cbName
+ )
+{
+ INIT_API ();
+
+ if (!sos::IsObject(objAddr, false))
+ {
+ return E_INVALIDARG;
+ }
+
+ sos::Object obj = TO_TADDR(objAddr);
+
+ if (WideCharToMultiByte(CP_ACP, 0, obj.GetTypeName(), (int) (_wcslen(obj.GetTypeName()) + 1),
+ szName, cbName, NULL, NULL) == 0)
+ {
+ return E_FAIL;
+ }
+ return S_OK;
+}
+
+// According to kksharma the Windows debuggers always sign-extend
+// arguments when calling externally, therefore objAddr
+// conforms to CLRDATA_ADDRESS contract.
+HRESULT CALLBACK
+_EFN_GetManagedObjectFieldInfo(
+ PDEBUG_CLIENT client,
+ ULONG64 objAddr,
+ __out_ecount (mdNameLen) PSTR szFieldName,
+ PULONG64 pValue,
+ PULONG pOffset
+ )
+{
+ INIT_API();
+ DacpObjectData objData;
+ LPWSTR fieldName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
+
+ if (szFieldName == NULL || *szFieldName == '\0' ||
+ objAddr == NULL)
+ {
+ return E_FAIL;
+ }
+
+ if (pOffset == NULL && pValue == NULL)
+ {
+ // One of these needs to be valid
+ return E_FAIL;
+ }
+
+ if (FAILED(objData.Request(g_sos, objAddr)))
+ {
+ return E_FAIL;
+ }
+
+ MultiByteToWideChar(CP_ACP,0,szFieldName,-1,fieldName,mdNameLen);
+
+ int iOffset = GetObjFieldOffset (objAddr, objData.MethodTable, fieldName);
+ if (iOffset <= 0)
+ {
+ return E_FAIL;
+ }
+
+ if (pOffset)
+ {
+ *pOffset = (ULONG) iOffset;
+ }
+
+ if (pValue)
+ {
+ if (FAILED(g_ExtData->ReadVirtual(UL64_TO_CDA(objAddr + iOffset), pValue, sizeof(ULONG64), NULL)))
+ {
+ return E_FAIL;
+ }
+ }
+
+ return S_OK;
+}
+
+void PrintHelp (__in_z LPCSTR pszCmdName)
+{
+ static LPSTR pText = NULL;
+
+ if (pText == NULL) {
+#ifndef FEATURE_PAL
+ HGLOBAL hResource = NULL;
+ HRSRC hResInfo = FindResource (g_hInstance, TEXT ("DOCUMENTATION"), TEXT ("TEXT"));
+ if (hResInfo) hResource = LoadResource (g_hInstance, hResInfo);
+ if (hResource) pText = (LPSTR) LockResource (hResource);
+ if (pText == NULL)
+ {
+ ExtOut("Error loading documentation resource\n");
+ return;
+ }
+#else
+ int err = PAL_InitializeDLL();
+ if(err != 0)
+ {
+ ExtOut("Error initializing PAL\n");
+ return;
+ }
+ char lpFilename[MAX_LONGPATH + 12]; // + 12 to make enough room for strcat function.
+ strcpy_s(lpFilename, _countof(lpFilename), g_ExtServices->GetCoreClrDirectory());
+ strcat_s(lpFilename, _countof(lpFilename), "sosdocsunix.txt");
+
+ HANDLE hSosDocFile = CreateFileA(lpFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+ if (hSosDocFile == INVALID_HANDLE_VALUE) {
+ ExtOut("Error finding documentation file\n");
+ return;
+ }
+
+ HANDLE hMappedSosDocFile = CreateFileMappingA(hSosDocFile, NULL, PAGE_READONLY, 0, 0, NULL);
+ CloseHandle(hSosDocFile);
+ if (hMappedSosDocFile == NULL) {
+ ExtOut("Error mapping documentation file\n");
+ return;
+ }
+
+ pText = (LPSTR)MapViewOfFile(hMappedSosDocFile, FILE_MAP_READ, 0, 0, 0);
+ CloseHandle(hMappedSosDocFile);
+ if (pText == NULL)
+ {
+ ExtOut("Error loading documentation file\n");
+ return;
+ }
+#endif
+ }
+
+ // Find our line in the text file
+ char searchString[MAX_LONGPATH];
+ sprintf_s(searchString, _countof(searchString), "COMMAND: %s.", pszCmdName);
+
+ LPSTR pStart = strstr(pText, searchString);
+ LPSTR pEnd = NULL;
+ if (!pStart)
+ {
+ ExtOut("Documentation for %s not found.\n", pszCmdName);
+ return;
+ }
+
+ // Go to the end of this line:
+ pStart = strchr(pStart, '\n');
+ if (!pStart)
+ {
+ ExtOut("Expected newline in documentation resource.\n");
+ return;
+ }
+
+ // Bypass the newline that pStart points to and setup pEnd for the loop below. We set
+ // pEnd to be the old pStart since we add one to it when we call strstr.
+ pEnd = pStart++;
+
+ // Find the first occurrence of \\ followed by an \r or an \n on a line by itself.
+ do
+ {
+ pEnd = strstr(pEnd+1, "\\\\");
+ } while (pEnd && ((pEnd[-1] != '\r' && pEnd[-1] != '\n') || (pEnd[3] != '\r' && pEnd[3] != '\n')));
+
+ if (pEnd)
+ {
+ // We have found a \\ followed by a \r or \n. Do not print out the character pEnd points
+ // to, as this will be the first \ (this is why we don't add one to the second parameter).
+ ExtOut("%.*s", pEnd - pStart, pStart);
+ }
+ else
+ {
+ // If pEnd is false then we have run to the end of the document. However, we did find
+ // the command to print, so we should simply print to the end of the file. We'll add
+ // an extra newline here in case the file does not contain one.
+ ExtOut("%s\n", pStart);
+ }
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function displays the commands available in strike and the *
+* arguments passed into each.
+* *
+\**********************************************************************/
+DECLARE_API(Help)
+{
+ // Call extension initialization functions directly, because we don't need the DAC dll to be initialized to get help.
+ HRESULT Status;
+ __ExtensionCleanUp __extensionCleanUp;
+ if ((Status = ExtQuery(client)) != S_OK) return Status;
+ ControlC = FALSE;
+
+ StringHolder commandName;
+ CMDValue arg[] =
+ {
+ {&commandName.data, COSTRING}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+
+ ExtOut("-------------------------------------------------------------------------------\n");
+
+ if (nArg == 1)
+ {
+ // Convert commandName to lower-case
+ LPSTR curChar = commandName.data;
+ while (*curChar != '\0')
+ {
+ if ( ((unsigned) *curChar <= 0x7F) && isupper(*curChar))
+ {
+ *curChar = (CHAR) tolower(*curChar);
+ }
+ curChar++;
+ }
+
+ // Strip off leading "!" if the user put that.
+ curChar = commandName.data;
+ if (*curChar == '!')
+ curChar++;
+
+ PrintHelp (curChar);
+ }
+ else
+ {
+ PrintHelp ("contents");
+ }
+
+ return S_OK;
+}
diff --git a/src/ToolBox/SOS/Strike/strike.h b/src/ToolBox/SOS/Strike/strike.h
new file mode 100644
index 0000000000..e070898ff5
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/strike.h
@@ -0,0 +1,144 @@
+// 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.
+
+// ==++==
+//
+
+//
+// ==--==
+#ifndef __strike_h__
+#define __strike_h__
+
+#ifndef _countof
+#define _countof(x) (sizeof(x)/sizeof(x[0]))
+#endif
+
+#if defined(_MSC_VER)
+#pragma warning(disable:4245) // signed/unsigned mismatch
+#pragma warning(disable:4100) // unreferenced formal parameter
+#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
+#pragma warning(disable:4127) // conditional expression is constant
+#pragma warning(disable:6255) // Prefast: alloca indicates failure by raising a stack overflow exception
+#endif
+
+#ifdef PAL_STDCPP_COMPAT
+#define _iswprint PAL_iswprint
+#define _wcslen PAL_wcslen
+#define _wcsncmp PAL_wcsncmp
+#define _wcsrchr PAL_wcsrchr
+#define _wcscmp PAL_wcscmp
+#define _wcschr PAL_wcschr
+#define _wcscspn PAL_wcscspn
+#define _wcscat PAL_wcscat
+#define _wcsstr PAL_wcsstr
+#else // PAL_STDCPP_COMPAT
+#define _iswprint iswprint
+#define _wcslen wcslen
+#define _wcsncmp wcsncmp
+#define _wcsrchr wcsrchr
+#define _wcscmp wcscmp
+#define _wcschr wcschr
+#define _wcscspn wcscspn
+#define _wcscat wcscat
+#define _wcsstr wcsstr
+#endif // !PAL_STDCPP_COMPAT
+
+#ifdef PLATFORM_UNIX
+#define _vsnprintf vsnprintf
+#endif
+
+#define ___in _SAL1_Source_(__in, (), _In_)
+#define ___out _SAL1_Source_(__out, (), _Out_)
+
+#define _max(a, b) (((a) > (b)) ? (a) : (b))
+#define _min(a, b) (((a) < (b)) ? (a) : (b))
+
+#include <winternl.h>
+#include <winver.h>
+#include <windows.h>
+
+#include <wchar.h>
+
+//#define NOEXTAPI
+#define KDEXT_64BIT
+#include <wdbgexts.h>
+#undef DECLARE_API
+#undef GetContext
+#undef SetContext
+#undef ReadMemory
+#undef WriteMemory
+#undef GetFieldValue
+#undef StackTrace
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+
+#ifndef PAL_STDCPP_COMPAT
+#include <malloc.h>
+#endif
+
+#ifdef FEATURE_PAL
+#ifndef alloca
+#define alloca __builtin_alloca
+#endif
+#ifndef _alloca
+#define _alloca __builtin_alloca
+#endif
+#endif // FEATURE_PAL
+
+#include <stddef.h>
+
+#ifndef FEATURE_PAL
+#include <basetsd.h>
+#endif
+
+#define CORHANDLE_MASK 0x1
+
+#include "static_assert.h"
+
+// exts.h includes dbgeng.h which has a bunch of IIDs we need instantiated.
+#define INITGUID
+#include "guiddef.h"
+
+#ifdef FEATURE_PAL
+#define SOS_PTR(x) (size_t)(x)
+#else // FEATURE_PAL
+#define SOS_PTR(x) (unsigned __int64)(x)
+#endif // FEATURE_PAL else
+
+#include "exts.h"
+
+//Alignment constant for allocation
+#if defined(_TARGET_X86_) || defined(_TARGET_ARM_)
+#define ALIGNCONST 3
+#else
+#define ALIGNCONST 7
+#endif
+
+//The large object heap uses a different alignment
+#define ALIGNCONSTLARGE 7
+
+#ifdef _WIN64
+#define SIZEOF_OBJHEADER 8
+#else // !_WIN64
+#define SIZEOF_OBJHEADER 4
+#endif // !_WIN64
+
+#define plug_skew SIZEOF_OBJHEADER
+#define min_obj_size (sizeof(BYTE*)+plug_skew+sizeof(size_t))
+
+extern BOOL CallStatus;
+
+
+#ifndef NT_SUCCESS
+#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
+#endif
+
+HRESULT SetNGENCompilerFlags(DWORD flags);
+
+
+#endif // __strike_h__
diff --git a/src/ToolBox/SOS/Strike/util.cpp b/src/ToolBox/SOS/Strike/util.cpp
new file mode 100644
index 0000000000..9eec76e42c
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/util.cpp
@@ -0,0 +1,6975 @@
+// 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 "sos.h"
+#include "disasm.h"
+#include <dbghelp.h>
+
+#include "corhdr.h"
+#include "cor.h"
+#include "dacprivate.h"
+#include "sospriv.h"
+#include "corerror.h"
+#include "safemath.h"
+
+#include <psapi.h>
+#include <cordebug.h>
+#include <xcordebug.h>
+#include <metahost.h>
+#include <mscoree.h>
+#include <tchar.h>
+#include "debugshim.h"
+
+#ifdef FEATURE_PAL
+#include "datatarget.h"
+#endif // FEATURE_PAL
+#include "gcinfo.h"
+
+#ifndef STRESS_LOG
+#define STRESS_LOG
+#endif // STRESS_LOG
+#define STRESS_LOG_READONLY
+#include "stresslog.h"
+
+#ifndef FEATURE_PAL
+#define MAX_SYMBOL_LEN 4096
+#define SYM_BUFFER_SIZE (sizeof(IMAGEHLP_SYMBOL) + MAX_SYMBOL_LEN)
+char symBuffer[SYM_BUFFER_SIZE];
+PIMAGEHLP_SYMBOL sym = (PIMAGEHLP_SYMBOL) symBuffer;
+#else
+#include <sys/stat.h>
+#include <coreruncommon.h>
+#include <dlfcn.h>
+#endif // !FEATURE_PAL
+
+#include <coreclrhost.h>
+#include <set>
+
+LoadSymbolsForModuleDelegate SymbolReader::loadSymbolsForModuleDelegate;
+DisposeDelegate SymbolReader::disposeDelegate;
+ResolveSequencePointDelegate SymbolReader::resolveSequencePointDelegate;
+GetLocalVariableName SymbolReader::getLocalVariableNameDelegate;
+GetLineByILOffsetDelegate SymbolReader::getLineByILOffsetDelegate;
+
+const char * const CorElementTypeName[ELEMENT_TYPE_MAX]=
+{
+#define TYPEINFO(e,ns,c,s,g,ia,ip,if,im,gv) c,
+#include "cortypeinfo.h"
+#undef TYPEINFO
+};
+
+const char * const CorElementTypeNamespace[ELEMENT_TYPE_MAX]=
+{
+#define TYPEINFO(e,ns,c,s,g,ia,ip,if,im,gv) ns,
+#include "cortypeinfo.h"
+#undef TYPEINFO
+};
+
+IXCLRDataProcess *g_clrData = NULL;
+ISOSDacInterface *g_sos = NULL;
+ICorDebugProcess *g_pCorDebugProcess = NULL;
+
+#ifndef IfFailRet
+#define IfFailRet(EXPR) do { Status = (EXPR); if(FAILED(Status)) { return (Status); } } while (0)
+#endif
+
+#ifndef IfFailGoto
+#define IfFailGoto(EXPR, label) do { Status = (EXPR); if(FAILED(Status)) { goto label; } } while (0)
+#endif // IfFailGoto
+
+#ifndef IfFailGo
+#define IfFailGo(EXPR) IfFailGoto(EXPR, Error)
+#endif // IfFailGo
+
+// Max number of reverted rejit versions that !dumpmd and !ip2md will print
+const UINT kcMaxRevertedRejitData = 10;
+
+#ifndef FEATURE_PAL
+
+// ensure we always allocate on the process heap
+void* __cdecl operator new(size_t size) throw()
+{ return HeapAlloc(GetProcessHeap(), 0, size); }
+void __cdecl operator delete(void* pObj) throw()
+{ HeapFree(GetProcessHeap(), 0, pObj); }
+
+void* __cdecl operator new[](size_t size) throw()
+{ return HeapAlloc(GetProcessHeap(), 0, size); }
+void __cdecl operator delete[](void* pObj) throw()
+{ HeapFree(GetProcessHeap(), 0, pObj); }
+
+/**********************************************************************\
+* Here we define types and functions that support custom COM *
+* activation rules, as defined by the CIOptions enum. *
+* *
+\**********************************************************************/
+
+typedef unsigned __int64 QWORD;
+
+namespace com_activation
+{
+ //
+ // Forward declarations for the implementation methods
+ //
+
+ HRESULT CreateInstanceCustomImpl(
+ REFCLSID clsid,
+ REFIID iid,
+ LPCWSTR dllName,
+ CIOptions cciOptions,
+ void** ppItf);
+ HRESULT ClrCreateInstance(
+ REFCLSID clsid,
+ REFIID iid,
+ LPCWSTR dllName,
+ CIOptions cciOptions,
+ void** ppItf);
+ HRESULT CreateInstanceFromPath(
+ REFCLSID clsid,
+ REFIID iid,
+ LPCWSTR path,
+ void** ppItf);
+ BOOL GetPathFromModule(
+ HMODULE hModule,
+ __in_ecount(cFqPath) LPWSTR fqPath,
+ DWORD cFqPath);
+ HRESULT PickClrRuntimeInfo(
+ ICLRMetaHost *pMetaHost,
+ CIOptions cciOptions,
+ ICLRRuntimeInfo** ppClr);
+ QWORD VerString2Qword(LPCWSTR vStr);
+ void CleanupClsidHmodMap();
+
+ // Helper structures for defining the CLSID -> HMODULE hash table we
+ // use for caching already activated objects
+ class hash_compareGUID
+ {
+ public:
+ static const size_t bucket_size = 4;
+ static const size_t min_buckets = 8;
+ hash_compareGUID()
+ { }
+
+ size_t operator( )(const GUID& _Key) const
+ {
+ DWORD *pdw = (DWORD*)&_Key;
+ return (size_t)(pdw[0] ^ pdw[1] ^ pdw[2] ^ pdw[3]);
+ }
+
+ bool operator( )(const GUID& _Key1, const GUID& _Key2) const
+ { return memcmp(&_Key1, &_Key2, sizeof(GUID)) == -1; }
+ };
+
+ static std::unordered_map<GUID, HMODULE, hash_compareGUID> *g_pClsidHmodMap = NULL;
+
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* CreateInstanceCustomImpl() provides a way to activate a COM object *
+* w/o triggering the FeatureOnDemand dialog. In order to do this we *
+* must avoid using the CoCreateInstance() API, which, on a machine *
+* with v4+ installed and w/o v2, would trigger this. *
+* CreateInstanceCustom() activates the requested COM object according *
+* to the specified passed in CIOptions, in the following order *
+* (skipping the steps not enabled in the CIOptions flags passed in): *
+* 1. Attempt to activate the COM object using a framework install: *
+* a. If the debugger machine has a V4+ shell shim use the shim *
+* to activate the object *
+* b. Otherwise simply call CoCreateInstance *
+* 2. If unsuccessful attempt to activate looking for the dllName in *
+* the same folder as the DAC was loaded from *
+* 3. If unsuccessful attempt to activate the COM object looking in *
+* every path specified in the debugger's .exepath and .sympath *
+\**********************************************************************/
+HRESULT CreateInstanceCustomImpl(
+ REFCLSID clsid,
+ REFIID iid,
+ LPCWSTR dllName,
+ CIOptions cciOptions,
+ void** ppItf)
+{
+ _ASSERTE(ppItf != NULL);
+
+ if (ppItf == NULL)
+ return E_POINTER;
+
+ WCHAR wszClsid[64] = W("<CLSID>");
+
+ // Step 1: Attempt activation using an installed runtime
+ if ((cciOptions & cciFxMask) != 0)
+ {
+ CIOptions opt = cciOptions & cciFxMask;
+ if (SUCCEEDED(ClrCreateInstance(clsid, iid, dllName, opt, ppItf)))
+ return S_OK;
+
+ ExtDbgOut("Failed to instantiate {%ls} from installed .NET framework locations.\n", wszClsid);
+ }
+
+ if ((cciOptions & cciDbiColocated) != 0)
+ {
+ // if we institute a way to retrieve the module for the current DBI we
+ // can perform the same steps as for the DAC.
+ }
+
+ // Step 2: attempt activation using the folder the DAC was loaded from
+ if ((cciOptions & cciDacColocated) != 0)
+ {
+ _ASSERTE(dllName != NULL);
+ HMODULE hDac = NULL;
+ WCHAR path[MAX_LONGPATH];
+
+ if (SUCCEEDED(g_sos->GetDacModuleHandle(&hDac))
+ && GetPathFromModule(hDac, path, _countof(path)))
+ {
+ // build the fully qualified file name and attempt instantiation
+ if (wcscat_s(path, dllName) == 0
+ && SUCCEEDED(CreateInstanceFromPath(clsid, iid, path, ppItf)))
+ {
+ return S_OK;
+ }
+ }
+
+ ExtDbgOut("Failed to instantiate {%ls} from DAC location.\n", wszClsid);
+ }
+
+ // Step 3: attempt activation using the debugger's .exepath and .sympath
+ if ((cciOptions & cciDbgPath) != 0)
+ {
+ _ASSERTE(dllName != NULL);
+
+ ToRelease<IDebugSymbols3> spSym3(NULL);
+ HRESULT hr = g_ExtSymbols->QueryInterface(__uuidof(IDebugSymbols3), (void**)&spSym3);
+ if (FAILED(hr))
+ {
+ ExtDbgOut("Unable to query IDebugSymbol3 HRESULT=0x%x.\n", hr);
+ goto ErrDbgPath;
+ }
+
+ typedef HRESULT (__stdcall IDebugSymbols3::*GetPathFunc)(LPWSTR , ULONG, ULONG*);
+
+ // Handle both the image path and the symbol path
+ GetPathFunc rgGetPathFuncs[] =
+ { &IDebugSymbols3::GetImagePathWide, &IDebugSymbols3::GetSymbolPathWide };
+
+ for (int i = 0; i < _countof(rgGetPathFuncs); ++i)
+ {
+ ULONG pathSize = 0;
+
+ // get the path buffer size
+ if ((spSym3.GetPtr()->*rgGetPathFuncs[i])(NULL, 0, &pathSize) != S_OK)
+ {
+ continue;
+ }
+
+ ArrayHolder<WCHAR> imgPath = new WCHAR[pathSize+MAX_LONGPATH+1];
+ if (imgPath == NULL)
+ {
+ continue;
+ }
+
+ // actually get the path
+ if ((spSym3.GetPtr()->*rgGetPathFuncs[i])(imgPath, pathSize, NULL) != S_OK)
+ {
+ continue;
+ }
+
+ LPWSTR ctx;
+ LPCWSTR pathElem = wcstok_s(imgPath, W(";"), &ctx);
+ while (pathElem != NULL)
+ {
+ WCHAR fullName[MAX_LONGPATH];
+ wcscpy_s(fullName, _countof(fullName), pathElem);
+ if (wcscat_s(fullName, W("\\")) == 0 && wcscat_s(fullName, dllName) == 0)
+ {
+ if (SUCCEEDED(CreateInstanceFromPath(clsid, iid, fullName, ppItf)))
+ {
+ return S_OK;
+ }
+ }
+
+ pathElem = wcstok_s(NULL, W(";"), &ctx);
+ }
+ }
+
+ ErrDbgPath:
+ ExtDbgOut("Failed to instantiate {%ls} from debugger's image path.\n", wszClsid);
+ }
+
+ return REGDB_E_CLASSNOTREG;
+}
+
+
+#ifdef _MSC_VER
+// SOS is essentially single-threaded. ignore "construction of local static object is not thread-safe"
+#pragma warning(push)
+#pragma warning(disable:4640)
+#endif // _MSC_VER
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* ClrCreateInstance() attempts to activate a COM object using an *
+* installed framework: *
+* a. If the debugger machine has a V4+ shell shim use the shim to *
+* activate the object *
+* b. Otherwise simply call CoCreateInstance *
+\**********************************************************************/
+HRESULT ClrCreateInstance(
+ REFCLSID clsid,
+ REFIID iid,
+ LPCWSTR dllName,
+ CIOptions cciOptions,
+ void** ppItf)
+{
+ _ASSERTE((cciOptions & ~cciFxMask) == 0 && (cciOptions & cciFxMask) != 0);
+ HRESULT Status = S_OK;
+
+ static CIOptions prevOpt = 0;
+ static HRESULT prevHr = S_OK;
+
+ // if we already tried to use NetFx install and failed don't try it again
+ if (prevOpt == cciOptions && FAILED(prevHr))
+ {
+ return prevHr;
+ }
+
+ prevOpt = cciOptions;
+
+ // first try usig the metahost API:
+ HRESULT (__stdcall *pfnCLRCreateInstance)(REFCLSID clsid, REFIID riid, LPVOID * ppInterface) = NULL;
+ HMODULE hMscoree = NULL;
+
+ // if there's a v4+ shim on the debugger machine
+ if (GetProcAddressT("CLRCreateInstance", W("mscoree.dll"), &pfnCLRCreateInstance, &hMscoree))
+ {
+ // attempt to create an ICLRMetaHost instance
+ ToRelease<ICLRMetaHost> spMH;
+ Status = pfnCLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (void**)&spMH);
+ if (Status == E_NOTIMPL)
+ {
+ // E_NOTIMPL means we have a v4 aware mscoree but no v4+ framework
+ IfFailGo( CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, iid, ppItf) );
+ }
+ else
+ {
+ IfFailGo( Status );
+
+ // pick a runtime according to cciOptions
+ ToRelease<ICLRRuntimeInfo> spClr;
+ IfFailGo( PickClrRuntimeInfo(spMH, cciOptions, &spClr) );
+
+ // activate the COM object
+ Status = spClr->GetInterface(clsid, iid, ppItf);
+
+ if (FAILED(Status) && dllName)
+ {
+ // if we have a v4+ runtime that does not have the fix to activate the requested CLSID
+ // try activating with the path
+ WCHAR clrDir[MAX_LONGPATH];
+ DWORD cchClrDir = _countof(clrDir);
+ IfFailGo( spClr->GetRuntimeDirectory(clrDir, &cchClrDir) );
+ IfFailGo( wcscat_s(clrDir, dllName) == 0 ? S_OK : E_FAIL );
+ IfFailGo( CreateInstanceFromPath(clsid, iid, clrDir, ppItf) );
+ }
+ }
+ }
+ else
+ {
+ // otherwise fallback to regular COM activation
+ IfFailGo( CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, iid, ppItf) );
+ }
+
+Error:
+ if (hMscoree != NULL)
+ {
+ FreeLibrary(hMscoree);
+ }
+
+ // remember if we succeeded or failed
+ prevHr = Status;
+
+ return Status;
+}
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif // _MSC_VER
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* CreateInstanceFromPath() instantiates a COM object using a passed in *
+* fully-qualified path and a CLSID. *
+* *
+* Note: *
+* *
+* It uses a unordered_map to cache the mapping between a CLSID and the *
+* HMODULE that is successfully used to activate the CLSID from. When *
+* SOS is unloaded (in DebugExtensionUninitialize()) we call *
+* FreeLibrary() for all cached HMODULEs. *
+\**********************************************************************/
+HRESULT CreateInstanceFromPath(
+ REFCLSID clsid,
+ REFIID iid,
+ LPCWSTR path,
+ void** ppItf)
+{
+ HRESULT Status = S_OK;
+ HRESULT (__stdcall *pfnDllGetClassObject)(REFCLSID rclsid, REFIID riid, LPVOID *ppv) = NULL;
+
+ HMODULE hmod = NULL;
+
+ if (g_pClsidHmodMap == NULL)
+ {
+ g_pClsidHmodMap = new std::unordered_map<GUID, HMODULE, hash_compareGUID>();
+ OnUnloadTask::Register(CleanupClsidHmodMap);
+ }
+
+ auto it = g_pClsidHmodMap->find(clsid);
+ if (it != g_pClsidHmodMap->end())
+ hmod = it->second;
+
+ if (!GetProcAddressT("DllGetClassObject", path, &pfnDllGetClassObject, &hmod))
+ return REGDB_E_CLASSNOTREG;
+
+ ToRelease<IClassFactory> pFactory;
+ IfFailGo(pfnDllGetClassObject(clsid, IID_IClassFactory, (void**)&pFactory));
+
+ IfFailGo(pFactory->CreateInstance(NULL, iid, ppItf));
+
+ // only cache the HMODULE if we successfully created the COM object
+ (*g_pClsidHmodMap)[clsid] = hmod;
+
+ return S_OK;
+
+Error:
+ if (hmod != NULL)
+ FreeLibrary(hmod);
+
+ return Status;
+}
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* CleanupClsidHmodMap() cleans up the CLSID -> HMODULE map used to *
+* cache successful activations from specific paths. This is registered *
+* as an OnUnloadTask in CreateInstanceFromPath(), and executes when *
+* SOS is unloaded, in DebugExtensionUninitialize(). *
+\**********************************************************************/
+void CleanupClsidHmodMap()
+{
+ if (g_pClsidHmodMap != NULL)
+ {
+ for (auto it = g_pClsidHmodMap->begin(); it != g_pClsidHmodMap->end(); ++it)
+ {
+ _ASSERTE(it->second != NULL);
+ FreeLibrary(it->second);
+ }
+
+ delete g_pClsidHmodMap;
+ g_pClsidHmodMap = NULL;
+ }
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* PickClrRuntimeInfo() selects on CLR runtime from the ones installed *
+* on the debugger machine. If cciFxAny is specified in cciOptions it *
+* simply returns the first runtime enumerated by the metahost *
+* interface. If cciLatestFx is specified we pick the runtime with the *
+* highest version (parsing the string returned from *
+* ICLRRuntimeInfo::GetVersionString(). *
+\**********************************************************************/
+HRESULT PickClrRuntimeInfo(
+ ICLRMetaHost *pMetaHost,
+ CIOptions cciOptions,
+ ICLRRuntimeInfo** ppClr)
+{
+ if (ppClr == NULL)
+ return E_POINTER;
+
+ // only support "Any framework" and "latest framework"
+ if (cciOptions != cciAnyFx && cciOptions != cciLatestFx)
+ return E_INVALIDARG;
+
+ HRESULT Status = S_OK;
+ *ppClr = NULL;
+
+ // get the CLRRuntime enumerator
+ ToRelease<IEnumUnknown> spClrsEnum;
+ IfFailRet(pMetaHost->EnumerateInstalledRuntimes(&spClrsEnum));
+
+ ToRelease<ICLRRuntimeInfo> spChosenClr;
+ QWORD verMax = 0;
+
+ int cntClr = 0;
+ while (1)
+ {
+ // retrieve the next ICLRRuntimeInfo
+ ULONG cnt;
+ ToRelease<IUnknown> spClrUnk;
+ if (spClrsEnum->Next(1, &spClrUnk, &cnt) != S_OK || cnt != 1)
+ break;
+
+ ToRelease<ICLRRuntimeInfo> spClr;
+ BOOL bLoadable = FALSE;
+ // ignore un-loadable runtimes
+ if (FAILED(spClrUnk->QueryInterface(IID_ICLRRuntimeInfo, (void**)&spClr))
+ || FAILED(spClr->IsLoadable(&bLoadable))
+ || !bLoadable)
+ {
+ continue;
+ }
+
+ WCHAR vStr[128];
+ DWORD cStr = _countof(vStr);
+ if (FAILED(spClr->GetVersionString(vStr, &cStr)))
+ continue;
+
+ ++cntClr;
+
+ if ((cciOptions & cciAnyFx) != 0)
+ {
+ spChosenClr = spClr.Detach();
+ break;
+ }
+
+ QWORD ver = VerString2Qword(vStr);
+ if ((cciOptions & cciLatestFx) != 0)
+ {
+ if (ver > verMax)
+ {
+ verMax = ver;
+ spChosenClr = spClr.Detach();
+ }
+ }
+ }
+
+ if (cntClr == 0 || spChosenClr == NULL)
+ {
+ *ppClr = NULL;
+ return E_NOINTERFACE;
+ }
+ else
+ {
+ *ppClr = spChosenClr.Detach();
+ return S_OK;
+ }
+}
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* VerString2Qword() parses a string as returned from *
+* ICLRRuntimeInfo::GetVersionString() into a QWORD, assuming every *
+* numeric element is a WORD portion in the QWORD. *
+\**********************************************************************/
+QWORD VerString2Qword(LPCWSTR vStr)
+{
+ _ASSERTE(vStr[0] == L'v' || vStr[0] == L'V');
+ QWORD result = 0;
+
+ DWORD v1, v2, v3;
+ if (swscanf_s(vStr+1, W("%d.%d.%d"), &v1, &v2, &v3) == 3)
+ {
+ result = ((QWORD)v1 << 48) | ((QWORD)v2 << 32) | ((QWORD)v3 << 16);
+ }
+ else if (swscanf_s(vStr+1, W("%d.%d"), &v1, &v2) == 2)
+ {
+ result = ((QWORD)v1 << 48) | ((QWORD)v2 << 32);
+ }
+ else if (swscanf_s(vStr+1, W("%d"), &v1) == 1)
+ {
+ result = ((QWORD)v1 << 48);
+ }
+
+ return result;
+}
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* GetPathFromModule() returns the name of the folder containing the *
+* file associated with hModule. *
+ \**********************************************************************/
+BOOL GetPathFromModule(
+ HMODULE hModule,
+ __in_ecount(cFqPath) LPWSTR fqPath,
+ DWORD cFqPath)
+{
+ int len = GetModuleFileNameW(hModule, fqPath, cFqPath);
+ if (len == 0 || len == cFqPath)
+ return FALSE;
+
+ WCHAR *pLastSep = _wcsrchr(fqPath, DIRECTORY_SEPARATOR_CHAR_W);
+ if (pLastSep == NULL || pLastSep+1 >= fqPath+cFqPath)
+ return FALSE;
+
+ *(pLastSep+1) = L'\0';
+
+ return TRUE;
+}
+
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* CreateInstanceCustom() provides a way to activate a COM object w/o *
+* triggering the FeatureOnDemand dialog. In order to do this we *
+* must avoid using the CoCreateInstance() API, which, on a machine *
+* with v4+ installed and w/o v2, would trigger this. *
+* CreateInstanceCustom() activates the requested COM object according *
+* to the specified passed in CIOptions, in the following order *
+* (skipping the steps not enabled in the CIOptions flags passed in): *
+* 1. Attempt to activate the COM object using a framework install: *
+* a. If the debugger machine has a V4+ shell shim use the shim *
+* to activate the object *
+* b. Otherwise simply call CoCreateInstance *
+* 2. If unsuccessful attempt to activate looking for the dllName in *
+* the same folder as the DAC was loaded from *
+* 3. If unsuccessful attempt to activate the COM object looking in *
+* every path specified in the debugger's .exepath and .sympath *
+\**********************************************************************/
+HRESULT CreateInstanceCustom(
+ REFCLSID clsid,
+ REFIID iid,
+ LPCWSTR dllName,
+ CIOptions cciOptions,
+ void** ppItf)
+{
+ return com_activation::CreateInstanceCustomImpl(clsid, iid, dllName, cciOptions, ppItf);
+}
+
+
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to get the memory address given a symbol *
+* name. It handles difference in symbol name between ntsd and *
+* windbg. *
+* *
+\**********************************************************************/
+DWORD_PTR GetValueFromExpression (___in __in_z const char *const instr)
+{
+ ULONG64 dwAddr;
+ const char *str = instr;
+ char name[256];
+
+ dwAddr = 0;
+ HRESULT hr = g_ExtSymbols->GetOffsetByName (str, &dwAddr);
+ if (SUCCEEDED(hr))
+ return (DWORD_PTR)dwAddr;
+ else if (hr == S_FALSE && dwAddr)
+ return (DWORD_PTR)dwAddr;
+
+ strcpy_s (name, _countof(name), str);
+ char *ptr;
+ if ((ptr = strstr (name, "__")) != NULL)
+ {
+ ptr[0] = ':';
+ ptr[1] = ':';
+ ptr += 2;
+ while ((ptr = strstr(ptr, "__")) != NULL)
+ {
+ ptr[0] = ':';
+ ptr[1] = ':';
+ ptr += 2;
+ }
+ dwAddr = 0;
+ hr = g_ExtSymbols->GetOffsetByName (name, &dwAddr);
+ if (SUCCEEDED(hr))
+ return (DWORD_PTR)dwAddr;
+ else if (hr == S_FALSE && dwAddr)
+ return (DWORD_PTR)dwAddr;
+ }
+ else if ((ptr = strstr (name, "::")) != NULL)
+ {
+ ptr[0] = '_';
+ ptr[1] = '_';
+ ptr += 2;
+ while ((ptr = strstr(ptr, "::")) != NULL)
+ {
+ ptr[0] = '_';
+ ptr[1] = '_';
+ ptr += 2;
+ }
+ dwAddr = 0;
+ hr = g_ExtSymbols->GetOffsetByName (name, &dwAddr);
+ if (SUCCEEDED(hr))
+ return (DWORD_PTR)dwAddr;
+ else if (hr == S_FALSE && dwAddr)
+ return (DWORD_PTR)dwAddr;
+ }
+ return 0;
+}
+
+#endif // FEATURE_PAL
+
+ModuleInfo moduleInfo[MSCOREND] = {{0,FALSE,0},{0,FALSE,0},{0,FALSE,0}};
+
+void ReportOOM()
+{
+ ExtOut("SOS Error: Out of memory\n");
+}
+
+HRESULT CheckEEDll()
+{
+#ifndef FEATURE_PAL
+ VS_FIXEDFILEINFO ee = {};
+
+ static VS_FIXEDFILEINFO sos = {};
+ static BOOL sosDataInit = FALSE;
+
+ BOOL result = GetEEVersion(&ee);
+ if (result && !sosDataInit)
+ {
+ result = GetSOSVersion(&sos);
+
+ if (result)
+ sosDataInit = TRUE;
+ }
+
+ // We will ignore errors because it's possible sos is being loaded before CLR.
+ if (result)
+ {
+ if ((ee.dwFileVersionMS != sos.dwFileVersionMS) || (ee.dwFileVersionLS != sos.dwFileVersionLS))
+ {
+ ExtOut("The version of SOS does not match the version of CLR you are debugging. Please\n");
+ ExtOut("load the matching version of SOS for the version of CLR you are debugging.\n");
+ ExtOut("CLR Version: %u.%u.%u.%u\n",
+ HIWORD(ee.dwFileVersionMS),
+ LOWORD(ee.dwFileVersionMS),
+ HIWORD(ee.dwFileVersionLS),
+ LOWORD(ee.dwFileVersionLS));
+
+ ExtOut("SOS Version: %u.%u.%u.%u\n",
+ HIWORD(sos.dwFileVersionMS),
+ LOWORD(sos.dwFileVersionMS),
+ HIWORD(sos.dwFileVersionLS),
+ LOWORD(sos.dwFileVersionLS));
+ }
+ }
+
+ DEBUG_MODULE_PARAMETERS Params;
+
+ // Do we have clr.dll
+ if (moduleInfo[MSCORWKS].baseAddr == 0)
+ {
+ g_ExtSymbols->GetModuleByModuleName (MAIN_CLR_MODULE_NAME_A,0,NULL,
+ &moduleInfo[MSCORWKS].baseAddr);
+ if (moduleInfo[MSCORWKS].baseAddr != 0 && moduleInfo[MSCORWKS].hasPdb == FALSE)
+ {
+ g_ExtSymbols->GetModuleParameters (1, &moduleInfo[MSCORWKS].baseAddr, 0, &Params);
+ if (Params.SymbolType == SymDeferred)
+ {
+ g_ExtSymbols->Reload("/f " MAIN_CLR_DLL_NAME_A);
+ g_ExtSymbols->GetModuleParameters (1, &moduleInfo[MSCORWKS].baseAddr, 0, &Params);
+ }
+
+ if (Params.SymbolType == SymPdb || Params.SymbolType == SymDia)
+ {
+ moduleInfo[MSCORWKS].hasPdb = TRUE;
+ }
+
+ moduleInfo[MSCORWKS].size = Params.Size;
+ }
+ if (moduleInfo[MSCORWKS].baseAddr != 0 && moduleInfo[MSCORWKS].hasPdb == FALSE)
+ ExtOut("PDB symbol for clr.dll not loaded\n");
+ }
+
+ return (moduleInfo[MSCORWKS].baseAddr != 0) ? S_OK : E_FAIL;
+#else
+ return S_OK;
+#endif // FEATURE_PAL
+}
+
+EEFLAVOR GetEEFlavor ()
+{
+#ifdef FEATURE_PAL
+ return MSCORWKS;
+#else // FEATUER_PAL
+ EEFLAVOR flavor = UNKNOWNEE;
+
+ if (SUCCEEDED(g_ExtSymbols->GetModuleByModuleName(MAIN_CLR_MODULE_NAME_A,0,NULL,NULL))) {
+ flavor = MSCORWKS;
+ }
+ return flavor;
+#endif // FEATURE_PAL else
+}
+
+BOOL IsDumpFile ()
+{
+ static int g_fDumpFile = -1;
+ if (g_fDumpFile == -1) {
+ ULONG Class;
+ ULONG Qualifier;
+ g_ExtControl->GetDebuggeeType(&Class,&Qualifier);
+ if (Qualifier >= DEBUG_DUMP_SMALL)
+ g_fDumpFile = 1;
+ else
+ g_fDumpFile = 0;
+ }
+ return g_fDumpFile != 0;
+}
+
+BOOL g_InMinidumpSafeMode = FALSE;
+
+BOOL IsMiniDumpFileNODAC ()
+{
+#ifndef FEATURE_PAL
+ ULONG Class;
+ ULONG Qualifier;
+ g_ExtControl->GetDebuggeeType(&Class,&Qualifier);
+ if (Qualifier == DEBUG_DUMP_SMALL)
+ {
+ g_ExtControl->GetDumpFormatFlags(&Qualifier);
+ if ((Qualifier & DEBUG_FORMAT_USER_SMALL_FULL_MEMORY) == 0)
+ {
+ return TRUE;
+ }
+ }
+
+#endif // FEATURE_PAL
+ return FALSE;
+}
+
+
+// We use this predicate to mean the smallest, most restrictive kind of
+// minidump file. There is no heap dump, only that set of information
+// gathered to make !clrstack, !threads, !help, !eeversion and !pe work.
+BOOL IsMiniDumpFile ()
+{
+#ifndef FEATURE_PAL
+ // It is okay for this to be static, because although the debugger may debug multiple
+ // managed processes at once, I don't believe multiple dumpfiles of different
+ // types is a scenario to worry about.
+ if (IsMiniDumpFileNODAC())
+ {
+ // Beyond recognizing the dump type above, all we can rely on for this
+ // is a flag set by the user indicating they want a safe mode minidump
+ // experience. This is primarily for testing.
+ return g_InMinidumpSafeMode;
+ }
+
+#endif // FEATURE_PAL
+ return FALSE;
+}
+
+ULONG DebuggeeType()
+{
+ static ULONG Class = DEBUG_CLASS_UNINITIALIZED;
+ if (Class == DEBUG_CLASS_UNINITIALIZED) {
+ ULONG Qualifier;
+ g_ExtControl->GetDebuggeeType(&Class,&Qualifier);
+ }
+ return Class;
+}
+
+#ifndef FEATURE_PAL
+
+// Check if a file exist
+BOOL FileExist (const char *filename)
+{
+ WIN32_FIND_DATA FindFileData;
+ HANDLE handle = FindFirstFile (filename, &FindFileData);
+ if (handle != INVALID_HANDLE_VALUE) {
+ FindClose (handle);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+BOOL FileExist (const WCHAR *filename)
+{
+ WIN32_FIND_DATAW FindFileData;
+ HANDLE handle = FindFirstFileW (filename, &FindFileData);
+ if (handle != INVALID_HANDLE_VALUE) {
+ FindClose (handle);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to find out if a dll is bbt-ized *
+* *
+\**********************************************************************/
+BOOL IsRetailBuild (size_t base)
+{
+ IMAGE_DOS_HEADER DosHeader;
+ if (g_ExtData->ReadVirtual(TO_CDADDR(base), &DosHeader, sizeof(DosHeader), NULL) != S_OK)
+ return FALSE;
+ IMAGE_NT_HEADERS32 Header32;
+ if (g_ExtData->ReadVirtual(TO_CDADDR(base + DosHeader.e_lfanew), &Header32, sizeof(Header32), NULL) != S_OK)
+ return FALSE;
+ // If there is no COMHeader, this can not be managed code.
+ if (Header32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress == 0)
+ return FALSE;
+
+ size_t debugDirAddr = base + Header32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
+ size_t nSize = Header32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
+ IMAGE_DEBUG_DIRECTORY debugDir;
+ size_t nbytes = 0;
+ while (nbytes < nSize) {
+ if (g_ExtData->ReadVirtual(TO_CDADDR(debugDirAddr+nbytes), &debugDir, sizeof(debugDir), NULL) != S_OK)
+ return FALSE;
+ if (debugDir.Type == 0xA) {
+ return TRUE;
+ }
+ nbytes += sizeof(debugDir);
+ }
+ return FALSE;
+}
+
+#endif // !FEATURE_PAL
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to read memory from the debugee's *
+* address space. If the initial read fails, it attempts to read *
+* only up to the edge of the page containing "offset". *
+* *
+\**********************************************************************/
+BOOL SafeReadMemory (TADDR offset, PVOID lpBuffer, ULONG cb,
+ PULONG lpcbBytesRead)
+{
+ BOOL bRet = FALSE;
+
+ bRet = SUCCEEDED(g_ExtData->ReadVirtual(TO_CDADDR(offset), lpBuffer, cb,
+ lpcbBytesRead));
+
+ if (!bRet)
+ {
+ cb = (ULONG)(NextOSPageAddress(offset) - offset);
+ bRet = SUCCEEDED(g_ExtData->ReadVirtual(TO_CDADDR(offset), lpBuffer, cb,
+ lpcbBytesRead));
+ }
+ return bRet;
+}
+
+ULONG OSPageSize ()
+{
+ static ULONG pageSize = 0;
+ if (pageSize == 0)
+ g_ExtControl->GetPageSize(&pageSize);
+
+ return pageSize;
+}
+
+size_t NextOSPageAddress (size_t addr)
+{
+ size_t pageSize = OSPageSize();
+ return (addr+pageSize)&(~(pageSize-1));
+}
+
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to get the address of MethodDesc *
+* given an ip address *
+* *
+\**********************************************************************/
+void IP2MethodDesc (DWORD_PTR IP, DWORD_PTR &methodDesc, JITTypes &jitType,
+ DWORD_PTR &gcinfoAddr)
+{
+
+ CLRDATA_ADDRESS EIP = TO_CDADDR(IP);
+ DacpCodeHeaderData codeHeaderData;
+
+ methodDesc = NULL;
+ gcinfoAddr = NULL;
+
+ if (codeHeaderData.Request(g_sos, EIP) != S_OK)
+ {
+ return;
+ }
+
+ methodDesc = (DWORD_PTR) codeHeaderData.MethodDescPtr;
+ jitType = (JITTypes) codeHeaderData.JITType;
+ gcinfoAddr = (DWORD_PTR) codeHeaderData.GCInfo;
+}
+
+BOOL IsValueField (DacpFieldDescData *pFD)
+{
+ return (pFD->Type == ELEMENT_TYPE_VALUETYPE);
+}
+
+void DisplayDataMember (DacpFieldDescData* pFD, DWORD_PTR dwAddr, BOOL fAlign=TRUE)
+{
+ if (dwAddr > 0)
+ {
+ // we must have called this function for a "real" (non-zero size) data type
+ PREFIX_ASSUME(gElementTypeInfo[pFD->Type] != 0);
+
+ DWORD_PTR dwTmp = dwAddr;
+ bool bVTStatic = (pFD->bIsStatic && pFD->Type == ELEMENT_TYPE_VALUETYPE);
+
+ if (gElementTypeInfo[pFD->Type] != NO_SIZE || bVTStatic)
+ {
+ union Value
+ {
+ char ch;
+ short Short;
+ DWORD_PTR ptr;
+ int Int;
+ unsigned int UInt;
+ __int64 Int64;
+ unsigned __int64 UInt64;
+ float Float;
+ double Double;
+ } value;
+
+ ZeroMemory(&value, sizeof(value));
+ if (bVTStatic)
+ {
+ // static VTypes are boxed
+ moveBlock (value, dwTmp, gElementTypeInfo[ELEMENT_TYPE_CLASS]);
+ }
+ else
+ {
+ moveBlock (value, dwTmp, gElementTypeInfo[pFD->Type]);
+ }
+
+ switch (pFD->Type)
+ {
+ case ELEMENT_TYPE_I1:
+ // there's no ANSI conformant type specifier for
+ // signed char, so use the next best thing,
+ // signed short (sign extending)
+ if (fAlign)
+ ExtOut("%" POINTERSIZE "hd", (short)value.ch);
+ else
+ ExtOut("%d", value.ch);
+ break;
+ case ELEMENT_TYPE_I2:
+ if (fAlign)
+ ExtOut("%" POINTERSIZE "hd", value.Short);
+ else
+ ExtOut("%d", value.Short);
+ break;
+ case ELEMENT_TYPE_I4:
+ if (fAlign)
+ ExtOut("%" POINTERSIZE "d", value.Int);
+ else
+ ExtOut("%d", value.Int);
+ break;
+ case ELEMENT_TYPE_I8:
+ ExtOut("%I64d", value.Int64);
+ break;
+ case ELEMENT_TYPE_U1:
+ case ELEMENT_TYPE_BOOLEAN:
+ if (fAlign)
+ // there's no ANSI conformant type specifier for
+ // unsigned char, so use the next best thing,
+ // unsigned short, not extending the sign
+ ExtOut("%" POINTERSIZE "hu", (USHORT)value.Short);
+ else
+ ExtOut("%u", value.ch);
+ break;
+ case ELEMENT_TYPE_U2:
+ if (fAlign)
+ ExtOut("%" POINTERSIZE "hu", value.Short);
+ else
+ ExtOut("%u", value.Short);
+ break;
+ case ELEMENT_TYPE_U4:
+ if (fAlign)
+ ExtOut("%" POINTERSIZE "u", value.UInt);
+ else
+ ExtOut("%u", value.UInt);
+ break;
+ case ELEMENT_TYPE_U8:
+ ExtOut("%I64u", value.UInt64);
+ break;
+ case ELEMENT_TYPE_I:
+ case ELEMENT_TYPE_U:
+ if (fAlign)
+ ExtOut("%" POINTERSIZE "p", SOS_PTR(value.ptr));
+ else
+ ExtOut("%p", SOS_PTR(value.ptr));
+ break;
+ case ELEMENT_TYPE_R4:
+ ExtOut("%f", value.Float);
+ break;
+ case ELEMENT_TYPE_R8:
+ ExtOut("%f", value.Double);
+ break;
+ case ELEMENT_TYPE_CHAR:
+ if (fAlign)
+ ExtOut("%" POINTERSIZE "hx", value.Short);
+ else
+ ExtOut("%x", value.Short);
+ break;
+ case ELEMENT_TYPE_VALUETYPE:
+ if (value.ptr)
+ DMLOut(DMLValueClass(pFD->MTOfType, dwTmp));
+ else
+ ExtOut("%p", SOS_PTR(0));
+ break;
+ default:
+ if (value.ptr)
+ DMLOut(DMLObject(value.ptr));
+ else
+ ExtOut("%p", SOS_PTR(0));
+ break;
+ }
+ }
+ else
+ {
+ if (pFD->Type == ELEMENT_TYPE_VALUETYPE)
+ DMLOut(DMLValueClass(pFD->MTOfType, dwTmp));
+ else
+ ExtOut("%p", SOS_PTR(0));
+ }
+ }
+ else
+ {
+ ExtOut("%" POINTERSIZE "s", " ");
+ }
+}
+
+void GetStaticFieldPTR(DWORD_PTR* pOutPtr, DacpDomainLocalModuleData* pDLMD, DacpMethodTableData* pMTD, DacpFieldDescData* pFDD, BYTE* pFlags = 0)
+{
+ DWORD_PTR dwTmp;
+
+ if (pFDD->Type == ELEMENT_TYPE_VALUETYPE
+ || pFDD->Type == ELEMENT_TYPE_CLASS)
+ {
+ dwTmp = (DWORD_PTR) pDLMD->pGCStaticDataStart + pFDD->dwOffset;
+ }
+ else
+ {
+ dwTmp = (DWORD_PTR) pDLMD->pNonGCStaticDataStart + pFDD->dwOffset;
+ }
+
+ *pOutPtr = 0;
+
+ if (pMTD->bIsDynamic)
+ {
+ ExtOut("dynamic statics NYI");
+ return;
+ }
+ else
+ {
+ if (pFlags && pMTD->bIsShared)
+ {
+ BYTE flags;
+ DWORD_PTR pTargetFlags = (DWORD_PTR) pDLMD->pClassData + RidFromToken(pMTD->cl) - 1;
+ move_xp (flags, pTargetFlags);
+
+ *pFlags = flags;
+ }
+
+
+ *pOutPtr = dwTmp;
+ }
+ return;
+}
+
+void GetDLMFlags(DacpDomainLocalModuleData* pDLMD, DacpMethodTableData* pMTD, BYTE* pFlags)
+{
+ if (pMTD->bIsDynamic)
+ {
+ ExtOut("dynamic statics NYI");
+ return;
+ }
+ else
+ {
+ if (pFlags)
+ {
+ BYTE flags;
+ DWORD_PTR pTargetFlags = (DWORD_PTR) pDLMD->pClassData + RidFromToken(pMTD->cl) - 1;
+ move_xp (flags, pTargetFlags);
+
+ *pFlags = flags;
+ }
+ }
+ return;
+}
+
+void GetThreadStaticFieldPTR(DWORD_PTR* pOutPtr, DacpThreadLocalModuleData* pTLMD, DacpMethodTableData* pMTD, DacpFieldDescData* pFDD, BYTE* pFlags = 0)
+{
+ DWORD_PTR dwTmp;
+
+ if (pFDD->Type == ELEMENT_TYPE_VALUETYPE
+ || pFDD->Type == ELEMENT_TYPE_CLASS)
+ {
+ dwTmp = (DWORD_PTR) pTLMD->pGCStaticDataStart + pFDD->dwOffset;
+ }
+ else
+ {
+ dwTmp = (DWORD_PTR) pTLMD->pNonGCStaticDataStart + pFDD->dwOffset;
+ }
+
+ *pOutPtr = 0;
+
+ if (pMTD->bIsDynamic)
+ {
+ ExtOut("dynamic thread statics NYI");
+ return;
+ }
+ else
+ {
+ if (pFlags)
+ {
+ BYTE flags;
+ DWORD_PTR pTargetFlags = (DWORD_PTR) pTLMD->pClassData + RidFromToken(pMTD->cl) - 1;
+ move_xp (flags, pTargetFlags);
+
+ *pFlags = flags;
+ }
+
+ *pOutPtr = dwTmp;
+ }
+ return;
+}
+
+void DisplaySharedStatic(ULONG64 dwModuleDomainID, DacpMethodTableData* pMT, DacpFieldDescData *pFD)
+{
+ DacpAppDomainStoreData adsData;
+ if (adsData.Request(g_sos)!=S_OK)
+ {
+ ExtOut("Unable to get AppDomain information\n");
+ }
+
+ ArrayHolder<CLRDATA_ADDRESS> pArray = new CLRDATA_ADDRESS[adsData.DomainCount];
+ if (pArray==NULL)
+ {
+ ReportOOM();
+ return;
+ }
+
+ if (g_sos->GetAppDomainList(adsData.DomainCount,pArray, NULL)!=S_OK)
+ {
+ ExtOut("Unable to get array of AppDomains\n");
+ return;
+ }
+
+#if defined(_TARGET_WIN64_)
+ ExtOut(" >> Domain:Value ");
+#else
+ ExtOut(" >> Domain:Value ");
+#endif
+ // Skip the SystemDomain and SharedDomain
+ for (int i = 0; i < adsData.DomainCount ; i ++)
+ {
+ DacpAppDomainData appdomainData;
+ if (appdomainData.Request(g_sos,pArray[i])!=S_OK)
+ {
+ ExtOut("Unable to get AppDomain %lx\n",pArray[i]);
+ return;
+ }
+
+ DacpDomainLocalModuleData vDomainLocalModule;
+ if (g_sos->GetDomainLocalModuleDataFromAppDomain(appdomainData.AppDomainPtr, (int)dwModuleDomainID, &vDomainLocalModule) != S_OK)
+ {
+ DMLOut(" %s:NotInit ", DMLDomain(pArray[i]));
+ continue;
+ }
+
+ DWORD_PTR dwTmp;
+ BYTE Flags = 0;
+ GetStaticFieldPTR(&dwTmp, &vDomainLocalModule , pMT, pFD, &Flags);
+
+ if ((Flags&1) == 0) {
+ // We have not initialized this yet.
+ DMLOut(" %s:NotInit ", DMLDomain(pArray[i]));
+ continue;
+ }
+ else if (Flags & 2) {
+ // We have not initialized this yet.
+ DMLOut(" %s:FailInit", DMLDomain(pArray[i]));
+ continue;
+ }
+
+ DMLOut(" %s:", DMLDomain(appdomainData.AppDomainPtr));
+ DisplayDataMember(pFD, dwTmp, FALSE);
+ }
+ ExtOut(" <<\n");
+}
+
+void DisplayThreadStatic (DacpModuleData* pModule, DacpMethodTableData* pMT, DacpFieldDescData *pFD, BOOL fIsShared)
+{
+ SIZE_T dwModuleIndex = (SIZE_T)pModule->dwModuleIndex;
+ SIZE_T dwModuleDomainID = (SIZE_T)pModule->dwModuleID;
+
+ DacpThreadStoreData ThreadStore;
+ ThreadStore.Request(g_sos);
+
+ ExtOut(" >> Thread:Value");
+ CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
+ while (CurThread)
+ {
+ DacpThreadData vThread;
+ if (vThread.Request(g_sos, CurThread) != S_OK)
+ {
+ ExtOut(" error getting thread %p, aborting this field\n", SOS_PTR(CurThread));
+ return;
+ }
+
+ if (vThread.osThreadId != 0)
+ {
+ CLRDATA_ADDRESS appDomainAddr = vThread.domain;
+
+ // Get the DLM (we need this to check the ClassInit flags).
+ // It's annoying that we have to issue one request for
+ // domain-neutral modules and domain-specific modules.
+ DacpDomainLocalModuleData vDomainLocalModule;
+ if (fIsShared)
+ {
+ if (g_sos->GetDomainLocalModuleDataFromAppDomain(appDomainAddr, (int)dwModuleDomainID, &vDomainLocalModule) != S_OK)
+ {
+ // Not initialized, go to next thread
+ // and continue looping
+ CurThread = vThread.nextThread;
+ continue;
+ }
+ }
+ else
+ {
+ if (g_sos->GetDomainLocalModuleDataFromModule(pMT->Module, &vDomainLocalModule) != S_OK)
+ {
+ // Not initialized, go to next thread
+ // and continue looping
+ CurThread = vThread.nextThread;
+ continue;
+ }
+ }
+
+ // Get the TLM
+ DacpThreadLocalModuleData vThreadLocalModule;
+ if (g_sos->GetThreadLocalModuleData(CurThread, (int)dwModuleIndex, &vThreadLocalModule) != S_OK)
+ {
+ // Not initialized, go to next thread
+ // and continue looping
+ CurThread = vThread.nextThread;
+ continue;
+ }
+
+ DWORD_PTR dwTmp;
+ BYTE Flags = 0;
+ GetThreadStaticFieldPTR(&dwTmp, &vThreadLocalModule, pMT, pFD, &Flags);
+
+ if ((Flags&4) == 0)
+ {
+ // Not allocated, go to next thread
+ // and continue looping
+ CurThread = vThread.nextThread;
+ continue;
+ }
+
+ Flags = 0;
+ GetDLMFlags(&vDomainLocalModule, pMT, &Flags);
+
+ if ((Flags&1) == 0)
+ {
+ // Not initialized, go to next thread
+ // and continue looping
+ CurThread = vThread.nextThread;
+ continue;
+ }
+
+ ExtOut(" %x:", vThread.osThreadId);
+ DisplayDataMember(pFD, dwTmp, FALSE);
+ }
+
+ // Go to next thread
+ CurThread = vThread.nextThread;
+ }
+ ExtOut(" <<\n");
+}
+
+void DisplayContextStatic (DacpFieldDescData *pFD, size_t offset, BOOL fIsShared)
+{
+ ExtOut("\nDisplay of context static variables is not implemented yet\n");
+ /*
+ int numDomain;
+ DWORD_PTR *domainList = NULL;
+ GetDomainList (domainList, numDomain);
+ ToDestroy des0 ((void**)&domainList);
+ AppDomain vAppDomain;
+ Context vContext;
+
+ ExtOut(" >> Domain:Value");
+ for (int i = 0; i < numDomain; i ++)
+ {
+ DWORD_PTR dwAddr = domainList[i];
+ if (dwAddr == 0) {
+ continue;
+ }
+ vAppDomain.Fill (dwAddr);
+ if (vAppDomain.m_pDefaultContext == 0)
+ continue;
+ dwAddr = (DWORD_PTR)vAppDomain.m_pDefaultContext;
+ vContext.Fill (dwAddr);
+
+ if (fIsShared)
+ dwAddr = (DWORD_PTR)vContext.m_pSharedStaticData;
+ else
+ dwAddr = (DWORD_PTR)vContext.m_pUnsharedStaticData;
+ if (dwAddr == 0)
+ continue;
+ dwAddr += offsetof(STATIC_DATA, dataPtr);
+ dwAddr += offset;
+ if (safemove (dwAddr, dwAddr) == 0)
+ continue;
+ if (dwAddr == 0)
+ // We have not initialized this yet.
+ continue;
+
+ dwAddr += pFD->dwOffset;
+ if (pFD->Type == ELEMENT_TYPE_CLASS
+ || pFD->Type == ELEMENT_TYPE_VALUETYPE)
+ {
+ if (safemove (dwAddr, dwAddr) == 0)
+ continue;
+ }
+ if (dwAddr == 0)
+ // We have not initialized this yet.
+ continue;
+ ExtOut(" %p:", (ULONG64)domainList[i]);
+ DisplayDataMember (pFD, dwAddr, FALSE);
+ }
+ ExtOut(" <<\n");
+ */
+}
+
+const char * ElementTypeName(unsigned type)
+{
+ switch (type) {
+ case ELEMENT_TYPE_PTR:
+ return "PTR";
+ break;
+ case ELEMENT_TYPE_BYREF:
+ return "BYREF";
+ break;
+ case ELEMENT_TYPE_VALUETYPE:
+ return "VALUETYPE";
+ break;
+ case ELEMENT_TYPE_CLASS:
+ return "CLASS";
+ break;
+ case ELEMENT_TYPE_VAR:
+ return "VAR";
+ break;
+ case ELEMENT_TYPE_ARRAY:
+ return "ARRAY";
+ break;
+ case ELEMENT_TYPE_FNPTR:
+ return "FNPTR";
+ break;
+ case ELEMENT_TYPE_SZARRAY:
+ return "SZARRAY";
+ break;
+ case ELEMENT_TYPE_MVAR:
+ return "MVAR";
+ break;
+ default:
+ if ((type >= _countof(CorElementTypeName)) || (CorElementTypeName[type] == NULL))
+ {
+ return "";
+ }
+ return CorElementTypeName[type];
+ break;
+ }
+} // ElementTypeName
+
+const char * ElementTypeNamespace(unsigned type)
+{
+ if ((type >= _countof(CorElementTypeName)) || (CorElementTypeNamespace[type] == NULL))
+ {
+ return "";
+ }
+ return CorElementTypeNamespace[type];
+}
+
+void ComposeName_s(CorElementType Type, __out_ecount(capacity_buffer) LPSTR buffer, size_t capacity_buffer)
+{
+ const char *p = ElementTypeNamespace(Type);
+ if ((p) && (*p != '\0'))
+ {
+ strcpy_s(buffer,capacity_buffer,p);
+ strcat_s(buffer,capacity_buffer,".");
+ strcat_s(buffer,capacity_buffer,ElementTypeName(Type));
+ }
+ else
+ {
+ strcpy_s(buffer,capacity_buffer,ElementTypeName(Type));
+ }
+}
+
+// NOTE: pszName is changed
+// INPUT MAXCHARS RETURN
+// HelloThere 5 ...re
+// HelloThere 8 ...There
+LPWSTR FormatTypeName (__out_ecount (maxChars) LPWSTR pszName, UINT maxChars)
+{
+ UINT iStart = 0;
+ UINT iLen = (int) _wcslen(pszName);
+ if (iLen > maxChars)
+ {
+ iStart = iLen - maxChars;
+ UINT numDots = (maxChars < 3) ? maxChars : 3;
+ for (UINT i=0; i < numDots; i++)
+ pszName[iStart+i] = '.';
+ }
+ return pszName + iStart;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump all fields of a managed object. *
+* dwStartAddr specifies the beginning memory address. *
+* bFirst is used to avoid printing header everytime. *
+* *
+\**********************************************************************/
+void DisplayFields(CLRDATA_ADDRESS cdaMT, DacpMethodTableData *pMTD, DacpMethodTableFieldData *pMTFD, DWORD_PTR dwStartAddr, BOOL bFirst, BOOL bValueClass)
+{
+ static DWORD numInstanceFields = 0;
+ if (bFirst)
+ {
+ ExtOutIndent();
+ ExtOut("%" POINTERSIZE "s %8s %8s %20s %2s %8s %" POINTERSIZE "s %s\n",
+ "MT", "Field", "Offset", "Type", "VT", "Attr", "Value", "Name");
+ numInstanceFields = 0;
+ }
+
+ BOOL fIsShared = pMTD->bIsShared;
+
+ if (pMTD->ParentMethodTable)
+ {
+ DacpMethodTableData vParentMethTable;
+ if (vParentMethTable.Request(g_sos,pMTD->ParentMethodTable) != S_OK)
+ {
+ ExtOut("Invalid parent MethodTable\n");
+ return;
+ }
+
+ DacpMethodTableFieldData vParentMethTableFields;
+ if (vParentMethTableFields.Request(g_sos,pMTD->ParentMethodTable) != S_OK)
+ {
+ ExtOut("Invalid parent EEClass\n");
+ return;
+ }
+
+ DisplayFields(pMTD->ParentMethodTable, &vParentMethTable, &vParentMethTableFields, dwStartAddr, FALSE, bValueClass);
+ }
+
+ DWORD numStaticFields = 0;
+ CLRDATA_ADDRESS dwAddr = pMTFD->FirstField;
+ DacpFieldDescData vFieldDesc;
+
+ // Get the module name
+ DacpModuleData module;
+ if (module.Request(g_sos, pMTD->Module)!=S_OK)
+ return;
+
+ ToRelease<IMetaDataImport> pImport = MDImportForModule(&module);
+
+ while (numInstanceFields < pMTFD->wNumInstanceFields
+ || numStaticFields < pMTFD->wNumStaticFields)
+ {
+ if (IsInterrupt())
+ return;
+
+ ExtOutIndent ();
+
+ if ((vFieldDesc.Request(g_sos, dwAddr)!=S_OK) ||
+ (vFieldDesc.Type >= ELEMENT_TYPE_MAX))
+ {
+ ExtOut("Unable to display fields\n");
+ return;
+ }
+ dwAddr = vFieldDesc.NextField;
+
+ DWORD offset = vFieldDesc.dwOffset;
+ if(!((vFieldDesc.bIsThreadLocal || vFieldDesc.bIsContextLocal || fIsShared) && vFieldDesc.bIsStatic))
+ {
+ if (!bValueClass)
+ {
+ offset += sizeof(BaseObject);
+ }
+ }
+
+ DMLOut("%s %8x %8x ", DMLMethodTable(vFieldDesc.MTOfType),
+ TokenFromRid(vFieldDesc.mb, mdtFieldDef),
+ offset);
+
+ char ElementName[mdNameLen];
+ if ((vFieldDesc.Type == ELEMENT_TYPE_VALUETYPE ||
+ vFieldDesc.Type == ELEMENT_TYPE_CLASS) && vFieldDesc.MTOfType)
+ {
+ NameForMT_s((DWORD_PTR)vFieldDesc.MTOfType, g_mdName, mdNameLen);
+ ExtOut("%20.20S ", FormatTypeName(g_mdName, 20));
+ }
+ else
+ {
+ if (vFieldDesc.Type == ELEMENT_TYPE_CLASS && vFieldDesc.TokenOfType != mdTypeDefNil)
+ {
+ // Get the name from Metadata!!!
+ NameForToken_s(TokenFromRid(vFieldDesc.TokenOfType, mdtTypeDef), pImport, g_mdName, mdNameLen, false);
+ ExtOut("%20.20S ", FormatTypeName(g_mdName, 20));
+ }
+ else
+ {
+ // If ET type from signature is different from fielddesc, then the signature one is more descriptive.
+ // For example, E_T_STRING in field desc will be E_T_CLASS. In minidump's case, we won't have
+ // the method table for it.
+ ComposeName_s(vFieldDesc.Type != vFieldDesc.sigType ? vFieldDesc.sigType : vFieldDesc.Type, ElementName, sizeof(ElementName)/sizeof(ElementName[0]));
+ ExtOut("%20.20s ", ElementName);
+ }
+ }
+
+ ExtOut("%2s ", (IsElementValueType(vFieldDesc.Type)) ? "1" : "0");
+
+ if (vFieldDesc.bIsStatic && (vFieldDesc.bIsThreadLocal || vFieldDesc.bIsContextLocal))
+ {
+ numStaticFields ++;
+ if (fIsShared)
+ ExtOut("%8s %" POINTERSIZE "s", "shared", vFieldDesc.bIsThreadLocal ? "TLstatic" : "CLstatic");
+ else
+ ExtOut("%8s ", vFieldDesc.bIsThreadLocal ? "TLstatic" : "CLstatic");
+
+ NameForToken_s(TokenFromRid(vFieldDesc.mb, mdtFieldDef), pImport, g_mdName, mdNameLen, false);
+ ExtOut(" %S\n", g_mdName);
+
+ if (IsMiniDumpFile())
+ {
+ ExtOut(" <no information>\n");
+ }
+ else
+ {
+ if (vFieldDesc.bIsThreadLocal)
+ {
+ DacpModuleData vModule;
+ if (vModule.Request(g_sos,pMTD->Module) == S_OK)
+ {
+ DisplayThreadStatic(&vModule, pMTD, &vFieldDesc, fIsShared);
+ }
+ }
+ else if (vFieldDesc.bIsContextLocal)
+ {
+ DisplayContextStatic(&vFieldDesc,
+ pMTFD->wContextStaticOffset,
+ fIsShared);
+ }
+ }
+
+ }
+ else if (vFieldDesc.bIsStatic)
+ {
+ numStaticFields ++;
+
+ if (fIsShared)
+ {
+ ExtOut("%8s %" POINTERSIZE "s", "shared", "static");
+
+ NameForToken_s(TokenFromRid(vFieldDesc.mb, mdtFieldDef), pImport, g_mdName, mdNameLen, false);
+ ExtOut(" %S\n", g_mdName);
+
+ if (IsMiniDumpFile())
+ {
+ ExtOut(" <no information>\n");
+ }
+ else
+ {
+ DacpModuleData vModule;
+ if (vModule.Request(g_sos,pMTD->Module) == S_OK)
+ {
+ DisplaySharedStatic(vModule.dwModuleID, pMTD, &vFieldDesc);
+ }
+ }
+ }
+ else
+ {
+ ExtOut("%8s ", "static");
+
+ DacpDomainLocalModuleData vDomainLocalModule;
+
+ // The MethodTable isn't shared, so the module must not be loaded domain neutral. We can
+ // get the specific DomainLocalModule instance without needing to know the AppDomain in advance.
+ if (g_sos->GetDomainLocalModuleDataFromModule(pMTD->Module, &vDomainLocalModule) != S_OK)
+ {
+ ExtOut(" <no information>\n");
+ }
+ else
+ {
+ DWORD_PTR dwTmp;
+ GetStaticFieldPTR(&dwTmp, &vDomainLocalModule, pMTD, &vFieldDesc);
+ DisplayDataMember(&vFieldDesc, dwTmp);
+
+ NameForToken_s(TokenFromRid(vFieldDesc.mb, mdtFieldDef), pImport, g_mdName, mdNameLen, false);
+ ExtOut(" %S\n", g_mdName);
+ }
+ }
+ }
+ else
+ {
+ numInstanceFields ++;
+
+ ExtOut("%8s ", "instance");
+
+ if (dwStartAddr > 0)
+ {
+ DWORD_PTR dwTmp = dwStartAddr + vFieldDesc.dwOffset + (bValueClass ? 0 : sizeof(BaseObject));
+ DisplayDataMember(&vFieldDesc, dwTmp);
+ }
+ else
+ {
+ ExtOut(" %8s", " ");
+ }
+
+
+ NameForToken_s(TokenFromRid(vFieldDesc.mb, mdtFieldDef), pImport, g_mdName, mdNameLen, false);
+ ExtOut(" %S\n", g_mdName);
+ }
+
+ }
+
+ return;
+}
+
+// Return value: -1 = error,
+// 0 = field not found,
+// > 0 = offset to field from objAddr
+int GetObjFieldOffset(CLRDATA_ADDRESS cdaObj, __in_z LPCWSTR wszFieldName, BOOL bFirst)
+{
+ TADDR mt = NULL;
+ if FAILED(GetMTOfObject(TO_TADDR(cdaObj), &mt))
+ return -1;
+
+ return GetObjFieldOffset(cdaObj, TO_CDADDR(mt), wszFieldName, bFirst);
+}
+
+// Return value: -1 = error,
+// 0 = field not found,
+// > 0 = offset to field from objAddr
+int GetObjFieldOffset(CLRDATA_ADDRESS cdaObj, CLRDATA_ADDRESS cdaMT, __in_z LPCWSTR wszFieldName,
+ BOOL bFirst/*=TRUE*/)
+{
+
+#define EXITPOINT(EXPR) do { if(!(EXPR)) { return -1; } } while (0)
+
+ DacpObjectData objData;
+ DacpMethodTableData dmtd;
+ DacpMethodTableFieldData vMethodTableFields;
+ DacpFieldDescData vFieldDesc;
+ DacpModuleData module;
+ static DWORD numInstanceFields = 0; // Static due to recursion visiting parents
+
+ if (bFirst)
+ {
+ numInstanceFields = 0;
+ }
+
+ EXITPOINT(objData.Request(g_sos, cdaObj) == S_OK);
+ EXITPOINT(dmtd.Request(g_sos, cdaMT) == S_OK);
+
+ if (dmtd.ParentMethodTable)
+ {
+ DWORD retVal = GetObjFieldOffset (cdaObj, dmtd.ParentMethodTable,
+ wszFieldName, FALSE);
+ if (retVal != 0)
+ {
+ // return in case of error or success.
+ // Fall through for field-not-found.
+ return retVal;
+ }
+ }
+
+ EXITPOINT (vMethodTableFields.Request(g_sos,cdaMT) == S_OK);
+ EXITPOINT (module.Request(g_sos,dmtd.Module) == S_OK);
+
+ CLRDATA_ADDRESS dwAddr = vMethodTableFields.FirstField;
+ ToRelease<IMetaDataImport> pImport = MDImportForModule(&module);
+
+ while (numInstanceFields < vMethodTableFields.wNumInstanceFields)
+ {
+ EXITPOINT (vFieldDesc.Request(g_sos, dwAddr) == S_OK);
+
+ if (!vFieldDesc.bIsStatic)
+ {
+ DWORD offset = vFieldDesc.dwOffset + sizeof(BaseObject);
+ NameForToken_s (TokenFromRid(vFieldDesc.mb, mdtFieldDef), pImport, g_mdName, mdNameLen, false);
+ if (_wcscmp (wszFieldName, g_mdName) == 0)
+ {
+ return offset;
+ }
+ numInstanceFields ++;
+ }
+
+ dwAddr = vFieldDesc.NextField;
+ }
+
+ // Field name not found...
+ return 0;
+
+#undef EXITPOINT
+}
+
+// Returns an AppDomain address if AssemblyPtr is loaded into that domain only. Otherwise
+// returns NULL
+CLRDATA_ADDRESS IsInOneDomainOnly(CLRDATA_ADDRESS AssemblyPtr)
+{
+ CLRDATA_ADDRESS appDomain = NULL;
+
+ DacpAppDomainStoreData adstore;
+ if (adstore.Request(g_sos) != S_OK)
+ {
+ ExtOut("Unable to get appdomain store\n");
+ return NULL;
+ }
+
+ size_t AllocSize;
+ if (!ClrSafeInt<size_t>::multiply(sizeof(CLRDATA_ADDRESS), adstore.DomainCount, AllocSize))
+ {
+ ReportOOM();
+ return NULL;
+ }
+
+ ArrayHolder<CLRDATA_ADDRESS> pArray = new CLRDATA_ADDRESS[adstore.DomainCount];
+ if (pArray==NULL)
+ {
+ ReportOOM();
+ return NULL;
+ }
+
+ if (g_sos->GetAppDomainList(adstore.DomainCount, pArray, NULL)!=S_OK)
+ {
+ ExtOut ("Failed to get appdomain list\n");
+ return NULL;
+ }
+
+ for (int i = 0; i < adstore.DomainCount; i++)
+ {
+ if (IsInterrupt())
+ return NULL;
+
+ DacpAppDomainData dadd;
+ if (dadd.Request(g_sos, pArray[i]) != S_OK)
+ {
+ ExtOut ("Unable to get AppDomain %p\n", SOS_PTR(pArray[i]));
+ return NULL;
+ }
+
+ if (dadd.AssemblyCount)
+ {
+ size_t AssemblyAllocSize;
+ if (!ClrSafeInt<size_t>::multiply(sizeof(CLRDATA_ADDRESS), dadd.AssemblyCount, AssemblyAllocSize))
+ {
+ ReportOOM();
+ return NULL;
+ }
+
+ ArrayHolder<CLRDATA_ADDRESS> pAsmArray = new CLRDATA_ADDRESS[dadd.AssemblyCount];
+ if (pAsmArray==NULL)
+ {
+ ReportOOM();
+ return NULL;
+ }
+
+ if (g_sos->GetAssemblyList(dadd.AppDomainPtr,dadd.AssemblyCount,pAsmArray, NULL)!=S_OK)
+ {
+ ExtOut("Unable to get array of Assemblies\n");
+ return NULL;
+ }
+
+ for (LONG n = 0; n < dadd.AssemblyCount; n ++)
+ {
+ if (IsInterrupt())
+ return NULL;
+
+ if (AssemblyPtr == pAsmArray[n])
+ {
+ if (appDomain != NULL)
+ {
+ // We have found more than one AppDomain that loaded this
+ // assembly, we must return NULL.
+ return NULL;
+ }
+ appDomain = dadd.AppDomainPtr;
+ }
+ }
+ }
+ }
+
+
+ return appDomain;
+}
+
+CLRDATA_ADDRESS GetAppDomainForMT(CLRDATA_ADDRESS mtPtr)
+{
+ DacpMethodTableData mt;
+ if (mt.Request(g_sos, mtPtr) != S_OK)
+ {
+ return NULL;
+ }
+
+ DacpModuleData module;
+ if (module.Request(g_sos, mt.Module) != S_OK)
+ {
+ return NULL;
+ }
+
+ DacpAssemblyData assembly;
+ if (assembly.Request(g_sos, module.Assembly) != S_OK)
+ {
+ return NULL;
+ }
+
+ DacpAppDomainStoreData adstore;
+ if (adstore.Request(g_sos) != S_OK)
+ {
+ return NULL;
+ }
+
+ return (assembly.ParentDomain == adstore.sharedDomain) ?
+ IsInOneDomainOnly(assembly.AssemblyPtr) :
+ assembly.ParentDomain;
+}
+
+CLRDATA_ADDRESS GetAppDomain(CLRDATA_ADDRESS objPtr)
+{
+ CLRDATA_ADDRESS appDomain = NULL;
+
+ DacpObjectData objData;
+ if (objData.Request(g_sos,objPtr) != S_OK)
+ {
+ return NULL;
+ }
+
+ // First check eeclass->module->assembly->domain.
+ // Then check the object flags word
+ // finally, search threads for a reference to the object, and look at the thread context.
+
+ DacpMethodTableData mt;
+ if (mt.Request(g_sos,objData.MethodTable) != S_OK)
+ {
+ return NULL;
+ }
+
+ DacpModuleData module;
+ if (module.Request(g_sos,mt.Module) != S_OK)
+ {
+ return NULL;
+ }
+
+ DacpAssemblyData assembly;
+ if (assembly.Request(g_sos,module.Assembly) != S_OK)
+ {
+ return NULL;
+ }
+
+ DacpAppDomainStoreData adstore;
+ if (adstore.Request(g_sos) != S_OK)
+ {
+ return NULL;
+ }
+
+ if (assembly.ParentDomain == adstore.sharedDomain)
+ {
+ sos::Object obj(TO_TADDR(objPtr));
+ ULONG value = 0;
+ if (!obj.TryGetHeader(value))
+ {
+ return NULL;
+ }
+
+ DWORD adIndex = (value >> SBLK_APPDOMAIN_SHIFT) & SBLK_MASK_APPDOMAININDEX;
+ if ( ((value & BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX) != 0) || adIndex==0)
+ {
+ // No AppDomainID information. We'll make use of a heuristic.
+ // If the assembly is in the shared domain, we can report it as
+ // being in domain X if the only other domain that has the assembly
+ // loaded is domain X.
+ appDomain = IsInOneDomainOnly(assembly.AssemblyPtr);
+ if (appDomain == NULL && ((value & BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX) != 0))
+ {
+ if ((value & BIT_SBLK_IS_HASHCODE) == 0)
+ {
+ UINT index = value & MASK_SYNCBLOCKINDEX;
+ // We have a syncblock, the appdomain ID may be in there.
+ DacpSyncBlockData syncBlockData;
+ if (syncBlockData.Request(g_sos,index) == S_OK)
+ {
+ appDomain = syncBlockData.appDomainPtr;
+ }
+ }
+ }
+ }
+ else if ((value & BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX) == 0)
+ {
+ size_t AllocSize;
+ if (!ClrSafeInt<size_t>::multiply(sizeof(CLRDATA_ADDRESS), adstore.DomainCount, AllocSize))
+ {
+ return NULL;
+ }
+ // we know we have a non-zero adIndex. Find the appdomain.
+ ArrayHolder<CLRDATA_ADDRESS> pArray = new CLRDATA_ADDRESS[adstore.DomainCount];
+ if (pArray==NULL)
+ {
+ return NULL;
+ }
+
+ if (g_sos->GetAppDomainList(adstore.DomainCount, pArray, NULL)!=S_OK)
+ {
+ return NULL;
+ }
+
+ for (int i = 0; i < adstore.DomainCount; i++)
+ {
+ DacpAppDomainData dadd;
+ if (dadd.Request(g_sos, pArray[i]) != S_OK)
+ {
+ return NULL;
+ }
+ if (dadd.dwId == adIndex)
+ {
+ appDomain = pArray[i];
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ appDomain = assembly.ParentDomain;
+ }
+
+ return appDomain;
+}
+
+HRESULT FileNameForModule (DWORD_PTR pModuleAddr, __out_ecount (MAX_LONGPATH) WCHAR *fileName)
+{
+ DacpModuleData ModuleData;
+ fileName[0] = L'\0';
+
+ HRESULT hr = ModuleData.Request(g_sos, TO_CDADDR(pModuleAddr));
+ if (SUCCEEDED(hr))
+ {
+ hr = FileNameForModule(&ModuleData,fileName);
+ }
+
+ return hr;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to find the file name given a Module. *
+* *
+\**********************************************************************/
+// fileName should be at least MAX_LONGPATH
+HRESULT FileNameForModule (DacpModuleData *pModule, __out_ecount (MAX_LONGPATH) WCHAR *fileName)
+{
+ fileName[0] = L'\0';
+
+ HRESULT hr = S_OK;
+ CLRDATA_ADDRESS dwAddr = pModule->File;
+ if (dwAddr == 0)
+ {
+ // TODO: We have dynamic module
+ return E_NOTIMPL;
+ }
+
+ CLRDATA_ADDRESS base = 0;
+ hr = g_sos->GetPEFileBase(dwAddr, &base);
+ if (SUCCEEDED(hr))
+ {
+ hr = g_sos->GetPEFileName(dwAddr, MAX_LONGPATH, fileName, NULL);
+ if (SUCCEEDED(hr))
+ {
+ if (fileName[0] != W('\0'))
+ return hr; // done
+ }
+#ifndef FEATURE_PAL
+ // Try the base *
+ if (base)
+ {
+ hr = DllsName((ULONG_PTR) base, fileName);
+ }
+#endif // !FEATURE_PAL
+ }
+
+ // If we got here, either DllsName worked, or we couldn't find a name
+ return hr;
+}
+
+void AssemblyInfo(DacpAssemblyData *pAssembly)
+{
+ ExtOut("ClassLoader: %p\n", SOS_PTR(pAssembly->ClassLoader));
+ if ((ULONG64)pAssembly->AssemblySecDesc != NULL)
+ ExtOut("SecurityDescriptor: %p\n", SOS_PTR(pAssembly->AssemblySecDesc));
+ ExtOut(" Module Name\n");
+
+ ArrayHolder<CLRDATA_ADDRESS> Modules = new CLRDATA_ADDRESS[pAssembly->ModuleCount];
+ if (Modules == NULL
+ || g_sos->GetAssemblyModuleList(pAssembly->AssemblyPtr, pAssembly->ModuleCount, Modules, NULL) != S_OK)
+ {
+ ReportOOM();
+ return;
+ }
+
+ for (UINT n=0;n<pAssembly->ModuleCount;n++)
+ {
+ if (IsInterrupt())
+ {
+ return;
+ }
+
+ CLRDATA_ADDRESS ModuleAddr = Modules[n];
+ DMLOut("%s " WIN86_8SPACES, DMLModule(ModuleAddr));
+ DacpModuleData moduleData;
+ if (moduleData.Request(g_sos,ModuleAddr)==S_OK)
+ {
+ WCHAR fileName[MAX_LONGPATH];
+ FileNameForModule (&moduleData, fileName);
+ if (fileName[0])
+ {
+ ExtOut("%S\n", fileName);
+ }
+ else
+ {
+ ExtOut("%S\n", (moduleData.bIsReflection) ? W("Dynamic Module") : W("Unknown Module"));
+ }
+ }
+ }
+}
+
+const char *GetStageText(DacpAppDomainDataStage stage)
+{
+ switch(stage)
+ {
+ case STAGE_CREATING:
+ return "CREATING";
+ case STAGE_READYFORMANAGEDCODE:
+ return "READYFORMANAGEDCODE";
+ case STAGE_ACTIVE:
+ return "ACTIVE";
+ case STAGE_OPEN:
+ return "OPEN";
+ case STAGE_UNLOAD_REQUESTED:
+ return "UNLOAD_REQUESTED";
+ case STAGE_EXITING:
+ return "EXITING";
+ case STAGE_EXITED:
+ return "EXITED";
+ case STAGE_FINALIZING:
+ return "FINALIZING";
+ case STAGE_FINALIZED:
+ return "FINALIZED";
+ case STAGE_HANDLETABLE_NOACCESS:
+ return "HANDLETABLE_NOACCESS";
+ case STAGE_CLEARED:
+ return "CLEARED";
+ case STAGE_COLLECTED:
+ return "COLLECTED";
+ case STAGE_CLOSED:
+ return "CLOSED";
+ }
+ return "UNKNOWN";
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the contents of a domain. *
+* *
+\**********************************************************************/
+void DomainInfo (DacpAppDomainData *pDomain)
+{
+ ExtOut("LowFrequencyHeap: %p\n", SOS_PTR(pDomain->pLowFrequencyHeap));
+ ExtOut("HighFrequencyHeap: %p\n", SOS_PTR(pDomain->pHighFrequencyHeap));
+ ExtOut("StubHeap: %p\n", SOS_PTR(pDomain->pStubHeap));
+ ExtOut("Stage: %s\n", GetStageText(pDomain->appDomainStage));
+ if ((ULONG64)pDomain->AppSecDesc != NULL)
+ ExtOut("SecurityDescriptor: %p\n", SOS_PTR(pDomain->AppSecDesc));
+ ExtOut("Name: ");
+
+ if (g_sos->GetAppDomainName(pDomain->AppDomainPtr, mdNameLen, g_mdName, NULL)!=S_OK)
+ {
+ ExtOut("Error getting AppDomain friendly name\n");
+ }
+ else
+ {
+ ExtOut("%S\n", (g_mdName[0] != L'\0') ? g_mdName : W("None"));
+ }
+
+ if (pDomain->AssemblyCount == 0)
+ return;
+
+ ArrayHolder<CLRDATA_ADDRESS> pArray = new CLRDATA_ADDRESS[pDomain->AssemblyCount];
+ if (pArray==NULL)
+ {
+ ReportOOM();
+ return;
+ }
+
+ if (g_sos->GetAssemblyList(pDomain->AppDomainPtr,pDomain->AssemblyCount,pArray, NULL)!=S_OK)
+ {
+ ExtOut("Unable to get array of Assemblies\n");
+ return;
+ }
+
+ LONG n;
+ // Assembly vAssembly;
+ for (n = 0; n < pDomain->AssemblyCount; n ++)
+ {
+ if (IsInterrupt())
+ return;
+
+ if (n != 0)
+ ExtOut("\n");
+
+ DMLOut("Assembly: %s", DMLAssembly(pArray[n]));
+ DacpAssemblyData assemblyData;
+ if (assemblyData.Request(g_sos, pArray[n], pDomain->AppDomainPtr) == S_OK)
+ {
+ if (assemblyData.isDynamic)
+ ExtOut(" (Dynamic)");
+
+ ExtOut(" [");
+ if (g_sos->GetAssemblyName(pArray[n], mdNameLen, g_mdName, NULL) == S_OK)
+ ExtOut("%S", g_mdName);
+ ExtOut("]\n");
+
+ AssemblyInfo(&assemblyData);
+ }
+ }
+
+ ExtOut("\n");
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to find the name of a MethodDesc using *
+* metadata API. *
+* *
+\**********************************************************************/
+BOOL NameForMD_s (DWORD_PTR pMD, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName)
+{
+ mdName[0] = L'\0';
+ CLRDATA_ADDRESS StartAddr = TO_CDADDR(pMD);
+ DacpMethodDescData MethodDescData;
+
+ // don't need to check for minidump file as all commands are seals
+ // We also do not have EEJitManager to validate anyway.
+ //
+ if (!IsMiniDumpFile() && MethodDescData.Request(g_sos,StartAddr) != S_OK)
+ {
+ ExtOut("%p is not a MethodDesc\n", SOS_PTR(StartAddr));
+ return FALSE;
+ }
+
+ if (g_sos->GetMethodDescName(StartAddr, mdNameLen, mdName, NULL) != S_OK)
+ {
+ wcscpy_s(mdName, capacity_mdName, W("UNKNOWN"));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to find the name of a MethodTable using *
+* metadata API. *
+* *
+\**********************************************************************/
+BOOL NameForMT_s(DWORD_PTR MTAddr, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName)
+{
+ HRESULT hr = g_sos->GetMethodTableName(TO_CDADDR(MTAddr), (ULONG32)capacity_mdName, mdName, NULL);
+ return SUCCEEDED(hr);
+}
+
+WCHAR *CreateMethodTableName(TADDR mt, TADDR cmt)
+{
+ bool array = false;
+ WCHAR *res = NULL;
+
+ if (mt == sos::MethodTable::GetFreeMT())
+ {
+ res = new WCHAR[5];
+ wcscpy_s(res, 5, W("Free"));
+ return res;
+ }
+
+ if (mt == sos::MethodTable::GetArrayMT() && cmt != NULL)
+ {
+ mt = cmt;
+ array = true;
+ }
+
+ unsigned int needed = 0;
+ HRESULT hr = g_sos->GetMethodTableName(mt, 0, NULL, &needed);
+
+ // If failed, we will return null.
+ if (SUCCEEDED(hr))
+ {
+ // +2 for [], if we need it.
+ res = new WCHAR[needed+2];
+ hr = g_sos->GetMethodTableName(mt, needed, res, NULL);
+
+ if (FAILED(hr))
+ {
+ delete [] res;
+ res = NULL;
+ }
+ else if (array)
+ {
+ res[needed-1] = '[';
+ res[needed] = ']';
+ res[needed+1] = 0;
+ }
+ }
+
+ return res;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Return TRUE if str2 is a substring of str1 and str1 and str2 *
+* share the same file path.
+* *
+\**********************************************************************/
+BOOL IsSameModuleName (const char *str1, const char *str2)
+{
+ if (strlen (str1) < strlen (str2))
+ return FALSE;
+ const char *ptr1 = str1 + strlen(str1)-1;
+ const char *ptr2 = str2 + strlen(str2)-1;
+ while (ptr2 >= str2)
+ {
+#ifndef FEATURE_PAL
+ if (tolower(*ptr1) != tolower(*ptr2))
+#else
+ if (*ptr1 != *ptr2)
+#endif
+ {
+ return FALSE;
+ }
+ ptr2--;
+ ptr1--;
+ }
+ if (ptr1 >= str1 && *ptr1 != DIRECTORY_SEPARATOR_CHAR_A && *ptr1 != ':')
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Return TRUE if moduleAddr is the address of a module. *
+* *
+\**********************************************************************/
+BOOL IsModule (DWORD_PTR moduleAddr)
+{
+ DacpModuleData module;
+ return (module.Request(g_sos, TO_CDADDR(moduleAddr))==S_OK);
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Return TRUE if value is the address of a MethodTable. *
+* We verify that MethodTable and EEClass are right.
+* *
+\**********************************************************************/
+BOOL IsMethodTable (DWORD_PTR value)
+{
+ DacpMethodTableData mtabledata;
+ if (mtabledata.Request(g_sos, TO_CDADDR(value))!=S_OK)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Return TRUE if value is the address of a MethodDesc. *
+* We verify that MethodTable and EEClass are right.
+* *
+\**********************************************************************/
+BOOL IsMethodDesc (DWORD_PTR value)
+{
+ // Just by retrieving one successfully from the DAC, we know we have a MethodDesc.
+ DacpMethodDescData MethodDescData;
+ if (MethodDescData.Request(g_sos, TO_CDADDR(value)) != S_OK)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+DacpUsefulGlobalsData g_special_usefulGlobals;
+
+BOOL IsObjectArray (DacpObjectData *pData)
+{
+ if (pData->ObjectType == OBJ_ARRAY)
+ return g_special_usefulGlobals.ArrayMethodTable == pData->MethodTable;
+
+ return FALSE;
+}
+
+BOOL IsObjectArray (DWORD_PTR obj)
+{
+ DWORD_PTR mtAddr = NULL;
+ if (SUCCEEDED(GetMTOfObject(obj, &mtAddr)))
+ return TO_TADDR(g_special_usefulGlobals.ArrayMethodTable) == mtAddr;
+
+ return FALSE;
+}
+
+BOOL IsStringObject (size_t obj)
+{
+ DWORD_PTR mtAddr = NULL;
+
+ if (SUCCEEDED(GetMTOfObject(obj, &mtAddr)))
+ return TO_TADDR(g_special_usefulGlobals.StringMethodTable) == mtAddr;
+
+ return FALSE;
+}
+
+void DumpStackObjectsOutput(const char *location, DWORD_PTR objAddr, BOOL verifyFields)
+{
+ // rule out pointers that are outside of the gc heap.
+ if (g_snapshot.GetHeap(objAddr) == NULL)
+ return;
+
+ DacpObjectData objectData;
+ if (objectData.Request(g_sos, TO_CDADDR(objAddr)) != S_OK)
+ return;
+
+ if (sos::IsObject(objAddr, verifyFields != FALSE)
+ && !sos::MethodTable::IsFreeMT(TO_TADDR(objectData.MethodTable)))
+ {
+ DMLOut("%-" POINTERSIZE "s %s ", location, DMLObject(objAddr));
+ if (g_sos->GetObjectClassName(TO_CDADDR(objAddr), mdNameLen, g_mdName, NULL)==S_OK)
+ {
+ ExtOut("%S", g_mdName);
+
+ if (IsStringObject(objAddr))
+ {
+ ExtOut(" ");
+ StringObjectContent(objAddr, FALSE, 40);
+ }
+ else if (IsObjectArray(objAddr) &&
+ (g_sos->GetMethodTableName(objectData.ElementTypeHandle, mdNameLen, g_mdName, NULL) == S_OK))
+ {
+ ExtOut(" ");
+ ExtOut("(%S[])", g_mdName);
+ }
+ }
+ else
+ {
+ ExtOut("<unknown type>");
+ }
+ ExtOut("\n");
+ }
+}
+
+void DumpStackObjectsOutput(DWORD_PTR ptr, DWORD_PTR objAddr, BOOL verifyFields)
+{
+ char location[64];
+ sprintf_s(location, 64, "%p", (DWORD_PTR *)ptr);
+
+ DumpStackObjectsOutput(location, objAddr, verifyFields);
+}
+
+void DumpStackObjectsInternal(size_t StackTop, size_t StackBottom, BOOL verifyFields)
+{
+ for (DWORD_PTR ptr = StackTop; ptr <= StackBottom; ptr += sizeof(DWORD_PTR))
+ {
+ if (IsInterrupt())
+ return;
+
+ DWORD_PTR objAddr;
+ move_xp(objAddr, ptr);
+
+ DumpStackObjectsOutput(ptr, objAddr, verifyFields);
+ }
+}
+
+void DumpRegObjectHelper(const char *regName, BOOL verifyFields)
+{
+ DWORD_PTR reg;
+#ifdef FEATURE_PAL
+ if (FAILED(g_ExtRegisters->GetValueByName(regName, &reg)))
+ return;
+#else
+ DEBUG_VALUE value;
+ ULONG IREG;
+ if (FAILED(g_ExtRegisters->GetIndexByName(regName, &IREG)) ||
+ FAILED(g_ExtRegisters->GetValue(IREG, &value)))
+ return;
+
+#if defined(SOS_TARGET_X86) || defined(SOS_TARGET_ARM)
+ reg = (DWORD_PTR) value.I32;
+#elif defined(SOS_TARGET_AMD64) || defined(SOS_TARGET_ARM64)
+ reg = (DWORD_PTR) value.I64;
+#else
+#error Unsupported target
+#endif
+#endif // FEATURE_PAL
+
+ DumpStackObjectsOutput(regName, reg, verifyFields);
+}
+
+void DumpStackObjectsHelper (
+ TADDR StackTop,
+ TADDR StackBottom,
+ BOOL verifyFields)
+{
+ ExtOut(g_targetMachine->GetDumpStackObjectsHeading());
+
+ LPCSTR* regs;
+ unsigned int cnt;
+ g_targetMachine->GetGCRegisters(&regs, &cnt);
+
+ for (size_t i = 0; i < cnt; ++i)
+ DumpRegObjectHelper(regs[i], verifyFields);
+
+ // Make certain StackTop is dword aligned:
+ DumpStackObjectsInternal(StackTop & ~ALIGNCONST, StackBottom, verifyFields);
+}
+
+void AddToModuleList(DWORD_PTR * &moduleList, int &numModule, int &maxList,
+ DWORD_PTR dwModuleAddr)
+{
+ int i;
+ for (i = 0; i < numModule; i ++)
+ {
+ if (moduleList[i] == dwModuleAddr)
+ break;
+ }
+ if (i == numModule)
+ {
+ moduleList[numModule] = dwModuleAddr;
+ numModule ++;
+ if (numModule == maxList)
+ {
+ int listLength = 0;
+ if (!ClrSafeInt<int>::multiply(maxList, 2, listLength))
+ {
+ ExtOut("<integer overflow>\n");
+ numModule = 0;
+ ControlC = 1;
+ return;
+ }
+ DWORD_PTR *list = new DWORD_PTR [listLength];
+
+ if (list == NULL)
+ {
+ numModule = 0;
+ ControlC = 1;
+ return;
+ }
+ memcpy (list, moduleList, maxList * sizeof(PVOID));
+ delete[] moduleList;
+ moduleList = list;
+ maxList *= 2;
+ }
+ }
+}
+
+BOOL IsFusionLoadedModule (LPCSTR fusionName, LPCSTR mName)
+{
+ // The fusion name will be in this format:
+ // <module name>, Version=<version>, Culture=<culture>, PublicKeyToken=<token>
+ // If fusionName up to the comma matches mName (case insensitive),
+ // we consider that a match was found.
+ LPCSTR commaPos = strchr (fusionName, ',');
+ if (commaPos)
+ {
+ // verify that fusionName and mName match up to a comma.
+ while (*fusionName != ',')
+ {
+ if (*mName == '\0')
+ {
+ return FALSE;
+ }
+
+#ifndef FEATURE_PAL
+ if (tolower(*fusionName) != tolower(*mName))
+#else
+ if (*fusionName != *mName)
+#endif
+ {
+ return FALSE;
+ }
+ fusionName++;
+ mName++;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL DebuggerModuleNamesMatch (CLRDATA_ADDRESS PEFileAddr, ___in __in_z LPSTR mName)
+{
+ // Another way to see if a module is the same is
+ // to accept that mName may be the debugger's name for
+ // a loaded module. We can get the debugger's name for
+ // the module we are looking at right now, and compare
+ // it with mName, if they match exactly, we can add
+ // the module to the list.
+ if (PEFileAddr)
+ {
+ CLRDATA_ADDRESS pebase = 0;
+ if (g_sos->GetPEFileBase(PEFileAddr, &pebase) == S_OK)
+ {
+ if (pebase)
+ {
+ ULONG Index;
+ ULONG64 base;
+ if (g_ExtSymbols->GetModuleByOffset(pebase, 0, &Index, &base) == S_OK)
+ {
+ CHAR ModuleName[MAX_LONGPATH+1];
+
+ if (g_ExtSymbols->GetModuleNames(Index, base, NULL, 0, NULL, ModuleName,
+ MAX_LONGPATH, NULL, NULL, 0, NULL) == S_OK)
+ {
+ if (_stricmp (ModuleName, mName) == 0)
+ {
+ return TRUE;
+ }
+ }
+ }
+ }
+ }
+ }
+ return FALSE;
+}
+
+DWORD_PTR *ModuleFromName(__in_opt LPSTR mName, int *numModule)
+{
+ if (numModule == NULL)
+ return NULL;
+
+ DWORD_PTR *moduleList = NULL;
+ *numModule = 0;
+
+ DacpAppDomainStoreData adsData;
+ if (adsData.Request(g_sos)!=S_OK)
+ return NULL;
+
+ ArrayHolder<CLRDATA_ADDRESS> pAssemblyArray = NULL;
+ ArrayHolder<CLRDATA_ADDRESS> pModules = NULL;
+ int arrayLength = 0;
+ if (!ClrSafeInt<int>::addition(adsData.DomainCount, 2, arrayLength))
+ {
+ ExtOut("<integer overflow>\n");
+ return NULL;
+ }
+ ArrayHolder<CLRDATA_ADDRESS> pArray = new CLRDATA_ADDRESS[arrayLength];
+
+ if (pArray==NULL)
+ {
+ ReportOOM();
+ return NULL;
+ }
+
+ pArray[0] = adsData.systemDomain;
+ pArray[1] = adsData.sharedDomain;
+ if (g_sos->GetAppDomainList(adsData.DomainCount, pArray.GetPtr()+2, NULL)!=S_OK)
+ {
+ ExtOut("Unable to get array of AppDomains\n");
+ return NULL;
+ }
+
+ // List all domain
+ size_t AllocSize;
+ int maxList = arrayLength; // account for system and shared domains
+ if (maxList <= 0 || !ClrSafeInt<size_t>::multiply(maxList, sizeof(PVOID), AllocSize))
+ {
+ ExtOut("Integer overflow error.\n");
+ return NULL;
+ }
+
+ moduleList = new DWORD_PTR[maxList];
+ if (moduleList == NULL)
+ {
+ ReportOOM();
+ return NULL;
+ }
+
+ WCHAR StringData[MAX_LONGPATH];
+ char fileName[sizeof(StringData)/2];
+
+ // Search all domains to find a module
+ for (int n = 0; n < adsData.DomainCount+2; n++)
+ {
+ if (IsInterrupt())
+ {
+ ExtOut("<interrupted>\n");
+ goto Failure;
+ }
+
+ DacpAppDomainData appDomain;
+ if (FAILED(appDomain.Request(g_sos,pArray[n])))
+ {
+ // Don't print a failure message here, there is a very normal case when checking
+ // for modules after clr is loaded but before any AppDomains or assemblies are created
+ // for example:
+ // >sxe ld:clr
+ // >g
+ // ...
+ // ModLoad: clr.dll
+ // >!bpmd Foo.dll Foo.Bar
+
+ // we will correctly give the answer that whatever module you were looking for, it isn't loaded yet
+ goto Failure;
+ }
+
+ if (appDomain.AssemblyCount)
+ {
+ pAssemblyArray = new CLRDATA_ADDRESS[appDomain.AssemblyCount];
+ if (pAssemblyArray==NULL)
+ {
+ ReportOOM();
+ goto Failure;
+ }
+
+ if (FAILED(g_sos->GetAssemblyList(appDomain.AppDomainPtr, appDomain.AssemblyCount, pAssemblyArray, NULL)))
+ {
+ ExtOut("Unable to get array of Assemblies for the given AppDomain..\n");
+ goto Failure;
+ }
+
+ for (int nAssem = 0; nAssem < appDomain.AssemblyCount; nAssem ++)
+ {
+ if (IsInterrupt())
+ {
+ ExtOut("<interrupted>\n");
+ goto Failure;
+ }
+
+ DacpAssemblyData assemblyData;
+ if (FAILED(assemblyData.Request(g_sos, pAssemblyArray[nAssem])))
+ {
+ ExtOut("Failed to request assembly.\n");
+ goto Failure;
+ }
+
+ pModules = new CLRDATA_ADDRESS[assemblyData.ModuleCount];
+ if (FAILED(g_sos->GetAssemblyModuleList(assemblyData.AssemblyPtr, assemblyData.ModuleCount, pModules, NULL)))
+ {
+ ExtOut("Failed to get the modules for the given assembly.\n");
+ goto Failure;
+ }
+
+ for (UINT nModule = 0; nModule < assemblyData.ModuleCount; nModule++)
+ {
+ if (IsInterrupt())
+ {
+ ExtOut("<interrupted>\n");
+ goto Failure;
+ }
+
+ CLRDATA_ADDRESS ModuleAddr = pModules[nModule];
+ DacpModuleData ModuleData;
+ if (FAILED(ModuleData.Request(g_sos,ModuleAddr)))
+ {
+ ExtOut("Failed to request Module data from assembly.\n");
+ goto Failure;
+ }
+
+ FileNameForModule ((DWORD_PTR)ModuleAddr, StringData);
+ int m;
+ for (m = 0; StringData[m] != L'\0'; m++)
+ {
+ fileName[m] = (char)StringData[m];
+ }
+ fileName[m] = '\0';
+
+ if ((mName == NULL) ||
+ IsSameModuleName(fileName, mName) ||
+ DebuggerModuleNamesMatch(ModuleData.File, mName) ||
+ IsFusionLoadedModule(fileName, mName))
+ {
+ AddToModuleList(moduleList, *numModule, maxList, (DWORD_PTR)ModuleAddr);
+ }
+ }
+
+ pModules = NULL;
+ }
+ pAssemblyArray = NULL;
+ }
+ }
+
+ return moduleList;
+
+ // We do not want to return a half-constructed list. Instead, we return NULL on a failure.
+Failure:
+ delete [] moduleList;
+ return NULL;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Find the EE data given a name. *
+* *
+\**********************************************************************/
+void GetInfoFromName(DWORD_PTR ModulePtr, const char* name)
+{
+ ToRelease<IMetaDataImport> pImport = MDImportForModule (ModulePtr);
+ if (pImport == 0)
+ return;
+
+ static WCHAR wszName[MAX_CLASSNAME_LENGTH];
+ size_t n;
+ size_t length = strlen (name);
+ for (n = 0; n <= length; n ++)
+ wszName[n] = name[n];
+
+ // First enumerate methods. We're taking advantage of the DAC's
+ // CLRDataModule::EnumMethodDefinitionByName which can parse
+ // method names (whether in nested classes, or explicit interface
+ // method implementations).
+ ToRelease<IXCLRDataModule> ModuleDefinition;
+ if (g_sos->GetModule(ModulePtr, &ModuleDefinition) == S_OK)
+ {
+ CLRDATA_ENUM h;
+ if (ModuleDefinition->StartEnumMethodDefinitionsByName(wszName, 0, &h) == S_OK)
+ {
+ IXCLRDataMethodDefinition *pMeth = NULL;
+ BOOL fStatus = FALSE;
+ while (ModuleDefinition->EnumMethodDefinitionByName(&h, &pMeth) == S_OK)
+ {
+ if (fStatus)
+ ExtOut("-----------------------\n");
+
+ mdTypeDef token;
+ if (pMeth->GetTokenAndScope(&token, NULL) == S_OK)
+ {
+ GetInfoFromModule(ModulePtr, token);
+ fStatus = TRUE;
+ }
+ pMeth->Release();
+ }
+ ModuleDefinition->EndEnumMethodDefinitionsByName(h);
+ if (fStatus)
+ return;
+ }
+ }
+
+ // Now look for types, type members and fields
+ mdTypeDef cl;
+ mdToken tkEnclose = mdTokenNil;
+ WCHAR *pName;
+ WCHAR *pHead = wszName;
+ while ( ((pName = _wcschr (pHead,L'+')) != NULL) ||
+ ((pName = _wcschr (pHead,L'/')) != NULL)) {
+ pName[0] = L'\0';
+ if (FAILED(pImport->FindTypeDefByName(pHead,tkEnclose,&tkEnclose)))
+ return;
+ pHead = pName+1;
+ }
+
+ pName = pHead;
+
+ // @todo: Handle Nested classes correctly.
+ if (SUCCEEDED (pImport->FindTypeDefByName (pName, tkEnclose, &cl)))
+ {
+ GetInfoFromModule(ModulePtr, cl);
+ return;
+ }
+
+ // See if it is a method
+ WCHAR *pwzMethod;
+ if ((pwzMethod = _wcsrchr(pName, L'.')) == NULL)
+ return;
+
+ if (pwzMethod[-1] == L'.')
+ pwzMethod --;
+ pwzMethod[0] = L'\0';
+ pwzMethod ++;
+
+ // @todo: Handle Nested classes correctly.
+ if (SUCCEEDED(pImport->FindTypeDefByName (pName, tkEnclose, &cl)))
+ {
+ mdMethodDef token;
+ ULONG cTokens;
+ HCORENUM henum = NULL;
+
+ // is Member?
+ henum = NULL;
+ if (SUCCEEDED (pImport->EnumMembersWithName (&henum, cl, pwzMethod,
+ &token, 1, &cTokens))
+ && cTokens == 1)
+ {
+ ExtOut("Member (mdToken token) of\n");
+ GetInfoFromModule(ModulePtr, cl);
+ return;
+ }
+
+ // is Field?
+ henum = NULL;
+ if (SUCCEEDED (pImport->EnumFieldsWithName (&henum, cl, pwzMethod,
+ &token, 1, &cTokens))
+ && cTokens == 1)
+ {
+ ExtOut("Field (mdToken token) of\n");
+ GetInfoFromModule(ModulePtr, cl);
+ return;
+ }
+ }
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Find the EE data given a token. *
+* *
+\**********************************************************************/
+DWORD_PTR GetMethodDescFromModule(DWORD_PTR ModuleAddr, ULONG token)
+{
+ if (TypeFromToken(token) != mdtMethodDef)
+ return NULL;
+
+ CLRDATA_ADDRESS md = 0;
+ if (FAILED(g_sos->GetMethodDescFromToken(ModuleAddr, token, &md)))
+ {
+ return NULL;
+ }
+ else if (0 == md)
+ {
+ // a NULL ReturnValue means the method desc is not loaded yet
+ return MD_NOT_YET_LOADED;
+ }
+ else if ( !IsMethodDesc((DWORD_PTR)md))
+ {
+ return NULL;
+ }
+
+ return (DWORD_PTR)md;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Find the MethodDefinitions given a name. *
+* *
+\**********************************************************************/
+HRESULT GetMethodDefinitionsFromName(TADDR ModulePtr, IXCLRDataModule* mod, const char *name, IXCLRDataMethodDefinition **ppOut, int numMethods, int *numMethodsNeeded)
+{
+ if (name == NULL)
+ return E_FAIL;
+
+ size_t n;
+ size_t length = strlen (name);
+ for (n = 0; n <= length; n ++)
+ g_mdName[n] = name[n];
+
+ CLRDATA_ENUM h;
+ int methodCount = 0;
+ if (mod->StartEnumMethodDefinitionsByName(g_mdName, 0, &h) == S_OK)
+ {
+ IXCLRDataMethodDefinition *pMeth = NULL;
+ while (mod->EnumMethodDefinitionByName(&h, &pMeth) == S_OK)
+ {
+ methodCount++;
+ pMeth->Release();
+ }
+ mod->EndEnumMethodDefinitionsByName(h);
+ }
+
+ if(numMethodsNeeded != NULL)
+ *numMethodsNeeded = methodCount;
+ if(ppOut == NULL)
+ return S_OK;
+ if(numMethods > methodCount)
+ numMethods = methodCount;
+
+ if (methodCount > 0)
+ {
+ if (mod->StartEnumMethodDefinitionsByName(g_mdName, 0, &h) == S_OK)
+ {
+ IXCLRDataMethodDefinition *pMeth = NULL;
+ for (int i = 0; i < numMethods && mod->EnumMethodDefinitionByName(&h, &pMeth) == S_OK; i++)
+ {
+ ppOut[i] = pMeth;
+ }
+ mod->EndEnumMethodDefinitionsByName(h);
+ }
+ }
+
+ return S_OK;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Find the EE data given a name. *
+* *
+\**********************************************************************/
+HRESULT GetMethodDescsFromName(TADDR ModulePtr, IXCLRDataModule* mod, const char *name, DWORD_PTR **pOut,int *numMethods)
+{
+ if (name == NULL || pOut == NULL || numMethods == NULL)
+ return E_FAIL;
+
+ *pOut = NULL;
+ *numMethods = 0;
+
+ size_t n;
+ size_t length = strlen (name);
+ for (n = 0; n <= length; n ++)
+ g_mdName[n] = name[n];
+
+ CLRDATA_ENUM h;
+ int methodCount = 0;
+ if (mod->StartEnumMethodDefinitionsByName(g_mdName, 0, &h) == S_OK)
+ {
+ IXCLRDataMethodDefinition *pMeth = NULL;
+ while (mod->EnumMethodDefinitionByName(&h, &pMeth) == S_OK)
+ {
+ methodCount++;
+ pMeth->Release();
+ }
+ mod->EndEnumMethodDefinitionsByName(h);
+ }
+
+ if (methodCount > 0)
+ {
+ *pOut = new TADDR[methodCount];
+ if (*pOut==NULL)
+ {
+ ReportOOM();
+ return E_OUTOFMEMORY;
+ }
+
+ *numMethods = methodCount;
+
+ if (mod->StartEnumMethodDefinitionsByName(g_mdName, 0, &h) == S_OK)
+ {
+ int i = 0;
+ IXCLRDataMethodDefinition *pMeth = NULL;
+ while (mod->EnumMethodDefinitionByName(&h, &pMeth) == S_OK)
+ {
+ mdTypeDef token;
+ if (pMeth->GetTokenAndScope(&token, NULL) != S_OK)
+ (*pOut)[i] = NULL;
+ (*pOut)[i] = GetMethodDescFromModule(ModulePtr, token);
+ if ((*pOut)[i] == NULL)
+ {
+ *numMethods = 0;
+ return E_FAIL;
+ }
+ i++;
+ pMeth->Release();
+ }
+ mod->EndEnumMethodDefinitionsByName(h);
+ }
+ }
+
+ return S_OK;
+}
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* Find the EE data given a token. *
+* *
+\**********************************************************************/
+void GetInfoFromModule (DWORD_PTR ModuleAddr, ULONG token, DWORD_PTR *ret)
+{
+ switch (TypeFromToken(token))
+ {
+ case mdtMethodDef:
+ break;
+ case mdtTypeDef:
+ break;
+ case mdtTypeRef:
+ break;
+ case mdtFieldDef:
+ break;
+ default:
+ ExtOut("This token type is not supported\n");
+ return;
+ break;
+ }
+
+ CLRDATA_ADDRESS md = 0;
+ if (FAILED(g_sos->GetMethodDescFromToken(ModuleAddr, token, &md)) || !IsValidToken (ModuleAddr, token))
+ {
+ ExtOut("<invalid module token>\n");
+ return;
+ }
+
+ if (ret != NULL)
+ {
+ *ret = (DWORD_PTR)md;
+ return;
+ }
+
+ ExtOut("Token: %p\n", SOS_PTR(token));
+
+ switch (TypeFromToken(token))
+ {
+ case mdtFieldDef:
+ {
+ NameForToken_s(ModuleAddr, token, g_mdName, mdNameLen);
+ ExtOut("Field name: %S\n", g_mdName);
+ break;
+ }
+ case mdtMethodDef:
+ {
+ if (md)
+ {
+ DMLOut("MethodDesc: %s\n", DMLMethodDesc(md));
+
+ // Easiest to get full parameterized method name from ..::GetMethodName
+ if (g_sos->GetMethodDescName(md, mdNameLen, g_mdName, NULL) != S_OK)
+ {
+ // Fall back to just method name without parameters..
+ NameForToken_s(ModuleAddr, token, g_mdName, mdNameLen);
+ }
+ }
+ else
+ {
+ ExtOut("MethodDesc: <not loaded yet>\n");
+ NameForToken_s(ModuleAddr, token, g_mdName, mdNameLen);
+ }
+
+ ExtOut("Name: %S\n", g_mdName);
+ // Nice to have a little more data
+ if (md)
+ {
+ DacpMethodDescData MethodDescData;
+ if (MethodDescData.Request(g_sos, md) == S_OK)
+ {
+ if (MethodDescData.bHasNativeCode)
+ {
+ DMLOut("JITTED Code Address: %s\n", DMLIP(MethodDescData.NativeCodeAddr));
+ }
+ else
+ {
+#ifndef FEATURE_PAL
+ if (IsDMLEnabled())
+ DMLOut("Not JITTED yet. Use <exec cmd=\"!bpmd -md %p\">!bpmd -md %p</exec> to break on run.\n",
+ SOS_PTR(md), SOS_PTR(md));
+ else
+ ExtOut("Not JITTED yet. Use !bpmd -md %p to break on run.\n", SOS_PTR(md));
+#else
+ ExtOut("Not JITTED yet. Use 'bpmd -md %p' to break on run.\n", SOS_PTR(md));
+#endif
+ }
+ }
+ else
+ {
+ ExtOut ("<Error getting MethodDesc information>\n");
+ }
+ }
+ else
+ {
+ ExtOut("Not JITTED yet.\n");
+ }
+ break;
+ }
+ case mdtTypeDef:
+ case mdtTypeRef:
+ {
+ if (md)
+ {
+ DMLOut("MethodTable: %s\n", DMLMethodTable(md));
+ DacpMethodTableData mtabledata;
+ if (mtabledata.Request(g_sos, md) == S_OK)
+ {
+ DMLOut("EEClass: %s\n", DMLClass(mtabledata.Class));
+ }
+ else
+ {
+ ExtOut("EEClass: <error getting EEClass>\n");
+ }
+ }
+ else
+ {
+ ExtOut("MethodTable: <not loaded yet>\n");
+ ExtOut("EEClass: <not loaded yet>\n");
+ }
+ NameForToken_s(ModuleAddr, token, g_mdName, mdNameLen);
+ ExtOut("Name: %S\n", g_mdName);
+ break;
+ }
+ default:
+ break;
+ }
+ return;
+}
+
+BOOL IsMTForFreeObj(DWORD_PTR pMT)
+{
+ return (pMT == g_special_usefulGlobals.FreeMethodTable);
+}
+
+const char *EHTypeName(EHClauseType et)
+{
+ if (et == EHFault)
+ return "FAULT";
+ else if (et == EHFinally)
+ return "FINALLY";
+ else if (et == EHFilter)
+ return "FILTER";
+ else if (et == EHTyped)
+ return "TYPED";
+ else
+ return "UNKNOWN";
+}
+
+void DumpRejitData(DacpReJitData * pReJitData)
+{
+ ExtOut(" ReJITID %p: ", SOS_PTR(pReJitData->rejitID));
+ DMLOut("CodeAddr = %s", DMLIP(pReJitData->NativeCodeAddr));
+
+ LPCSTR szFlags;
+ switch (pReJitData->flags)
+ {
+ default:
+ case DacpReJitData::kUnknown:
+ szFlags = "";
+ break;
+
+ case DacpReJitData::kRequested:
+ szFlags = " (READY to jit on next call)";
+ break;
+
+ case DacpReJitData::kActive:
+ szFlags = " (CURRENT)";
+ break;
+
+ case DacpReJitData::kReverted:
+ szFlags = " (reverted)";
+ break;
+ }
+ ExtOut("%s\n", szFlags);
+}
+
+// For !ip2md requests, this function helps us ensure that rejitted version corresponding
+// to the specified IP always gets dumped. It may have already been dumped if it was the
+// current rejit version (which is always dumped) or one of the reverted versions that we
+// happened to dump before we clipped their number down to kcRejitDataRevertedMax.
+BOOL ShouldDumpRejitDataRequested(DacpMethodDescData * pMethodDescData, DacpReJitData * pRevertedRejitData, UINT cRevertedRejitData)
+{
+ if (pMethodDescData->rejitDataRequested.rejitID == 0)
+ return FALSE;
+
+ if (pMethodDescData->rejitDataRequested.rejitID == pMethodDescData->rejitDataCurrent.rejitID)
+ return FALSE;
+
+ for (ULONG i=0; i < cRevertedRejitData; i++)
+ {
+ if (pMethodDescData->rejitDataRequested.rejitID == pRevertedRejitData[i].rejitID)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+void DumpAllRejitDataIfNecessary(DacpMethodDescData * pMethodDescData, DacpReJitData * pRevertedRejitData, UINT cRevertedRejitData)
+{
+ // If there's no rejit info to output, then skip
+ if ((pMethodDescData->rejitDataCurrent.rejitID == 0) &&
+ (pMethodDescData->rejitDataRequested.rejitID == 0) &&
+ (cRevertedRejitData == 0))
+ {
+ return;
+ }
+ ExtOut("ReJITed versions:\n");
+
+ // Dump CURRENT rejit info
+ DumpRejitData(&pMethodDescData->rejitDataCurrent);
+
+ // Dump reverted rejit infos
+ for (ULONG i=0; i < cRevertedRejitData; i++)
+ {
+ DumpRejitData(&pRevertedRejitData[i]);
+ }
+
+ // For !ip2md, ensure we dump the rejit version corresponding to the specified IP
+ // (if not already dumped)
+ if (ShouldDumpRejitDataRequested(pMethodDescData, pRevertedRejitData, cRevertedRejitData))
+ DumpRejitData(&pMethodDescData->rejitDataRequested);
+
+ // If we maxed out the reverted versions we dumped, let user know there may be more
+ if (cRevertedRejitData == kcMaxRevertedRejitData)
+ ExtOut(" (... possibly more reverted versions ...)\n");
+}
+
+void DumpMDInfoFromMethodDescData(DacpMethodDescData * pMethodDescData, DacpReJitData * pRevertedRejitData, UINT cRevertedRejitData, BOOL fStackTraceFormat)
+{
+ static WCHAR wszNameBuffer[1024]; // should be large enough
+ BOOL bFailed = FALSE;
+ if (g_sos->GetMethodDescName(pMethodDescData->MethodDescPtr, 1024, wszNameBuffer, NULL) != S_OK)
+ {
+ wcscpy_s(wszNameBuffer, _countof(wszNameBuffer), W("UNKNOWN"));
+ bFailed = TRUE;
+ }
+
+ if (!fStackTraceFormat)
+ {
+ ExtOut("Method Name: %S\n", wszNameBuffer);
+
+ DacpMethodTableData mtdata;
+ if (SUCCEEDED(mtdata.Request(g_sos, pMethodDescData->MethodTablePtr)))
+ {
+ DMLOut("Class: %s\n", DMLClass(mtdata.Class));
+ }
+
+ DMLOut("MethodTable: %s\n", DMLMethodTable(pMethodDescData->MethodTablePtr));
+ ExtOut("mdToken: %p\n", SOS_PTR(pMethodDescData->MDToken));
+ DMLOut("Module: %s\n", DMLModule(pMethodDescData->ModulePtr));
+ ExtOut("IsJitted: %s\n", pMethodDescData->bHasNativeCode ? "yes" : "no");
+ DMLOut("CodeAddr: %s\n", DMLIP(pMethodDescData->NativeCodeAddr));
+
+ DacpMethodDescTransparencyData transparency;
+ if (SUCCEEDED(transparency.Request(g_sos, pMethodDescData->MethodDescPtr)))
+ {
+ ExtOut("Transparency: %s\n", GetTransparency(transparency));
+ }
+
+ DumpAllRejitDataIfNecessary(pMethodDescData, pRevertedRejitData, cRevertedRejitData);
+ }
+ else
+ {
+ if (!bFailed)
+ {
+ ExtOut("%S", wszNameBuffer);
+ }
+ else
+ {
+ // Only clutter the display with module/token for cases where we
+ // can't get the MethodDesc name for some reason.
+ DMLOut("Unknown MethodDesc (Module %s, mdToken %08x)",
+ DMLModule(pMethodDescData->ModulePtr),
+ pMethodDescData->MDToken);
+ }
+ }
+}
+
+void DumpMDInfo(DWORD_PTR dwMethodDescAddr, CLRDATA_ADDRESS dwRequestedIP /* = 0 */, BOOL fStackTraceFormat /* = FALSE */)
+{
+ DacpMethodDescData MethodDescData;
+ DacpReJitData revertedRejitData[kcMaxRevertedRejitData];
+ ULONG cNeededRevertedRejitData;
+ if (g_sos->GetMethodDescData(
+ TO_CDADDR(dwMethodDescAddr),
+ dwRequestedIP,
+ &MethodDescData,
+ _countof(revertedRejitData),
+ revertedRejitData,
+ &cNeededRevertedRejitData) != S_OK)
+ {
+ ExtOut("%p is not a MethodDesc\n", SOS_PTR(dwMethodDescAddr));
+ return;
+ }
+
+ DumpMDInfoFromMethodDescData(&MethodDescData, revertedRejitData, cNeededRevertedRejitData, fStackTraceFormat);
+}
+
+void GetDomainList (DWORD_PTR *&domainList, int &numDomain)
+{
+ DacpAppDomainStoreData adsData;
+
+ numDomain = 0;
+
+ if (adsData.Request(g_sos)!=S_OK)
+ {
+ return;
+ }
+
+ // Do prefast integer checks before the malloc.
+ size_t AllocSize;
+ LONG DomainAllocCount;
+ if (!ClrSafeInt<LONG>::addition(adsData.DomainCount, 2, DomainAllocCount) ||
+ !ClrSafeInt<size_t>::multiply(DomainAllocCount, sizeof(PVOID), AllocSize) ||
+ (domainList = new DWORD_PTR[DomainAllocCount]) == NULL)
+ {
+ return;
+ }
+
+ domainList[numDomain++] = (DWORD_PTR) adsData.systemDomain;
+ domainList[numDomain++] = (DWORD_PTR) adsData.sharedDomain;
+
+ CLRDATA_ADDRESS *pArray = new CLRDATA_ADDRESS[adsData.DomainCount];
+ if (pArray==NULL)
+ {
+ return;
+ }
+
+ if (g_sos->GetAppDomainList(adsData.DomainCount, pArray, NULL)!=S_OK)
+ {
+ delete [] pArray;
+ return;
+ }
+
+ for (int n=0;n<adsData.DomainCount;n++)
+ {
+ if (IsInterrupt())
+ break;
+ domainList[numDomain++] = (DWORD_PTR) pArray[n];
+ }
+
+ delete [] pArray;
+}
+
+
+HRESULT GetThreadList(DWORD_PTR **threadList, int *numThread)
+{
+ _ASSERTE(threadList != NULL);
+ _ASSERTE(numThread != NULL);
+
+ if (threadList == NULL || numThread == NULL)
+ {
+ return E_FAIL;
+ }
+
+ *numThread = 0;
+
+ DacpThreadStoreData ThreadStore;
+ if ( ThreadStore.Request(g_sos) != S_OK)
+ {
+ ExtOut("Failed to request threads from the thread store.");
+ return E_FAIL;
+ }
+
+ *threadList = new DWORD_PTR[ThreadStore.threadCount];
+ if (*threadList == NULL)
+ {
+ ReportOOM();
+ return E_OUTOFMEMORY;
+ }
+
+ CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
+ while (CurThread != NULL)
+ {
+ if (IsInterrupt())
+ return S_FALSE;
+
+ DacpThreadData Thread;
+ if (Thread.Request(g_sos, CurThread) != S_OK)
+ {
+ ExtOut("Failed to request Thread at %p\n", SOS_PTR(CurThread));
+ return E_FAIL;
+ }
+
+ (*threadList)[(*numThread)++] = (DWORD_PTR)CurThread;
+ CurThread = Thread.nextThread;
+ }
+
+ return S_OK;
+}
+
+CLRDATA_ADDRESS GetCurrentManagedThread ()
+{
+ DacpThreadStoreData ThreadStore;
+ ThreadStore.Request(g_sos);
+
+ ULONG Tid;
+ g_ExtSystem->GetCurrentThreadSystemId(&Tid);
+
+ CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
+ while (CurThread)
+ {
+ DacpThreadData Thread;
+ if (Thread.Request(g_sos, CurThread) != S_OK)
+ {
+ return NULL;
+ }
+
+ if (Thread.osThreadId == Tid)
+ {
+ return CurThread;
+ }
+
+ CurThread = Thread.nextThread;
+ }
+ return NULL;
+}
+
+
+void ReloadSymbolWithLineInfo()
+{
+#ifndef FEATURE_PAL
+ static BOOL bLoadSymbol = FALSE;
+ if (!bLoadSymbol)
+ {
+ ULONG Options;
+ g_ExtSymbols->GetSymbolOptions(&Options);
+ if (!(Options & SYMOPT_LOAD_LINES))
+ {
+ g_ExtSymbols->AddSymbolOptions(SYMOPT_LOAD_LINES);
+
+ if (SUCCEEDED(g_ExtSymbols->GetModuleByModuleName(MSCOREE_SHIM_A, 0, NULL, NULL)))
+ g_ExtSymbols->Reload("/f " MSCOREE_SHIM_A);
+
+ EEFLAVOR flavor = GetEEFlavor();
+ if (flavor == MSCORWKS)
+ g_ExtSymbols->Reload("/f " MAIN_CLR_DLL_NAME_A);
+ }
+
+ // reload mscoree.pdb and clrjit.pdb to get line info
+ bLoadSymbol = TRUE;
+ }
+#endif
+}
+
+// Return 1 if the function is our stub
+// Return MethodDesc if the function is managed
+// Otherwise return 0
+size_t FunctionType (size_t EIP)
+{
+ ULONG64 base = 0;
+ ULONG ulLoaded, ulUnloaded, ulIndex;
+
+ // Get the number of loaded and unloaded modules
+ if (FAILED(g_ExtSymbols->GetNumberModules(&ulLoaded, &ulUnloaded)))
+ return 0;
+
+
+ if (SUCCEEDED(g_ExtSymbols->GetModuleByOffset(TO_CDADDR(EIP), 0, &ulIndex, &base)) && base != 0)
+ {
+ if (ulIndex < ulLoaded)
+ {
+ IMAGE_DOS_HEADER DosHeader;
+ if (g_ExtData->ReadVirtual(TO_CDADDR(base), &DosHeader, sizeof(DosHeader), NULL) != S_OK)
+ return 0;
+ IMAGE_NT_HEADERS Header;
+ if (g_ExtData->ReadVirtual(TO_CDADDR(base + DosHeader.e_lfanew), &Header, sizeof(Header), NULL) != S_OK)
+ return 0;
+ // If there is no COMHeader, this can not be managed code.
+ if (Header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COMHEADER].VirtualAddress == 0)
+ return 0;
+
+ IMAGE_COR20_HEADER ComPlusHeader;
+ if (g_ExtData->ReadVirtual(TO_CDADDR(base + Header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COMHEADER].VirtualAddress),
+ &ComPlusHeader, sizeof(ComPlusHeader), NULL) != S_OK)
+ return 0;
+
+ // If there is no Precompiled image info, it can not be prejit code
+ if (ComPlusHeader.ManagedNativeHeader.VirtualAddress == 0) {
+ return 0;
+ }
+ }
+ }
+
+ CLRDATA_ADDRESS dwStartAddr = TO_CDADDR(EIP);
+ CLRDATA_ADDRESS pMD;
+ if (g_sos->GetMethodDescPtrFromIP(dwStartAddr, &pMD) != S_OK)
+ {
+ return 1;
+ }
+
+ return (size_t) pMD;
+}
+
+#ifndef FEATURE_PAL
+
+//
+// Gets version info for the CLR in the debuggee process.
+//
+BOOL GetEEVersion(VS_FIXEDFILEINFO *pFileInfo)
+{
+ _ASSERTE(g_ExtSymbols2);
+ _ASSERTE(pFileInfo);
+ // Grab the version info directly from the module.
+ return g_ExtSymbols2->GetModuleVersionInformation(DEBUG_ANY_ID,
+ moduleInfo[GetEEFlavor()].baseAddr,
+ "\\", pFileInfo, sizeof(VS_FIXEDFILEINFO), NULL) == S_OK;
+}
+
+extern HMODULE g_hInstance;
+BOOL GetSOSVersion(VS_FIXEDFILEINFO *pFileInfo)
+{
+ _ASSERTE(pFileInfo);
+
+ WCHAR wszFullPath[MAX_LONGPATH];
+ DWORD cchFullPath = GetModuleFileNameW(g_hInstance, wszFullPath, _countof(wszFullPath));
+
+ DWORD dwHandle = 0;
+ DWORD infoSize = GetFileVersionInfoSizeW(wszFullPath, &dwHandle);
+ if (infoSize)
+ {
+ ArrayHolder<BYTE> pVersionInfo = new BYTE[infoSize];
+ if (pVersionInfo)
+ {
+ if (GetFileVersionInfoW(wszFullPath, NULL, infoSize, pVersionInfo))
+ {
+ VS_FIXEDFILEINFO *pTmpFileInfo = NULL;
+ UINT uLen = 0;
+ if (VerQueryValue(pVersionInfo, "\\", (LPVOID *) &pTmpFileInfo, &uLen))
+ {
+ *pFileInfo = *pTmpFileInfo; // Copy the info
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+#endif // !FEATURE_PAL
+
+size_t ObjectSize(DWORD_PTR obj,BOOL fIsLargeObject)
+{
+ DWORD_PTR dwMT;
+ MOVE(dwMT, obj);
+ return ObjectSize(obj, dwMT, FALSE, fIsLargeObject);
+}
+
+size_t ObjectSize(DWORD_PTR obj, DWORD_PTR mt, BOOL fIsValueClass, BOOL fIsLargeObject)
+{
+ BOOL bContainsPointers;
+ size_t size = 0;
+ if (!GetSizeEfficient(obj, mt, fIsLargeObject, size, bContainsPointers))
+ {
+ return 0;
+ }
+ return size;
+}
+
+// This takes an array of values and sets every non-printable character
+// to be a period.
+void Flatten(__out_ecount(len) char *data, unsigned int len)
+{
+ for (unsigned int i = 0; i < len; ++i)
+ if (data[i] < 32 || data[i] > 126)
+ data[i] = '.';
+ data[len] = 0;
+}
+
+void CharArrayContent(TADDR pos, ULONG num, bool widechar)
+{
+ if (!pos || num <= 0)
+ return;
+
+ if (widechar)
+ {
+ ArrayHolder<WCHAR> data = new WCHAR[num+1];
+ if (!data)
+ {
+ ReportOOM();
+ return;
+ }
+
+ ULONG readLen = 0;
+ if (!SafeReadMemory(pos, data, num<<1, &readLen))
+ return;
+
+ Flatten(data.GetPtr(), readLen >> 1);
+ ExtOut("%S", data.GetPtr());
+ }
+ else
+ {
+ ArrayHolder<char> data = new char[num+1];
+ if (!data)
+ {
+ ReportOOM();
+ return;
+ }
+
+ ULONG readLen = 0;
+ if (!SafeReadMemory(pos, data, num, &readLen))
+ return;
+
+ _ASSERTE(readLen <= num);
+ Flatten(data, readLen);
+
+ ExtOut("%s", data.GetPtr());
+ }
+}
+
+void StringObjectContent(size_t obj, BOOL fLiteral, const int length)
+{
+ DacpObjectData objData;
+ if (objData.Request(g_sos, TO_CDADDR(obj))!=S_OK)
+ {
+ ExtOut("<Invalid Object>");
+ return;
+ }
+
+ strobjInfo stInfo;
+
+ if (MOVE(stInfo,obj) != S_OK)
+ {
+ ExtOut ("Error getting string data\n");
+ return;
+ }
+
+ if (objData.Size > 0x200000 ||
+ stInfo.m_StringLength > 0x200000)
+ {
+ ExtOut ("<String is invalid or too large to print>\n");
+ return;
+ }
+
+ ArrayHolder<WCHAR> pwszBuf = new WCHAR[stInfo.m_StringLength+1];
+ if (pwszBuf == NULL)
+ {
+ return;
+ }
+
+ DWORD_PTR dwAddr = (DWORD_PTR)pwszBuf.GetPtr();
+ if (g_sos->GetObjectStringData(TO_CDADDR(obj), stInfo.m_StringLength+1, pwszBuf, NULL)!=S_OK)
+ {
+ ExtOut("Error getting string data\n");
+ return;
+ }
+
+ if (!fLiteral)
+ {
+ pwszBuf[stInfo.m_StringLength] = L'\0';
+ ExtOut ("%S", pwszBuf.GetPtr());
+ }
+ else
+ {
+ ULONG32 count = stInfo.m_StringLength;
+ WCHAR buffer[256];
+ WCHAR out[512];
+ while (count)
+ {
+ DWORD toRead = 255;
+ if (count < toRead)
+ toRead = count;
+
+ ULONG bytesRead;
+ wcsncpy_s(buffer,_countof(buffer),(LPWSTR) dwAddr, toRead);
+ bytesRead = toRead*sizeof(WCHAR);
+ DWORD wcharsRead = bytesRead/2;
+ buffer[wcharsRead] = L'\0';
+
+ ULONG j,k=0;
+ for (j = 0; j < wcharsRead; j ++)
+ {
+ if (_iswprint (buffer[j])) {
+ out[k] = buffer[j];
+ k ++;
+ }
+ else
+ {
+ out[k++] = L'\\';
+ switch (buffer[j]) {
+ case L'\n':
+ out[k++] = L'n';
+ break;
+ case L'\0':
+ out[k++] = L'0';
+ break;
+ case L'\t':
+ out[k++] = L't';
+ break;
+ case L'\v':
+ out[k++] = L'v';
+ break;
+ case L'\b':
+ out[k++] = L'b';
+ break;
+ case L'\r':
+ out[k++] = L'r';
+ break;
+ case L'\f':
+ out[k++] = L'f';
+ break;
+ case L'\a':
+ out[k++] = L'a';
+ break;
+ case L'\\':
+ break;
+ case L'\?':
+ out[k++] = L'?';
+ break;
+ default:
+ out[k++] = L'?';
+ break;
+ }
+ }
+ }
+
+ out[k] = L'\0';
+ ExtOut ("%S", out);
+
+ count -= wcharsRead;
+ dwAddr += bytesRead;
+ }
+ }
+}
+
+#ifdef _TARGET_WIN64_
+
+#include <limits.h>
+
+__int64 str64hex(const char *ptr)
+{
+ __int64 value = 0;
+ unsigned char nCount = 0;
+
+ if(ptr==NULL)
+ return 0;
+
+ // Ignore leading 0x if present
+ if (*ptr=='0' && toupper(*(ptr+1))=='X') {
+ ptr = ptr + 2;
+ }
+
+ while (1) {
+
+ char digit;
+
+ if (isdigit(*ptr)) {
+ digit = *ptr - '0';
+ } else if (isalpha(*ptr)) {
+ digit = (((char)toupper(*ptr)) - 'A') + 10;
+ if (digit >= 16) {
+ break; // terminate
+ }
+ } else {
+ break;
+ }
+
+ if (nCount>15) {
+ return _UI64_MAX; // would be an overflow
+ }
+
+ value = value << 4;
+ value |= digit;
+
+ ptr++;
+ nCount++;
+ }
+
+ return value;
+}
+
+#endif // _TARGET_WIN64_
+
+BOOL GetValueForCMD (const char *ptr, const char *end, ARGTYPE type, size_t *value)
+{
+ if (type == COSTRING) {
+ // Allocate memory for the length of the string. Whitespace terminates
+ // User must free the string data.
+ char *pszValue = NULL;
+ size_t dwSize = (end - ptr);
+ pszValue= new char[dwSize+1];
+ if (pszValue == NULL)
+ {
+ return FALSE;
+ }
+ strncpy_s(pszValue,dwSize+1,ptr,dwSize); // _TRUNCATE
+ *value = (size_t) pszValue;
+ } else {
+ char *last;
+ if (type == COHEX) {
+#ifdef _TARGET_WIN64_
+ *value = str64hex(ptr);
+#else
+ *value = strtoul(ptr,&last,16);
+#endif
+ }
+ else {
+#ifdef _TARGET_WIN64_
+ *value = _atoi64(ptr);
+#else
+ *value = strtoul(ptr,&last,10);
+#endif
+ }
+
+#ifdef _TARGET_WIN64_
+ last = (char *) ptr;
+ // Ignore leading 0x if present
+ if (*last=='0' && toupper(*(last+1))=='X') {
+ last = last + 2;
+ }
+
+ while (isdigit(*last) || (toupper(*last)>='A' && toupper(*last)<='F')) {
+ last++;
+ }
+#endif
+
+ if (last != end) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+void SetValueForCMD (void *vptr, ARGTYPE type, size_t value)
+{
+ switch (type) {
+ case COBOOL:
+ *(BOOL*)vptr = (BOOL) value;
+ break;
+ case COSIZE_T:
+ case COSTRING:
+ case COHEX:
+ *(SIZE_T*)vptr = value;
+ break;
+ }
+}
+
+BOOL GetCMDOption(const char *string, CMDOption *option, size_t nOption,
+ CMDValue *arg, size_t maxArg, size_t *nArg)
+{
+ const char *end;
+ const char *ptr = string;
+ BOOL endofOption = FALSE;
+
+ for (size_t n = 0; n < nOption; n ++)
+ {
+ if (IsInterrupt())
+ return FALSE;
+
+ option[n].hasSeen = FALSE;
+ }
+
+ if (nArg) {
+ *nArg = 0;
+ }
+
+ while (ptr[0] != '\0')
+ {
+ if (IsInterrupt())
+ return FALSE;
+
+ // skip any space
+ if (isspace (ptr[0])) {
+ while (isspace (ptr[0]))
+ {
+ if (IsInterrupt())
+ return FALSE;
+
+ ptr ++;
+ }
+
+ continue;
+ }
+
+ end = ptr;
+
+ // Arguments can be quoted with ". We'll remove the quotes and
+ // allow spaces to exist in the string.
+ BOOL bQuotedArg = FALSE;
+ if (ptr[0] == '\'' && ptr[1] != '-')
+ {
+ bQuotedArg = TRUE;
+
+ // skip quote
+ ptr++;
+ end++;
+
+ while (end[0] != '\'' && end[0] != '\0')
+ {
+ if (IsInterrupt())
+ return FALSE;
+
+ end ++;
+ }
+ if (end[0] != '\'')
+ {
+ // Error, th ere was a start quote but no end quote
+ ExtOut ("Missing quote in %s\n", ptr);
+ return FALSE;
+ }
+ }
+ else // whitespace terminates
+ {
+ while (!isspace(end[0]) && end[0] != '\0')
+ {
+ if (IsInterrupt())
+ return FALSE;
+
+ end ++;
+ }
+ }
+
+#ifndef FEATURE_PAL
+ if (ptr[0] != '-' && ptr[0] != '/') {
+#else
+ if (ptr[0] != '-') {
+#endif
+ if (maxArg == 0) {
+ ExtOut ("Incorrect argument: %s\n", ptr);
+ return FALSE;
+ }
+ endofOption = TRUE;
+ if (*nArg >= maxArg) {
+ ExtOut ("Incorrect argument: %s\n", ptr);
+ return FALSE;
+ }
+
+ size_t value;
+ if (!GetValueForCMD (ptr,end,arg[*nArg].type,&value)) {
+
+ char oldChar = *end;
+ *(char *)end = '\0';
+ value = (size_t)GetExpression (ptr);
+ *(char *)end = oldChar;
+
+ /*
+
+ It is silly to do this, what if 0 is a valid expression for
+ the command?
+
+ if (value == 0) {
+ ExtOut ("Invalid argument: %s\n", ptr);
+ return FALSE;
+ }
+ */
+ }
+
+ SetValueForCMD (arg[*nArg].vptr, arg[*nArg].type, value);
+
+ (*nArg) ++;
+ }
+ else if (endofOption) {
+ ExtOut ("Wrong option: %s\n", ptr);
+ return FALSE;
+ }
+ else {
+ char buffer[80];
+ if (end-ptr > 79) {
+ ExtOut ("Invalid option %s\n", ptr);
+ return FALSE;
+ }
+ strncpy_s (buffer,_countof(buffer), ptr, end-ptr);
+
+ size_t n;
+ for (n = 0; n < nOption; n ++)
+ {
+ if (IsInterrupt())
+ return FALSE;
+
+ if (_stricmp (buffer, option[n].name) == 0) {
+ if (option[n].hasSeen) {
+ ExtOut ("Invalid option: option specified multiple times: %s\n", buffer);
+ return FALSE;
+ }
+ option[n].hasSeen = TRUE;
+ if (option[n].hasValue) {
+ // skip any space
+ ptr = end;
+ if (isspace (ptr[0])) {
+ while (isspace (ptr[0]))
+ {
+ if (IsInterrupt())
+ return FALSE;
+
+ ptr ++;
+ }
+ }
+ if (ptr[0] == '\0') {
+ ExtOut ("Missing value for option %s\n", buffer);
+ return FALSE;
+ }
+ end = ptr;
+ while (!isspace(end[0]) && end[0] != '\0')
+ {
+ if (IsInterrupt())
+ return FALSE;
+
+ end ++;
+ }
+
+ size_t value;
+ if (!GetValueForCMD (ptr,end,option[n].type,&value)) {
+
+ char oldChar = *end;
+ *(char *)end = '\0';
+ value = (size_t)GetExpression (ptr);
+ *(char *)end = oldChar;
+ }
+
+ SetValueForCMD (option[n].vptr,option[n].type,value);
+ }
+ else {
+ SetValueForCMD (option[n].vptr,option[n].type,TRUE);
+ }
+ break;
+ }
+ }
+ if (n == nOption) {
+ ExtOut ("Unknown option: %s\n", buffer);
+ return FALSE;
+ }
+ }
+
+ ptr = end;
+ if (bQuotedArg)
+ {
+ ptr++;
+ }
+ }
+ return TRUE;
+}
+
+ReadVirtualCache g_special_rvCacheSpace;
+ReadVirtualCache *rvCache = &g_special_rvCacheSpace;
+
+void ResetGlobals(void)
+{
+ // There are some globals used in SOS that exist for efficiency in one command,
+ // but should be reset because the next execution of an SOS command could be on
+ // another managed process. Reset them to a default state here, as this command
+ // is called on every SOS entry point.
+ g_sos->GetUsefulGlobals(&g_special_usefulGlobals);
+ g_special_mtCache.Clear();
+ g_special_rvCacheSpace.Clear();
+ Output::ResetIndent();
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Loads private DAC interface, and points g_clrData to it.
+//
+// Return Value:
+// HRESULT indicating success or failure
+//
+HRESULT LoadClrDebugDll(void)
+{
+ HRESULT hr = S_OK;
+#ifdef FEATURE_PAL
+ static IXCLRDataProcess* s_clrDataProcess = NULL;
+ if (s_clrDataProcess == NULL)
+ {
+ int err = PAL_InitializeDLL();
+ if(err != 0)
+ {
+ return CORDBG_E_UNSUPPORTED;
+ }
+ char dacModulePath[MAX_LONGPATH];
+ strcpy_s(dacModulePath, _countof(dacModulePath), g_ExtServices->GetCoreClrDirectory());
+ strcat_s(dacModulePath, _countof(dacModulePath), MAKEDLLNAME_A("mscordaccore"));
+
+ HMODULE hdac = LoadLibraryA(dacModulePath);
+ if (hdac == NULL)
+ {
+ return CORDBG_E_MISSING_DEBUGGER_EXPORTS;
+ }
+ PFN_CLRDataCreateInstance pfnCLRDataCreateInstance = (PFN_CLRDataCreateInstance)GetProcAddress(hdac, "CLRDataCreateInstance");
+ if (pfnCLRDataCreateInstance == NULL)
+ {
+ FreeLibrary(hdac);
+ return CORDBG_E_MISSING_DEBUGGER_EXPORTS;
+ }
+ ICLRDataTarget *target = new DataTarget();
+ hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), target, (void**)&s_clrDataProcess);
+ if (FAILED(hr))
+ {
+ s_clrDataProcess = NULL;
+ return hr;
+ }
+ ULONG32 flags = 0;
+ s_clrDataProcess->GetOtherNotificationFlags(&flags);
+ flags |= (CLRDATA_NOTIFY_ON_MODULE_LOAD | CLRDATA_NOTIFY_ON_MODULE_UNLOAD | CLRDATA_NOTIFY_ON_EXCEPTION);
+ s_clrDataProcess->SetOtherNotificationFlags(flags);
+ }
+ g_clrData = s_clrDataProcess;
+ g_clrData->AddRef();
+ g_clrData->Flush();
+#else
+ WDBGEXTS_CLR_DATA_INTERFACE Query;
+
+ Query.Iid = &__uuidof(IXCLRDataProcess);
+ if (!Ioctl(IG_GET_CLR_DATA_INTERFACE, &Query, sizeof(Query)))
+ {
+ return E_FAIL;
+ }
+
+ g_clrData = (IXCLRDataProcess*)Query.Iface;
+#endif
+ hr = g_clrData->QueryInterface(__uuidof(ISOSDacInterface), (void**)&g_sos);
+ if (FAILED(hr))
+ {
+ g_sos = NULL;
+ return hr;
+ }
+ return S_OK;
+}
+
+#ifndef FEATURE_PAL
+
+// This structure carries some input/output data to the FindFileInPathCallback below
+typedef struct _FindFileCallbackData
+{
+ DWORD timestamp;
+ DWORD filesize;
+ HMODULE hModule;
+} FindFileCallbackData;
+
+
+// A callback used by SymFindFileInPath - called once for each file that matches
+// the initial search criteria and allows the user to do arbitrary processing
+// This implementation checks that filesize and timestamp are correct, then
+// saves the loaded module handle
+// Parameters
+// filename - the full path the file which was found
+// context - a user specified pointer to arbitrary data, in this case a FindFileCallbackData
+// Return Value
+// TRUE if the search should continue (the file is no good)
+// FALSE if the search should stop (the file is good)
+BOOL
+FindFileInPathCallback(
+ ___in PCWSTR filename,
+ ___in PVOID context
+ )
+{
+ HRESULT hr;
+ FindFileCallbackData* pCallbackData;
+ pCallbackData = (FindFileCallbackData*)context;
+ if (!pCallbackData)
+ return TRUE;
+
+ pCallbackData->hModule = LoadLibraryExW(
+ filename,
+ NULL, // __reserved
+ LOAD_WITH_ALTERED_SEARCH_PATH); // Ensure we check the dir in wszFullPath first
+ if (pCallbackData->hModule == NULL)
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ ExtOut("Unable to load '%S'. HRESULT = 0x%x.\n", filename, hr);
+ return TRUE;
+ }
+
+ // Did we load the right one?
+ MODULEINFO modInfo = {0};
+ if (!GetModuleInformation(
+ GetCurrentProcess(),
+ pCallbackData->hModule,
+ &modInfo,
+ sizeof(modInfo)))
+ {
+ ExtOut("Failed to read module information for '%S'. HRESULT = 0x%x.\n", filename, HRESULT_FROM_WIN32(GetLastError()));
+ FreeLibrary(pCallbackData->hModule);
+ return TRUE;
+ }
+
+ IMAGE_DOS_HEADER * pDOSHeader = (IMAGE_DOS_HEADER *) modInfo.lpBaseOfDll;
+ IMAGE_NT_HEADERS * pNTHeaders = (IMAGE_NT_HEADERS *) (((LPBYTE) modInfo.lpBaseOfDll) + pDOSHeader->e_lfanew);
+ DWORD dwSizeActual = pNTHeaders->OptionalHeader.SizeOfImage;
+ DWORD dwTimeStampActual = pNTHeaders->FileHeader.TimeDateStamp;
+ if ((dwSizeActual != pCallbackData->filesize) || (dwTimeStampActual != pCallbackData->timestamp))
+ {
+ ExtOut("Found '%S', but it does not match the CLR being debugged.\n", filename);
+ ExtOut("Size: Expected '0x%x', Actual '0x%x'\n", pCallbackData->filesize, dwSizeActual);
+ ExtOut("Time stamp: Expected '0x%x', Actual '0x%x'\n", pCallbackData->timestamp, dwTimeStampActual);
+ FreeLibrary(pCallbackData->hModule);
+ return TRUE;
+ }
+
+ ExtOut("Loaded %S\n", filename);
+ return FALSE;
+}
+
+#endif // FEATURE_PAL
+
+//---------------------------------------------------------------------------------------
+// Provides a way for the public CLR debugging interface to find the appropriate
+// mscordbi.dll, DAC, etc.
+class SOSLibraryProvider : public ICLRDebuggingLibraryProvider
+{
+public:
+ SOSLibraryProvider() : m_ref(0)
+ {
+ }
+
+ virtual ~SOSLibraryProvider() {}
+
+ virtual HRESULT STDMETHODCALLTYPE QueryInterface(
+ REFIID InterfaceId,
+ PVOID* pInterface)
+ {
+ if (InterfaceId == IID_IUnknown)
+ {
+ *pInterface = static_cast<IUnknown *>(this);
+ }
+ else if (InterfaceId == IID_ICLRDebuggingLibraryProvider)
+ {
+ *pInterface = static_cast<ICLRDebuggingLibraryProvider *>(this);
+ }
+ else
+ {
+ *pInterface = NULL;
+ return E_NOINTERFACE;
+ }
+
+ AddRef();
+ return S_OK;
+ }
+
+ virtual ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return InterlockedIncrement(&m_ref);
+ }
+
+ virtual ULONG STDMETHODCALLTYPE Release()
+ {
+ LONG ref = InterlockedDecrement(&m_ref);
+ if (ref == 0)
+ {
+ delete this;
+ }
+ return ref;
+ }
+
+
+
+ // Called by the shim to locate and load mscordacwks and mscordbi
+ // Parameters:
+ // pwszFileName - the name of the file to load
+ // dwTimestamp - the expected timestamp of the file
+ // dwSizeOfImage - the expected SizeOfImage (a PE header data value)
+ // phModule - a handle to loaded module
+ //
+ // Return Value
+ // S_OK if the file was loaded, or any error if not
+ virtual HRESULT STDMETHODCALLTYPE ProvideLibrary(
+ const WCHAR * pwszFileName,
+ DWORD dwTimestamp,
+ DWORD dwSizeOfImage,
+ HMODULE * phModule)
+ {
+#ifndef FEATURE_PAL
+ HRESULT hr;
+ FindFileCallbackData callbackData = {0};
+ callbackData.timestamp = dwTimestamp;
+ callbackData.filesize = dwSizeOfImage;
+
+ if ((phModule == NULL) || (pwszFileName == NULL))
+ {
+ return E_INVALIDARG;
+ }
+
+ HMODULE dacModule;
+ if(g_sos == NULL)
+ {
+ // we ensure that windbg loads DAC first so that we can be sure to use the same one
+ return E_UNEXPECTED;
+ }
+ if (FAILED(hr = g_sos->GetDacModuleHandle(&dacModule)))
+ {
+ ExtOut("Failed to get the dac module handle. hr=0x%x.\n", hr);
+ return hr;
+ }
+
+ WCHAR dacPath[MAX_LONGPATH];
+ DWORD len = GetModuleFileNameW(dacModule, dacPath, MAX_LONGPATH);
+ if(len == 0 || len == MAX_LONGPATH)
+ {
+ ExtOut("GetModuleFileName(dacModuleHandle) failed. Last error = 0x%x\n", GetLastError());
+ return E_FAIL;
+ }
+
+ // if we are looking for the DAC, just load the one windbg already found
+ if(_wcsncmp(pwszFileName, W("mscordac"), _wcslen(W("mscordac")))==0)
+ {
+ FindFileInPathCallback(dacPath, &callbackData);
+ *phModule = callbackData.hModule;
+ return hr;
+ }
+
+ ULONG64 hProcess;
+ hr = g_ExtSystem->GetCurrentProcessHandle(&hProcess);
+ if (FAILED(hr))
+ {
+ ExtOut("IDebugSystemObjects::GetCurrentProcessHandle HRESULT=0x%x.\n", hr);
+ return hr;
+ }
+
+ ToRelease<IDebugSymbols3> spSym3(NULL);
+ hr = g_ExtSymbols->QueryInterface(__uuidof(IDebugSymbols3), (void**)&spSym3);
+ if (FAILED(hr))
+ {
+ ExtOut("Unable to query IDebugSymbol3 HRESULT=0x%x.\n", hr);
+ return hr;
+ }
+
+ ULONG pathSize = 0;
+ hr = spSym3->GetSymbolPathWide(NULL, 0, &pathSize);
+ if(FAILED(hr)) //S_FALSE if the path doesn't fit, but if the path was size 0 perhaps we would get S_OK?
+ {
+ ExtOut("Unable to get symbol path length. IDebugSymbols3::GetSymbolPathWide HRESULT=0x%x.\n", hr);
+ return hr;
+ }
+
+ ArrayHolder<WCHAR> symbolPath = new WCHAR[pathSize+MAX_LONGPATH+1];
+
+
+
+ hr = spSym3->GetSymbolPathWide(symbolPath, pathSize, NULL);
+ if(S_OK != hr)
+ {
+ ExtOut("Unable to get symbol path. IDebugSymbols3::GetSymbolPathWide HRESULT=0x%x.\n", hr);
+ return hr;
+ }
+
+ WCHAR foundPath[MAX_LONGPATH];
+ BOOL rc = SymFindFileInPathW((HANDLE)hProcess,
+ symbolPath,
+ pwszFileName,
+ (PVOID)(ULONG_PTR) dwTimestamp,
+ dwSizeOfImage,
+ 0,
+ SSRVOPT_DWORD,
+ foundPath,
+ (PFINDFILEINPATHCALLBACKW) &FindFileInPathCallback,
+ (PVOID) &callbackData
+ );
+ if(!rc)
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ ExtOut("SymFindFileInPath failed for %S. HRESULT=0x%x.\nPlease ensure that %S is on your symbol path.", pwszFileName, hr, pwszFileName);
+ }
+
+ *phModule = callbackData.hModule;
+ return hr;
+#else
+ WCHAR modulePath[MAX_LONGPATH];
+ int length = MultiByteToWideChar(CP_ACP, 0, g_ExtServices->GetCoreClrDirectory(), -1, modulePath, _countof(modulePath));
+ if (0 >= length)
+ {
+ ExtOut("MultiByteToWideChar(coreclrDirectory) failed. Last error = 0x%x\n", GetLastError());
+ return E_FAIL;
+ }
+ wcscat_s(modulePath, _countof(modulePath), pwszFileName);
+
+ *phModule = LoadLibraryW(modulePath);
+ if (*phModule == NULL)
+ {
+ HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
+ ExtOut("Unable to load '%S'. HRESULT = 0x%x.\n", pwszFileName, hr);
+ return hr;
+ }
+ return S_OK;
+#endif // FEATURE_PAL
+ }
+
+protected:
+ LONG m_ref;
+};
+
+//---------------------------------------------------------------------------------------
+// Data target for the debugged process. Provided to OpenVirtualProcess in order to
+// get an ICorDebugProcess back
+//
+class SOSDataTarget : public ICorDebugMutableDataTarget
+#ifdef FEATURE_PAL
+, public ICorDebugDataTarget4
+#endif
+{
+public:
+ SOSDataTarget() : m_ref(0)
+ {
+ }
+
+ virtual ~SOSDataTarget() {}
+
+ virtual HRESULT STDMETHODCALLTYPE QueryInterface(
+ REFIID InterfaceId,
+ PVOID* pInterface)
+ {
+ if (InterfaceId == IID_IUnknown)
+ {
+ *pInterface = static_cast<IUnknown *>(static_cast<ICorDebugDataTarget *>(this));
+ }
+ else if (InterfaceId == IID_ICorDebugDataTarget)
+ {
+ *pInterface = static_cast<ICorDebugDataTarget *>(this);
+ }
+ else if (InterfaceId == IID_ICorDebugMutableDataTarget)
+ {
+ *pInterface = static_cast<ICorDebugMutableDataTarget *>(this);
+ }
+#ifdef FEATURE_PAL
+ else if (InterfaceId == IID_ICorDebugDataTarget4)
+ {
+ *pInterface = static_cast<ICorDebugDataTarget4 *>(this);
+ }
+#endif
+ else
+ {
+ *pInterface = NULL;
+ return E_NOINTERFACE;
+ }
+
+ AddRef();
+ return S_OK;
+ }
+
+ virtual ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return InterlockedIncrement(&m_ref);
+ }
+
+ virtual ULONG STDMETHODCALLTYPE Release()
+ {
+ LONG ref = InterlockedDecrement(&m_ref);
+ if (ref == 0)
+ {
+ delete this;
+ }
+ return ref;
+ }
+
+ //
+ // ICorDebugDataTarget.
+ //
+
+ virtual HRESULT STDMETHODCALLTYPE GetPlatform(CorDebugPlatform * pPlatform)
+ {
+ ULONG platformKind = g_targetMachine->GetPlatform();
+#ifdef FEATURE_PAL
+ if(platformKind == IMAGE_FILE_MACHINE_I386)
+ *pPlatform = CORDB_PLATFORM_POSIX_X86;
+ else if(platformKind == IMAGE_FILE_MACHINE_AMD64)
+ *pPlatform = CORDB_PLATFORM_POSIX_AMD64;
+ else if(platformKind == IMAGE_FILE_MACHINE_ARMNT)
+ *pPlatform = CORDB_PLATFORM_POSIX_ARM;
+ else
+ return E_FAIL;
+#else
+ if(platformKind == IMAGE_FILE_MACHINE_I386)
+ *pPlatform = CORDB_PLATFORM_WINDOWS_X86;
+ else if(platformKind == IMAGE_FILE_MACHINE_AMD64)
+ *pPlatform = CORDB_PLATFORM_WINDOWS_AMD64;
+ else if(platformKind == IMAGE_FILE_MACHINE_ARMNT)
+ *pPlatform = CORDB_PLATFORM_WINDOWS_ARM;
+ else if(platformKind == IMAGE_FILE_MACHINE_ARM64)
+ *pPlatform = CORDB_PLATFORM_WINDOWS_ARM64;
+ else
+ return E_FAIL;
+#endif
+
+ return S_OK;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE ReadVirtual(
+ CORDB_ADDRESS address,
+ BYTE * pBuffer,
+ ULONG32 request,
+ ULONG32 * pcbRead)
+ {
+ if (g_ExtData == NULL)
+ {
+ return E_UNEXPECTED;
+ }
+ return g_ExtData->ReadVirtual(address, pBuffer, request, (PULONG) pcbRead);
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE GetThreadContext(
+ DWORD dwThreadOSID,
+ ULONG32 contextFlags,
+ ULONG32 contextSize,
+ BYTE * context)
+ {
+#ifdef FEATURE_PAL
+ if (g_ExtSystem == NULL)
+ {
+ return E_UNEXPECTED;
+ }
+ return g_ExtSystem->GetThreadContextById(dwThreadOSID, contextFlags, contextSize, context);
+#else
+ ULONG ulThreadIDOrig;
+ ULONG ulThreadIDRequested;
+ HRESULT hr;
+ HRESULT hrRet;
+
+ hr = g_ExtSystem->GetCurrentThreadId(&ulThreadIDOrig);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ hr = g_ExtSystem->GetThreadIdBySystemId(dwThreadOSID, &ulThreadIDRequested);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ hr = g_ExtSystem->SetCurrentThreadId(ulThreadIDRequested);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ // Prepare context structure
+ ZeroMemory(context, contextSize);
+ ((CONTEXT*) context)->ContextFlags = contextFlags;
+
+ // Ok, do it!
+ hrRet = g_ExtAdvanced3->GetThreadContext((LPVOID) context, contextSize);
+
+ // This is cleanup; failure here doesn't mean GetThreadContext should fail
+ // (that's determined by hrRet).
+ g_ExtSystem->SetCurrentThreadId(ulThreadIDOrig);
+
+ return hrRet;
+#endif // FEATURE_PAL
+ }
+
+ //
+ // ICorDebugMutableDataTarget.
+ //
+ virtual HRESULT STDMETHODCALLTYPE WriteVirtual(CORDB_ADDRESS address,
+ const BYTE * pBuffer,
+ ULONG32 bytesRequested)
+ {
+ if (g_ExtData == NULL)
+ {
+ return E_UNEXPECTED;
+ }
+ return g_ExtData->WriteVirtual(address, (PVOID)pBuffer, bytesRequested, NULL);
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE SetThreadContext(DWORD dwThreadID,
+ ULONG32 contextSize,
+ const BYTE * pContext)
+ {
+ return E_NOTIMPL;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE ContinueStatusChanged(DWORD dwThreadId,
+ CORDB_CONTINUE_STATUS continueStatus)
+ {
+ return E_NOTIMPL;
+ }
+
+#ifdef FEATURE_PAL
+ //
+ // ICorDebugDataTarget4
+ //
+ virtual HRESULT STDMETHODCALLTYPE VirtualUnwind(DWORD threadId, ULONG32 contextSize, PBYTE context)
+ {
+ if (g_ExtServices == NULL)
+ {
+ return E_UNEXPECTED;
+ }
+ return g_ExtServices->VirtualUnwind(threadId, contextSize, context);
+
+ }
+#endif // FEATURE_PAL
+
+protected:
+ LONG m_ref;
+};
+
+HRESULT InitCorDebugInterfaceFromModule(ULONG64 ulBase, ICLRDebugging * pClrDebugging)
+{
+ HRESULT hr;
+
+ ToRelease<ICorDebugMutableDataTarget> pSOSDataTarget = new SOSDataTarget;
+ pSOSDataTarget->AddRef();
+
+ ToRelease<ICLRDebuggingLibraryProvider> pSOSLibraryProvider = new SOSLibraryProvider;
+ pSOSLibraryProvider->AddRef();
+
+ CLR_DEBUGGING_VERSION clrDebuggingVersionRequested = {0};
+ clrDebuggingVersionRequested.wMajor = 4;
+
+ CLR_DEBUGGING_VERSION clrDebuggingVersionActual = {0};
+
+ CLR_DEBUGGING_PROCESS_FLAGS clrDebuggingFlags = (CLR_DEBUGGING_PROCESS_FLAGS)0;
+
+ ToRelease<IUnknown> pUnkProcess;
+
+ hr = pClrDebugging->OpenVirtualProcess(
+ ulBase,
+ pSOSDataTarget,
+ pSOSLibraryProvider,
+ &clrDebuggingVersionRequested,
+ IID_ICorDebugProcess,
+ &pUnkProcess,
+ &clrDebuggingVersionActual,
+ &clrDebuggingFlags);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ ICorDebugProcess * pCorDebugProcess = NULL;
+ hr = pUnkProcess->QueryInterface(IID_ICorDebugProcess, (PVOID*) &pCorDebugProcess);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ // Transfer memory ownership of refcount to global
+ g_pCorDebugProcess = pCorDebugProcess;
+ return S_OK;
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Unloads public ICorDebug interfaces, and clears g_pCorDebugProcess
+// This is only needed once after CLR unloads, not after every InitCorDebugInterface call
+//
+VOID UninitCorDebugInterface()
+{
+ if(g_pCorDebugProcess != NULL)
+ {
+ g_pCorDebugProcess->Detach();
+ g_pCorDebugProcess->Release();
+ g_pCorDebugProcess = NULL;
+ }
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Loads public ICorDebug interfaces, and points g_pCorDebugProcess to them
+// This should be called at least once per windbg stop state to ensure that
+// the interface is available and that it doesn't hold stale data. Calling it
+// more than once isn't an error, but does have perf overhead from needlessly
+// flushing memory caches.
+//
+// Return Value:
+// HRESULT indicating success or failure
+//
+
+HRESULT InitCorDebugInterface()
+{
+ HMODULE hModule = NULL;
+ HRESULT hr;
+ ToRelease<ICLRDebugging> pClrDebugging;
+
+ // we may already have an ICorDebug instance we can use
+ if(g_pCorDebugProcess != NULL)
+ {
+ // ICorDebugProcess4 is currently considered a private experimental interface on ICorDebug, it might go away so
+ // we need to be sure to handle its absense gracefully
+ ToRelease<ICorDebugProcess4> pProcess4 = NULL;
+ if(SUCCEEDED(g_pCorDebugProcess->QueryInterface(__uuidof(ICorDebugProcess4), (void**)&pProcess4)))
+ {
+ // FLUSH_ALL is more expensive than PROCESS_RUNNING, but this allows us to be safe even if things
+ // like IDNA are in use where we might be looking at non-sequential snapshots of process state
+ if(SUCCEEDED(pProcess4->ProcessStateChanged(FLUSH_ALL)))
+ {
+ // we already have an ICorDebug instance loaded and flushed, nothing more to do
+ return S_OK;
+ }
+ }
+
+ // this is a very heavy handed way of reseting
+ UninitCorDebugInterface();
+ }
+
+ // SOS now has a statically linked version of the loader code that is normally found in mscoree/mscoreei.dll
+ // Its not much code and takes a big step towards 0 install dependencies
+ // Need to pick the appropriate SKU of CLR to detect
+#if defined(FEATURE_CORESYSTEM)
+ GUID skuId = CLR_ID_ONECORE_CLR;
+#elif defined(FEATURE_CORECLR)
+ GUID skuId = CLR_ID_CORECLR;
+#else
+ GUID skuId = CLR_ID_V4_DESKTOP;
+#endif
+ CLRDebuggingImpl* pDebuggingImpl = new CLRDebuggingImpl(skuId);
+ hr = pDebuggingImpl->QueryInterface(IID_ICLRDebugging, (LPVOID *)&pClrDebugging);
+ if (FAILED(hr))
+ {
+ delete pDebuggingImpl;
+ return hr;
+ }
+
+#ifndef FEATURE_PAL
+ ULONG cLoadedModules;
+ ULONG cUnloadedModules;
+ hr = g_ExtSymbols->GetNumberModules(&cLoadedModules, &cUnloadedModules);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ ULONG64 ulBase;
+ for (ULONG i = 0; i < cLoadedModules; i++)
+ {
+ hr = g_ExtSymbols->GetModuleByIndex(i, &ulBase);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ // Dunno if this is a CLR module or not (or even if it's the particular one the
+ // user cares about during inproc SxS scenarios). For now, just try to use it
+ // to grab an ICorDebugProcess. If it works, great. Else, continue the loop
+ // until we find the first one that works.
+ hr = InitCorDebugInterfaceFromModule(ulBase, pClrDebugging);
+ if (SUCCEEDED(hr))
+ {
+ return hr;
+ }
+
+ // On failure, just iterate to the next module and try again...
+ }
+
+ // Still here? Didn't find the right module.
+ // TODO: Anything useful to return or log here?
+ return E_FAIL;
+#else
+ ULONG64 ulBase;
+ hr = g_ExtSymbols->GetModuleByModuleName(MAIN_CLR_DLL_NAME_A, 0, NULL, &ulBase);
+ if (SUCCEEDED(hr))
+ {
+ hr = InitCorDebugInterfaceFromModule(ulBase, pClrDebugging);
+ }
+ return hr;
+#endif // FEATURE_PAL
+}
+
+
+typedef enum
+{
+ GC_HEAP_INVALID = 0,
+ GC_HEAP_WKS = 1,
+ GC_HEAP_SVR = 2
+} GC_HEAP_TYPE;
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to find out if runtime is server build *
+* *
+\**********************************************************************/
+
+DacpGcHeapData *g_pHeapData = NULL;
+DacpGcHeapData g_HeapData;
+
+BOOL InitializeHeapData()
+{
+ if (g_pHeapData == NULL)
+ {
+ if (g_HeapData.Request(g_sos) != S_OK)
+ {
+ return FALSE;
+ }
+ g_pHeapData = &g_HeapData;
+ }
+ return TRUE;
+}
+
+BOOL IsServerBuild()
+{
+ return InitializeHeapData() ? g_pHeapData->bServerMode : FALSE;
+}
+
+UINT GetMaxGeneration()
+{
+ return InitializeHeapData() ? g_pHeapData->g_max_generation : 0;
+}
+
+UINT GetGcHeapCount()
+{
+ return InitializeHeapData() ? g_pHeapData->HeapCount : 0;
+}
+
+BOOL GetGcStructuresValid()
+{
+ // We don't want to use the cached HeapData, because this can change
+ // each time the program runs for a while.
+ DacpGcHeapData heapData;
+ if (heapData.Request(g_sos) != S_OK)
+ {
+ return FALSE;
+ }
+
+ return heapData.bGcStructuresValid;
+}
+
+void GetAllocContextPtrs(AllocInfo *pallocInfo)
+{
+ // gets the allocation contexts for all threads. This provides information about how much of
+ // the current allocation quantum has been allocated and the heap to which the quantum belongs.
+ // The allocation quantum is a fixed size chunk of zeroed memory from which allocations will come
+ // until it's filled. Each managed thread has its own allocation context.
+
+ pallocInfo->num = 0;
+ pallocInfo->array = NULL;
+
+ // get the thread store (See code:ClrDataAccess::RequestThreadStoreData for details)
+ DacpThreadStoreData ThreadStore;
+ if ( ThreadStore.Request(g_sos) != S_OK)
+ {
+ return;
+ }
+
+ int numThread = ThreadStore.threadCount;
+ if (numThread)
+ {
+ pallocInfo->array = new needed_alloc_context[numThread];
+ if (pallocInfo->array == NULL)
+ {
+ return;
+ }
+ }
+
+ // get details for each thread in the thread store
+ CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
+ while (CurThread != NULL)
+ {
+ if (IsInterrupt())
+ return;
+
+ DacpThreadData Thread;
+ // Get information about the thread (we're getting the values of several of the
+ // fields of the Thread instance from the target) See code:ClrDataAccess::RequestThreadData for
+ // details
+ if (Thread.Request(g_sos, CurThread) != S_OK)
+ {
+ return;
+ }
+
+ if (Thread.allocContextPtr != 0)
+ {
+ // get a list of all the allocation contexts
+ int j;
+ for (j = 0; j < pallocInfo->num; j ++)
+ {
+ if (pallocInfo->array[j].alloc_ptr == (BYTE *) Thread.allocContextPtr)
+ break;
+ }
+ if (j == pallocInfo->num)
+ {
+ pallocInfo->num ++;
+ pallocInfo->array[j].alloc_ptr = (BYTE *) Thread.allocContextPtr;
+ pallocInfo->array[j].alloc_limit = (BYTE *) Thread.allocContextLimit;
+ }
+ }
+
+ CurThread = Thread.nextThread;
+ }
+}
+
+HRESULT ReadVirtualCache::Read(TADDR taOffset, PVOID Buffer, ULONG BufferSize, PULONG lpcbBytesRead)
+{
+ // sign extend the passed in Offset so we can use it in when calling
+ // IDebugDataSpaces::ReadVirtual()
+
+ CLRDATA_ADDRESS Offset = TO_CDADDR(taOffset);
+ // Offset can be any random ULONG64, as it can come from VerifyObjectMember(), and this
+ // can pass random pointer values in case of GC heap corruption
+ HRESULT ret;
+ ULONG cbBytesRead = 0;
+
+ if (BufferSize == 0)
+ return S_OK;
+
+ if (BufferSize > CACHE_SIZE)
+ {
+ // Don't even try with the cache
+ return g_ExtData->ReadVirtual(Offset, Buffer, BufferSize, lpcbBytesRead);
+ }
+
+ if ((m_cacheValid)
+ && (taOffset >= m_startCache)
+ && (taOffset <= m_startCache + m_cacheSize - BufferSize))
+
+ {
+ // It is within the cache
+ memcpy(Buffer,(LPVOID) ((ULONG64)m_cache + (taOffset - m_startCache)), BufferSize);
+
+ if (lpcbBytesRead != NULL)
+ {
+ *lpcbBytesRead = BufferSize;
+ }
+
+ return S_OK;
+ }
+
+ m_cacheValid = FALSE;
+ m_startCache = taOffset;
+
+ // avoid an int overflow
+ if (m_startCache + CACHE_SIZE < m_startCache)
+ m_startCache = (TADDR)(-CACHE_SIZE);
+
+ ret = g_ExtData->ReadVirtual(TO_CDADDR(m_startCache), m_cache, CACHE_SIZE, &cbBytesRead);
+ if (ret != S_OK)
+ {
+ return ret;
+ }
+
+ m_cacheSize = cbBytesRead;
+ m_cacheValid = TRUE;
+ memcpy(Buffer, (LPVOID) ((ULONG64)m_cache + (taOffset - m_startCache)), BufferSize);
+
+ if (lpcbBytesRead != NULL)
+ {
+ *lpcbBytesRead = cbBytesRead;
+ }
+
+ return S_OK;
+}
+
+HRESULT GetMTOfObject(TADDR obj, TADDR *mt)
+{
+ if (!mt)
+ return E_POINTER;
+
+ // Read the MethodTable and if we succeed, get rid of the mark bits.
+ HRESULT hr = rvCache->Read(obj, mt, sizeof(TADDR), NULL);
+ if (SUCCEEDED(hr))
+ *mt &= ~3;
+
+ return hr;
+}
+
+#ifndef FEATURE_PAL
+
+StressLogMem::~StressLogMem ()
+{
+ MemRange * range = list;
+
+ while (range)
+ {
+ MemRange * temp = range->next;
+ delete range;
+ range = temp;
+ }
+}
+
+bool StressLogMem::Init (ULONG64 stressLogAddr, IDebugDataSpaces* memCallBack)
+{
+ size_t ThreadStressLogAddr = NULL;
+ HRESULT hr = memCallBack->ReadVirtual(UL64_TO_CDA(stressLogAddr + offsetof (StressLog, logs)),
+ &ThreadStressLogAddr, sizeof (ThreadStressLogAddr), 0);
+ if (hr != S_OK)
+ {
+ return false;
+ }
+
+ while(ThreadStressLogAddr != NULL)
+ {
+ size_t ChunkListHeadAddr = NULL;
+ hr = memCallBack->ReadVirtual(TO_CDADDR(ThreadStressLogAddr + ThreadStressLog::OffsetOfListHead ()),
+ &ChunkListHeadAddr, sizeof (ChunkListHeadAddr), 0);
+ if (hr != S_OK || ChunkListHeadAddr == NULL)
+ {
+ return false;
+ }
+
+ size_t StressLogChunkAddr = ChunkListHeadAddr;
+
+ do
+ {
+ AddRange (StressLogChunkAddr, sizeof (StressLogChunk));
+ hr = memCallBack->ReadVirtual(TO_CDADDR(StressLogChunkAddr + offsetof (StressLogChunk, next)),
+ &StressLogChunkAddr, sizeof (StressLogChunkAddr), 0);
+ if (hr != S_OK)
+ {
+ return false;
+ }
+ if (StressLogChunkAddr == NULL)
+ {
+ return true;
+ }
+ } while (StressLogChunkAddr != ChunkListHeadAddr);
+
+ hr = memCallBack->ReadVirtual(TO_CDADDR(ThreadStressLogAddr + ThreadStressLog::OffsetOfNext ()),
+ &ThreadStressLogAddr, sizeof (ThreadStressLogAddr), 0);
+ if (hr != S_OK)
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool StressLogMem::IsInStressLog (ULONG64 addr)
+{
+ MemRange * range = list;
+ while (range)
+ {
+ if (range->InRange (addr))
+ return true;
+ range = range->next;
+ }
+
+ return false;
+}
+
+#endif // !FEATURE_PAL
+
+unsigned int Output::g_bSuppressOutput = 0;
+unsigned int Output::g_Indent = 0;
+bool Output::g_bDbgOutput = false;
+bool Output::g_bDMLExposed = false;
+unsigned int Output::g_DMLEnable = 0;
+
+template <class T, int count, int size> const int StaticData<T, count, size>::Count = count;
+template <class T, int count, int size> const int StaticData<T, count, size>::Size = size;
+
+StaticData<char, 4, 1024> CachedString::cache;
+
+CachedString::CachedString()
+: mPtr(0), mRefCount(0), mIndex(~0), mSize(cache.Size)
+{
+ Create();
+}
+
+CachedString::CachedString(const CachedString &rhs)
+: mPtr(0), mRefCount(0), mIndex(~0), mSize(cache.Size)
+{
+ Copy(rhs);
+}
+
+CachedString::~CachedString()
+{
+ Clear();
+}
+
+const CachedString &CachedString::operator=(const CachedString &rhs)
+{
+ Clear();
+ Copy(rhs);
+ return *this;
+}
+
+void CachedString::Copy(const CachedString &rhs)
+{
+ if (rhs.IsOOM())
+ {
+ SetOOM();
+ }
+ else
+ {
+ mPtr = rhs.mPtr;
+ mIndex = rhs.mIndex;
+ mSize = rhs.mSize;
+
+ if (rhs.mRefCount)
+ {
+ mRefCount = rhs.mRefCount;
+ (*mRefCount)++;
+ }
+ else
+ {
+ // We only create count the first time we copy it, so
+ // we initialize it to 2.
+ mRefCount = rhs.mRefCount = new unsigned int(2);
+ if (!mRefCount)
+ SetOOM();
+ }
+ }
+}
+
+void CachedString::Clear()
+{
+ if (!mRefCount || --*mRefCount == 0)
+ {
+ if (mIndex == -1)
+ {
+ if (mPtr)
+ delete [] mPtr;
+ }
+ else if (mIndex >= 0 && mIndex < cache.Count)
+ {
+ cache.InUse[mIndex] = false;
+ }
+
+ if (mRefCount)
+ delete mRefCount;
+ }
+
+ mPtr = 0;
+ mIndex = ~0;
+ mRefCount = 0;
+ mSize = cache.Size;
+}
+
+
+void CachedString::Create()
+{
+ mIndex = -1;
+ mRefCount = 0;
+
+ // First try to find a string in the cache to use.
+ for (int i = 0; i < cache.Count; ++i)
+ if (!cache.InUse[i])
+ {
+ cache.InUse[i] = true;
+ mPtr = cache.Data[i];
+ mIndex = i;
+ break;
+ }
+
+ // We did not find a string to use, so we'll create a new one.
+ if (mIndex == -1)
+ {
+ mPtr = new char[cache.Size];
+ if (!mPtr)
+ SetOOM();
+ }
+}
+
+
+void CachedString::SetOOM()
+{
+ Clear();
+ mIndex = -2;
+}
+
+void CachedString::Allocate(int size)
+{
+ Clear();
+ mPtr = new char[size];
+
+ if (mPtr)
+ {
+ mSize = size;
+ mIndex = -1;
+ }
+ else
+ {
+ SetOOM();
+ }
+}
+
+size_t CountHexCharacters(CLRDATA_ADDRESS val)
+{
+ size_t ret = 0;
+
+ while (val)
+ {
+ val >>= 4;
+ ret++;
+ }
+
+ return ret;
+}
+
+void WhitespaceOut(int count)
+{
+ static const int FixedIndentWidth = 0x40;
+ static const char FixedIndentString[FixedIndentWidth+1] =
+ " ";
+
+ if (count <= 0)
+ return;
+
+ int mod = count & 0x3F;
+ count &= ~0x3F;
+
+ if (mod > 0)
+ g_ExtControl->Output(DEBUG_OUTPUT_NORMAL, "%.*s", mod, FixedIndentString);
+
+ for ( ; count > 0; count -= FixedIndentWidth)
+ g_ExtControl->Output(DEBUG_OUTPUT_NORMAL, FixedIndentString);
+}
+
+void DMLOut(PCSTR format, ...)
+{
+ if (Output::IsOutputSuppressed())
+ return;
+
+ va_list args;
+ va_start(args, format);
+ ExtOutIndent();
+
+#ifndef FEATURE_PAL
+ if (IsDMLEnabled() && !Output::IsDMLExposed())
+ {
+ g_ExtControl->ControlledOutputVaList(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, format, args);
+ }
+ else
+#endif
+ {
+ g_ExtControl->OutputVaList(DEBUG_OUTPUT_NORMAL, format, args);
+ }
+
+ va_end(args);
+}
+
+void IfDMLOut(PCSTR format, ...)
+{
+#ifndef FEATURE_PAL
+ if (Output::IsOutputSuppressed() || !IsDMLEnabled())
+ return;
+
+ va_list args;
+
+ va_start(args, format);
+ ExtOutIndent();
+ g_ExtControl->ControlledOutputVaList(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, format, args);
+ va_end(args);
+#endif
+}
+
+void ExtOut(PCSTR Format, ...)
+{
+ if (Output::IsOutputSuppressed())
+ return;
+
+ va_list Args;
+
+ va_start(Args, Format);
+ ExtOutIndent();
+ g_ExtControl->OutputVaList(DEBUG_OUTPUT_NORMAL, Format, Args);
+ va_end(Args);
+}
+
+void ExtWarn(PCSTR Format, ...)
+{
+ if (Output::IsOutputSuppressed())
+ return;
+
+ va_list Args;
+
+ va_start(Args, Format);
+ g_ExtControl->OutputVaList(DEBUG_OUTPUT_WARNING, Format, Args);
+ va_end(Args);
+}
+
+void ExtErr(PCSTR Format, ...)
+{
+ va_list Args;
+
+ va_start(Args, Format);
+ g_ExtControl->OutputVaList(DEBUG_OUTPUT_ERROR, Format, Args);
+ va_end(Args);
+}
+
+
+void ExtDbgOut(PCSTR Format, ...)
+{
+#ifdef _DEBUG
+ if (Output::g_bDbgOutput)
+ {
+ va_list Args;
+
+ va_start(Args, Format);
+ ExtOutIndent();
+ g_ExtControl->OutputVaList(DEBUG_OUTPUT_NORMAL, Format, Args);
+ va_end(Args);
+ }
+#endif
+}
+
+const char * const DMLFormats[] =
+{
+ NULL, // DML_None (do not use)
+ "<exec cmd=\"!DumpMT /d %s\">%s</exec>", // DML_MethodTable
+ "<exec cmd=\"!DumpMD /d %s\">%s</exec>", // DML_MethodDesc
+ "<exec cmd=\"!DumpClass /d %s\">%s</exec>", // DML_EEClass
+ "<exec cmd=\"!DumpModule /d %s\">%s</exec>", // DML_Module
+ "<exec cmd=\"!U /d %s\">%s</exec>", // DML_IP
+ "<exec cmd=\"!DumpObj /d %s\">%s</exec>", // DML_Object
+ "<exec cmd=\"!DumpDomain /d %s\">%s</exec>", // DML_Domain
+ "<exec cmd=\"!DumpAssembly /d %s\">%s</exec>", // DML_Assembly
+ "<exec cmd=\"~~[%s]s\">%s</exec>", // DML_ThreadID
+ "<exec cmd=\"!DumpVC /d %s %s\">%s</exec>", // DML_ValueClass
+ "<exec cmd=\"!DumpHeap /d -mt %s\">%s</exec>", // DML_DumpHeapMT
+ "<exec cmd=\"!ListNearObj /d %s\">%s</exec>", // DML_ListNearObj
+ "<exec cmd=\"!ThreadState %s\">%s</exec>", // DML_ThreadState
+ "<exec cmd=\"!PrintException /d %s\">%s</exec>",// DML_PrintException
+ "<exec cmd=\"!DumpRCW /d %s\">%s</exec>", // DML_RCWrapper
+ "<exec cmd=\"!DumpCCW /d %s\">%s</exec>", // DML_CCWrapper
+ "<exec cmd=\"!ClrStack -i %S %d\">%S</exec>", // DML_ManagedVar
+};
+
+void ConvertToLower(__out_ecount(len) char *buffer, size_t len)
+{
+ for (size_t i = 0; i < len && buffer[i]; ++i)
+ buffer[i] = (char)tolower(buffer[i]);
+}
+
+/* Build a hex display of addr.
+ */
+int GetHex(CLRDATA_ADDRESS addr, __out_ecount(len) char *out, size_t len, bool fill)
+{
+ int count = sprintf_s(out, len, fill ? "%p" : "%x", (size_t)addr);
+
+ ConvertToLower(out, len);
+
+ return count;
+}
+
+CachedString Output::BuildHexValue(CLRDATA_ADDRESS addr, FormatType type, bool fill)
+{
+ CachedString ret;
+ if (ret.IsOOM())
+ {
+ ReportOOM();
+ return ret;
+ }
+
+ if (IsDMLEnabled())
+ {
+ char hex[POINTERSIZE_BYTES*2 + 1];
+ GetHex(addr, hex, _countof(hex), fill);
+ sprintf_s(ret, ret.GetStrLen(), DMLFormats[type], hex, hex);
+ }
+ else
+ {
+ GetHex(addr, ret, ret.GetStrLen(), fill);
+ }
+
+ return ret;
+}
+
+CachedString Output::BuildVCValue(CLRDATA_ADDRESS mt, CLRDATA_ADDRESS addr, FormatType type, bool fill)
+{
+ _ASSERTE(type == DML_ValueClass);
+ CachedString ret;
+ if (ret.IsOOM())
+ {
+ ReportOOM();
+ return ret;
+ }
+
+ if (IsDMLEnabled())
+ {
+ char hexaddr[POINTERSIZE_BYTES*2 + 1];
+ char hexmt[POINTERSIZE_BYTES*2 + 1];
+
+ GetHex(addr, hexaddr, _countof(hexaddr), fill);
+ GetHex(mt, hexmt, _countof(hexmt), fill);
+
+ sprintf_s(ret, ret.GetStrLen(), DMLFormats[type], hexmt, hexaddr, hexaddr);
+ }
+ else
+ {
+ GetHex(addr, ret, ret.GetStrLen(), fill);
+ }
+
+ return ret;
+}
+
+CachedString Output::BuildManagedVarValue(__in_z LPCWSTR expansionName, ULONG frame, __in_z LPCWSTR simpleName, FormatType type)
+{
+ _ASSERTE(type == DML_ManagedVar);
+ CachedString ret;
+ if (ret.IsOOM())
+ {
+ ReportOOM();
+ return ret;
+ }
+
+ // calculate the number of digits in frame (this assumes base-10 display of frames)
+ int numFrameDigits = 0;
+ if (frame > 0)
+ {
+ ULONG tempFrame = frame;
+ while (tempFrame > 0)
+ {
+ ++numFrameDigits;
+ tempFrame /= 10;
+ }
+ }
+ else
+ {
+ numFrameDigits = 1;
+ }
+
+ size_t totalStringLength = strlen(DMLFormats[type]) + _wcslen(expansionName) + numFrameDigits + _wcslen(simpleName) + 1;
+ if (totalStringLength > ret.GetStrLen())
+ {
+ ret.Allocate(static_cast<int>(totalStringLength));
+ if (ret.IsOOM())
+ {
+ ReportOOM();
+ return ret;
+ }
+ }
+
+ if (IsDMLEnabled())
+ {
+ sprintf_s(ret, ret.GetStrLen(), DMLFormats[type], expansionName, frame, simpleName);
+ }
+ else
+ {
+ sprintf_s(ret, ret.GetStrLen(), "%S", simpleName);
+ }
+
+ return ret;
+}
+
+CachedString Output::BuildManagedVarValue(__in_z LPCWSTR expansionName, ULONG frame, int indexInArray, FormatType type)
+{
+ WCHAR indexString[24];
+ swprintf_s(indexString, _countof(indexString), W("[%d]"), indexInArray);
+ return BuildManagedVarValue(expansionName, frame, indexString, type);
+}
+
+EnableDMLHolder::EnableDMLHolder(BOOL enable)
+ : mEnable(enable)
+{
+#ifndef FEATURE_PAL
+ // If the user has not requested that we use DML, it's still possible that
+ // they have instead specified ".prefer_dml 1". If enable is false,
+ // we will check here for .prefer_dml. Since this class is only used once
+ // per command issued to SOS, this should only check the setting once per
+ // sos command issued.
+ if (!mEnable && Output::g_DMLEnable <= 0)
+ {
+ ULONG opts;
+ HRESULT hr = g_ExtControl->GetEngineOptions(&opts);
+ mEnable = SUCCEEDED(hr) && (opts & DEBUG_ENGOPT_PREFER_DML) == DEBUG_ENGOPT_PREFER_DML;
+ }
+
+ if (mEnable)
+ {
+ Output::g_DMLEnable++;
+ }
+#endif // FEATURE_PAL
+}
+
+EnableDMLHolder::~EnableDMLHolder()
+{
+#ifndef FEATURE_PAL
+ if (mEnable)
+ Output::g_DMLEnable--;
+#endif
+}
+
+bool IsDMLEnabled()
+{
+ return Output::g_DMLEnable > 0;
+}
+
+NoOutputHolder::NoOutputHolder(BOOL bSuppress)
+ : mSuppress(bSuppress)
+{
+ if (mSuppress)
+ Output::g_bSuppressOutput++;
+}
+
+NoOutputHolder::~NoOutputHolder()
+{
+ if (mSuppress)
+ Output::g_bSuppressOutput--;
+}
+
+//
+// Code to support mapping RVAs to managed code line numbers.
+//
+
+//
+// Retrieves the IXCLRDataMethodInstance* instance associated with the
+// passed in native offset.
+HRESULT
+GetClrMethodInstance(
+ ___in ULONG64 NativeOffset,
+ ___out IXCLRDataMethodInstance** Method)
+{
+ HRESULT Status;
+ CLRDATA_ENUM MethEnum;
+
+ Status = g_clrData->StartEnumMethodInstancesByAddress(NativeOffset, NULL, &MethEnum);
+
+ if (Status == S_OK)
+ {
+ Status = g_clrData->EnumMethodInstanceByAddress(&MethEnum, Method);
+ g_clrData->EndEnumMethodInstancesByAddress(MethEnum);
+ }
+
+ // Any alternate success is a true failure here.
+ return (Status == S_OK || FAILED(Status)) ? Status : E_NOINTERFACE;
+}
+
+//
+// Enumerates over the IL address map associated with the passed in
+// managed method, and returns the highest non-epilog offset.
+HRESULT
+GetLastMethodIlOffset(
+ ___in IXCLRDataMethodInstance* Method,
+ ___out PULONG32 MethodOffs)
+{
+ HRESULT Status;
+ CLRDATA_IL_ADDRESS_MAP MapLocal[16];
+ CLRDATA_IL_ADDRESS_MAP* Map = MapLocal;
+ ULONG32 MapCount = _countof(MapLocal);
+ ULONG32 MapNeeded;
+ ULONG32 HighestOffset;
+
+ for (;;)
+ {
+ if ((Status = Method->GetILAddressMap(MapCount, &MapNeeded, Map)) != S_OK)
+ {
+ return Status;
+ }
+
+ if (MapNeeded <= MapCount)
+ {
+ break;
+ }
+
+ // Need more map entries.
+ if (Map != MapLocal)
+ {
+ // Already went around and the answer changed,
+ // which should not be possible.
+ delete[] Map;
+ return E_UNEXPECTED;
+ }
+
+ Map = new CLRDATA_IL_ADDRESS_MAP[MapNeeded];
+ if (!Map)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ MapCount = MapNeeded;
+ }
+
+ HighestOffset = 0;
+ for (size_t i = 0; i < MapNeeded; i++)
+ {
+ if (Map[i].ilOffset != (ULONG32)CLRDATA_IL_OFFSET_NO_MAPPING &&
+ Map[i].ilOffset != (ULONG32)CLRDATA_IL_OFFSET_PROLOG &&
+ Map[i].ilOffset != (ULONG32)CLRDATA_IL_OFFSET_EPILOG &&
+ Map[i].ilOffset > HighestOffset)
+ {
+ HighestOffset = Map[i].ilOffset;
+ }
+ }
+
+ if (Map != MapLocal)
+ {
+ delete[] Map;
+ }
+
+ *MethodOffs = HighestOffset;
+ return S_OK;
+}
+
+//
+// Convert a native offset (possibly already associated with a managed
+// method identified by the passed in IXCLRDataMethodInstance) to a
+// triplet (ImageInfo, MethodToken, MethodOffset) that can be used to
+// represent an "IL offset".
+HRESULT
+ConvertNativeToIlOffset(
+ ___in ULONG64 native,
+ ___out IXCLRDataModule** ppModule,
+ ___out mdMethodDef* methodToken,
+ ___out PULONG32 methodOffs)
+{
+ ToRelease<IXCLRDataMethodInstance> pMethodInst(NULL);
+ HRESULT Status;
+
+ if ((Status = GetClrMethodInstance(native, &pMethodInst)) != S_OK)
+ {
+ return Status;
+ }
+
+ if ((Status = pMethodInst->GetILOffsetsByAddress(native, 1, NULL, methodOffs)) != S_OK)
+ {
+ *methodOffs = 0;
+ }
+ else
+ {
+ switch((LONG)*methodOffs)
+ {
+ case CLRDATA_IL_OFFSET_NO_MAPPING:
+ return E_NOINTERFACE;
+
+ case CLRDATA_IL_OFFSET_PROLOG:
+ // Treat all of the prologue as part of
+ // the first source line.
+ *methodOffs = 0;
+ break;
+
+ case CLRDATA_IL_OFFSET_EPILOG:
+ // Back up until we find the last real
+ // IL offset.
+ if ((Status = GetLastMethodIlOffset(pMethodInst, methodOffs)) != S_OK)
+ {
+ return Status;
+ }
+ break;
+ }
+ }
+
+ return pMethodInst->GetTokenAndScope(methodToken, ppModule);
+}
+
+// Based on a native offset, passed in the first argument this function
+// identifies the corresponding source file name and line number.
+HRESULT
+GetLineByOffset(
+ ___in ULONG64 offset,
+ ___out ULONG *pLinenum,
+ __out_ecount(cchFileName) WCHAR* pwszFileName,
+ ___in ULONG cchFileName)
+{
+ HRESULT Status = S_OK;
+ ULONG32 methodToken;
+ ULONG32 methodOffs;
+
+ // Find the image, method token and IL offset that correspond to "offset"
+ ToRelease<IXCLRDataModule> pModule(NULL);
+ IfFailRet(ConvertNativeToIlOffset(offset, &pModule, &methodToken, &methodOffs));
+
+ ToRelease<IMetaDataImport> pMDImport(NULL);
+ IfFailRet(pModule->QueryInterface(IID_IMetaDataImport, (LPVOID *) &pMDImport));
+
+ SymbolReader symbolReader;
+ IfFailRet(symbolReader.LoadSymbols(pMDImport, pModule));
+
+ return symbolReader.GetLineByILOffset(methodToken, methodOffs, pLinenum, pwszFileName, cchFileName);
+}
+
+void TableOutput::ReInit(int numColumns, int defaultColumnWidth, Alignment alignmentDefault, int indent, int padding)
+{
+ Clear();
+
+ mColumns = numColumns;
+ mDefaultWidth = defaultColumnWidth;
+ mIndent = indent;
+ mPadding = padding;
+ mCurrCol = 0;
+ mDefaultAlign = alignmentDefault;
+}
+
+void TableOutput::SetWidths(int columns, ...)
+{
+ SOS_Assert(columns > 0);
+ SOS_Assert(columns <= mColumns);
+
+ AllocWidths();
+
+ va_list list;
+ va_start(list, columns);
+
+ for (int i = 0; i < columns; ++i)
+ mWidths[i] = va_arg(list, int);
+
+ va_end(list);
+}
+
+void TableOutput::SetColWidth(int col, int width)
+{
+ SOS_Assert(col >= 0 && col < mColumns);
+ SOS_Assert(width >= 0);
+
+ AllocWidths();
+
+ mWidths[col] = width;
+}
+
+void TableOutput::SetColAlignment(int col, Alignment align)
+{
+ SOS_Assert(col >= 0 && col < mColumns);
+
+ if (!mAlignments)
+ {
+ mAlignments = new Alignment[mColumns];
+ for (int i = 0; i < mColumns; ++i)
+ mAlignments[i] = mDefaultAlign;
+ }
+
+ mAlignments[col] = align;
+}
+
+
+
+void TableOutput::Clear()
+{
+ if (mAlignments)
+ {
+ delete [] mAlignments;
+ mAlignments = 0;
+ }
+
+ if (mWidths)
+ {
+ delete [] mWidths;
+ mWidths = 0;
+ }
+}
+
+void TableOutput::AllocWidths()
+{
+ if (!mWidths)
+ {
+ mWidths = new int[mColumns];
+ for (int i = 0; i < mColumns; ++i)
+ mWidths[i] = mDefaultWidth;
+ }
+}
+
+int TableOutput::GetColumnWidth(int col)
+{
+ SOS_Assert(col < mColumns);
+
+ if (mWidths)
+ return mWidths[col];
+
+ return mDefaultWidth;
+}
+
+Alignment TableOutput::GetColAlign(int col)
+{
+ SOS_Assert(col < mColumns);
+ if (mAlignments)
+ return mAlignments[col];
+
+ return mDefaultAlign;
+}
+
+const char *TableOutput::GetWhitespace(int amount)
+{
+ static char WhiteSpace[256] = "";
+ static int count = 0;
+
+ if (count == 0)
+ {
+ count = _countof(WhiteSpace);
+ for (int i = 0; i < count-1; ++i)
+ WhiteSpace[i] = ' ';
+ WhiteSpace[count-1] = 0;
+ }
+
+ SOS_Assert(amount < count);
+ return &WhiteSpace[count-amount-1];
+}
+
+void TableOutput::OutputBlankColumns(int col)
+{
+ if (col < mCurrCol)
+ {
+ ExtOut("\n");
+ mCurrCol = 0;
+ }
+
+ int whitespace = 0;
+ for (int i = mCurrCol; i < col; ++i)
+ whitespace += GetColumnWidth(i) + mPadding;
+
+ ExtOut(GetWhitespace(whitespace));
+}
+
+void TableOutput::OutputIndent()
+{
+ if (mIndent)
+ ExtOut(GetWhitespace(mIndent));
+}
+
+#ifndef FEATURE_PAL
+
+PEOffsetMemoryReader::PEOffsetMemoryReader(TADDR moduleBaseAddress) :
+ m_moduleBaseAddress(moduleBaseAddress),
+ m_refCount(1)
+ {}
+
+HRESULT __stdcall PEOffsetMemoryReader::QueryInterface(REFIID riid, VOID** ppInterface)
+{
+ if(riid == __uuidof(IDiaReadExeAtOffsetCallback))
+ {
+ *ppInterface = static_cast<IDiaReadExeAtOffsetCallback*>(this);
+ AddRef();
+ return S_OK;
+ }
+ else if(riid == __uuidof(IUnknown))
+ {
+ *ppInterface = static_cast<IUnknown*>(this);
+ AddRef();
+ return S_OK;
+ }
+ else
+ {
+ return E_NOINTERFACE;
+ }
+}
+
+ULONG __stdcall PEOffsetMemoryReader::AddRef()
+{
+ return InterlockedIncrement((volatile LONG *) &m_refCount);
+}
+
+ULONG __stdcall PEOffsetMemoryReader::Release()
+{
+ ULONG count = InterlockedDecrement((volatile LONG *) &m_refCount);
+ if(count == 0)
+ {
+ delete this;
+ }
+ return count;
+}
+
+// IDiaReadExeAtOffsetCallback implementation
+HRESULT __stdcall PEOffsetMemoryReader::ReadExecutableAt(DWORDLONG fileOffset, DWORD cbData, DWORD* pcbData, BYTE data[])
+{
+ return SafeReadMemory(m_moduleBaseAddress + fileOffset, data, cbData, pcbData) ? S_OK : E_FAIL;
+}
+
+PERvaMemoryReader::PERvaMemoryReader(TADDR moduleBaseAddress) :
+ m_moduleBaseAddress(moduleBaseAddress),
+ m_refCount(1)
+ {}
+
+HRESULT __stdcall PERvaMemoryReader::QueryInterface(REFIID riid, VOID** ppInterface)
+{
+ if(riid == __uuidof(IDiaReadExeAtRVACallback))
+ {
+ *ppInterface = static_cast<IDiaReadExeAtRVACallback*>(this);
+ AddRef();
+ return S_OK;
+ }
+ else if(riid == __uuidof(IUnknown))
+ {
+ *ppInterface = static_cast<IUnknown*>(this);
+ AddRef();
+ return S_OK;
+ }
+ else
+ {
+ return E_NOINTERFACE;
+ }
+}
+
+ULONG __stdcall PERvaMemoryReader::AddRef()
+{
+ return InterlockedIncrement((volatile LONG *) &m_refCount);
+}
+
+ULONG __stdcall PERvaMemoryReader::Release()
+{
+ ULONG count = InterlockedDecrement((volatile LONG *) &m_refCount);
+ if(count == 0)
+ {
+ delete this;
+ }
+ return count;
+}
+
+// IDiaReadExeAtOffsetCallback implementation
+HRESULT __stdcall PERvaMemoryReader::ReadExecutableAtRVA(DWORD relativeVirtualAddress, DWORD cbData, DWORD* pcbData, BYTE data[])
+{
+ return SafeReadMemory(m_moduleBaseAddress + relativeVirtualAddress, data, cbData, pcbData) ? S_OK : E_FAIL;
+}
+
+#endif // FEATURE_PAL
+
+HRESULT SymbolReader::LoadSymbols(___in IMetaDataImport* pMD, ___in ICorDebugModule* pModule)
+{
+ HRESULT Status = S_OK;
+ BOOL isDynamic = FALSE;
+ BOOL isInMemory = FALSE;
+ IfFailRet(pModule->IsDynamic(&isDynamic));
+ IfFailRet(pModule->IsInMemory(&isInMemory));
+
+ if (isDynamic)
+ {
+ // Dynamic and in memory assemblies are a special case which we will ignore for now
+ ExtWarn("SOS Warning: Loading symbols for dynamic assemblies is not yet supported\n");
+ return E_FAIL;
+ }
+
+ ULONG64 peAddress = 0;
+ ULONG32 peSize = 0;
+ IfFailRet(pModule->GetBaseAddress(&peAddress));
+ IfFailRet(pModule->GetSize(&peSize));
+
+ ULONG32 len = 0;
+ WCHAR moduleName[MAX_LONGPATH];
+ IfFailRet(pModule->GetName(_countof(moduleName), &len, moduleName));
+
+#ifndef FEATURE_PAL
+ if (SUCCEEDED(LoadSymbolsForWindowsPDB(pMD, peAddress, moduleName, isInMemory)))
+ {
+ return S_OK;
+ }
+#endif // FEATURE_PAL
+ return LoadSymbolsForPortablePDB(moduleName, isInMemory, isInMemory, peAddress, peSize, 0, 0);
+}
+
+HRESULT SymbolReader::LoadSymbols(___in IMetaDataImport* pMD, ___in IXCLRDataModule* pModule)
+{
+ DacpGetModuleData moduleData;
+ HRESULT hr = moduleData.Request(pModule);
+ if (FAILED(hr))
+ {
+ ExtOut("LoadSymbols moduleData.Request FAILED 0x%08x\n", hr);
+ return hr;
+ }
+
+ if (moduleData.IsDynamic)
+ {
+ ExtWarn("SOS Warning: Loading symbols for dynamic assemblies is not yet supported\n");
+ return E_FAIL;
+ }
+
+ ArrayHolder<WCHAR> pModuleName = new WCHAR[MAX_LONGPATH + 1];
+ ULONG32 nameLen = 0;
+ hr = pModule->GetFileName(MAX_LONGPATH, &nameLen, pModuleName);
+ if (FAILED(hr))
+ {
+ ExtOut("LoadSymbols: IXCLRDataModule->GetFileName FAILED 0x%08x\n", hr);
+ return hr;
+ }
+
+#ifndef FEATURE_PAL
+ // TODO: in-memory windows PDB not supported
+ hr = LoadSymbolsForWindowsPDB(pMD, moduleData.LoadedPEAddress, pModuleName, moduleData.IsFileLayout);
+ if (SUCCEEDED(hr))
+ {
+ return hr;
+ }
+#endif // FEATURE_PAL
+
+ return LoadSymbolsForPortablePDB(
+ pModuleName,
+ moduleData.IsInMemory,
+ moduleData.IsFileLayout,
+ moduleData.LoadedPEAddress,
+ moduleData.LoadedPESize,
+ moduleData.InMemoryPdbAddress,
+ moduleData.InMemoryPdbSize);
+}
+
+#ifndef FEATURE_PAL
+
+HRESULT SymbolReader::LoadSymbolsForWindowsPDB(___in IMetaDataImport* pMD, ___in ULONG64 peAddress, __in_z WCHAR* pModuleName, ___in BOOL isFileLayout)
+{
+ HRESULT Status = S_OK;
+
+ if (m_pSymReader != NULL)
+ return S_OK;
+
+ IfFailRet(CoInitialize(NULL));
+
+ // We now need a binder object that will take the module and return a
+ // reader object
+ ToRelease<ISymUnmanagedBinder3> pSymBinder;
+ if (FAILED(Status = CreateInstanceCustom(CLSID_CorSymBinder_SxS,
+ IID_ISymUnmanagedBinder3,
+ W("diasymreader.dll"),
+ cciLatestFx|cciDacColocated|cciDbgPath,
+ (void**)&pSymBinder)))
+ {
+ ExtOut("SOS Error: Unable to CoCreateInstance class=CLSID_CorSymBinder_SxS, interface=IID_ISymUnmanagedBinder3, hr=0x%x\n", Status);
+ ExtOut("This usually means the installation of .Net Framework on your machine is missing or needs repair\n");
+ return Status;
+ }
+
+ ToRelease<IDebugSymbols3> spSym3(NULL);
+ Status = g_ExtSymbols->QueryInterface(__uuidof(IDebugSymbols3), (void**)&spSym3);
+ if (FAILED(Status))
+ {
+ ExtOut("SOS Error: Unable to query IDebugSymbols3 HRESULT=0x%x.\n", Status);
+ return Status;
+ }
+
+ ULONG pathSize = 0;
+ Status = spSym3->GetSymbolPathWide(NULL, 0, &pathSize);
+ if (FAILED(Status)) //S_FALSE if the path doesn't fit, but if the path was size 0 perhaps we would get S_OK?
+ {
+ ExtOut("SOS Error: Unable to get symbol path length. IDebugSymbols3::GetSymbolPathWide HRESULT=0x%x.\n", Status);
+ return Status;
+ }
+
+ ArrayHolder<WCHAR> symbolPath = new WCHAR[pathSize];
+ Status = spSym3->GetSymbolPathWide(symbolPath, pathSize, NULL);
+ if (S_OK != Status)
+ {
+ ExtOut("SOS Error: Unable to get symbol path. IDebugSymbols3::GetSymbolPathWide HRESULT=0x%x.\n", Status);
+ return Status;
+ }
+
+ ToRelease<IUnknown> pCallback = NULL;
+ if (isFileLayout)
+ {
+ pCallback = (IUnknown*) new PEOffsetMemoryReader(TO_TADDR(peAddress));
+ }
+ else
+ {
+ pCallback = (IUnknown*) new PERvaMemoryReader(TO_TADDR(peAddress));
+ }
+
+ // TODO: this should be better integrated with windbg's symbol lookup
+ Status = pSymBinder->GetReaderFromCallback(pMD, pModuleName, symbolPath,
+ AllowRegistryAccess | AllowSymbolServerAccess | AllowOriginalPathAccess | AllowReferencePathAccess, pCallback, &m_pSymReader);
+
+ if (FAILED(Status) && m_pSymReader != NULL)
+ {
+ m_pSymReader->Release();
+ m_pSymReader = NULL;
+ }
+ return Status;
+}
+
+#endif // FEATURE_PAL
+
+//
+// Pass to managed helper code to read in-memory PEs/PDBs
+// Returns the number of bytes read.
+//
+int ReadMemoryForSymbols(ULONG64 address, char *buffer, int cb)
+{
+ ULONG read;
+ if (SafeReadMemory(address, (PVOID)buffer, cb, &read))
+ {
+ return read;
+ }
+ return 0;
+}
+
+HRESULT SymbolReader::LoadSymbolsForPortablePDB(__in_z WCHAR* pModuleName, ___in BOOL isInMemory, ___in BOOL isFileLayout,
+ ___in ULONG64 peAddress, ___in ULONG64 peSize, ___in ULONG64 inMemoryPdbAddress, ___in ULONG64 inMemoryPdbSize)
+{
+ HRESULT Status = S_OK;
+
+ if (loadSymbolsForModuleDelegate == nullptr)
+ {
+ IfFailRet(PrepareSymbolReader());
+ }
+
+ // The module name needs to be null for in-memory PE's.
+ ArrayHolder<char> szModuleName = nullptr;
+ if (!isInMemory && pModuleName != nullptr)
+ {
+ szModuleName = new char[MAX_LONGPATH];
+ if (WideCharToMultiByte(CP_ACP, 0, pModuleName, (int)(_wcslen(pModuleName) + 1), szModuleName, MAX_LONGPATH, NULL, NULL) == 0)
+ {
+ return E_FAIL;
+ }
+ }
+
+ m_symbolReaderHandle = loadSymbolsForModuleDelegate(szModuleName, isFileLayout, peAddress,
+ (int)peSize, inMemoryPdbAddress, (int)inMemoryPdbSize, ReadMemoryForSymbols);
+
+ if (m_symbolReaderHandle == 0)
+ {
+ return E_FAIL;
+ }
+
+ return Status;
+}
+
+#ifndef FEATURE_PAL
+
+void AddFilesFromDirectoryToTpaList(const char* directory, std::string& tpaList)
+{
+ const char * const tpaExtensions[] = {
+ "*.ni.dll", // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir
+ "*.dll",
+ "*.ni.exe",
+ "*.exe",
+ };
+
+ std::set<std::string> addedAssemblies;
+
+ // Walk the directory for each extension separately so that we first get files with .ni.dll extension,
+ // then files with .dll extension, etc.
+ for (int extIndex = 0; extIndex < sizeof(tpaExtensions) / sizeof(tpaExtensions[0]); extIndex++)
+ {
+ const char* ext = tpaExtensions[extIndex];
+ size_t extLength = strlen(ext);
+
+ std::string assemblyPath(directory);
+ assemblyPath.append(DIRECTORY_SEPARATOR_STR_A);
+ assemblyPath.append(tpaExtensions[extIndex]);
+
+ WIN32_FIND_DATAA data;
+ HANDLE findHandle = FindFirstFileA(assemblyPath.c_str(), &data);
+
+ if (findHandle != INVALID_HANDLE_VALUE)
+ {
+ do
+ {
+ if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+ {
+
+ std::string filename(data.cFileName);
+ size_t extPos = filename.length() - extLength;
+ std::string filenameWithoutExt(filename.substr(0, extPos));
+
+ // Make sure if we have an assembly with multiple extensions present,
+ // we insert only one version of it.
+ if (addedAssemblies.find(filenameWithoutExt) == addedAssemblies.end())
+ {
+ addedAssemblies.insert(filenameWithoutExt);
+
+ tpaList.append(directory);
+ tpaList.append(DIRECTORY_SEPARATOR_STR_A);
+ tpaList.append(filename);
+ tpaList.append(";");
+ }
+ }
+ }
+ while (0 != FindNextFileA(findHandle, &data));
+
+ FindClose(findHandle);
+ }
+ }
+}
+
+bool GetEntrypointExecutableAbsolutePath(std::string& entrypointExecutable)
+{
+ ArrayHolder<char> hostPath = new char[MAX_LONGPATH+1];
+ if (::GetModuleFileName(NULL, hostPath, MAX_LONGPATH) == 0)
+ {
+ return false;
+ }
+
+ entrypointExecutable.clear();
+ entrypointExecutable.append(hostPath);
+
+ return true;
+}
+
+#endif // FEATURE_PAL
+
+HRESULT SymbolReader::PrepareSymbolReader()
+{
+ static bool attemptedSymbolReaderPreparation = false;
+ if (attemptedSymbolReaderPreparation)
+ {
+ // If we already tried to set up the symbol reader, we won't try again.
+ return E_FAIL;
+ }
+
+ attemptedSymbolReaderPreparation = true;
+
+ std::string absolutePath;
+ std::string coreClrPath;
+ HRESULT Status;
+
+#ifdef FEATURE_PAL
+ coreClrPath = g_ExtServices->GetCoreClrDirectory();
+ if (!GetAbsolutePath(coreClrPath.c_str(), absolutePath))
+ {
+ ExtErr("Error: Failed to get coreclr absolute path\n");
+ return E_FAIL;
+ }
+ coreClrPath.append(DIRECTORY_SEPARATOR_STR_A);
+ coreClrPath.append(MAIN_CLR_DLL_NAME_A);
+#else
+ ULONG index;
+ Status = g_ExtSymbols->GetModuleByModuleName(MAIN_CLR_MODULE_NAME_A, 0, &index, NULL);
+ if (FAILED(Status))
+ {
+ ExtErr("Error: Can't find coreclr module\n");
+ return Status;
+ }
+ ArrayHolder<char> szModuleName = new char[MAX_LONGPATH + 1];
+ Status = g_ExtSymbols->GetModuleNames(index, 0, szModuleName, MAX_LONGPATH, NULL, NULL, 0, NULL, NULL, 0, NULL);
+ if (FAILED(Status))
+ {
+ ExtErr("Error: Failed to get coreclr module name\n");
+ return Status;
+ }
+ coreClrPath = szModuleName;
+
+ // Parse off the module name to get just the path
+ size_t pos = coreClrPath.rfind(DIRECTORY_SEPARATOR_CHAR_A);
+ if (pos == std::string::npos)
+ {
+ ExtErr("Error: Failed to parse coreclr module name\n");
+ return E_FAIL;
+ }
+ absolutePath.assign(coreClrPath, 0, pos);
+#endif // FEATURE_PAL
+
+ HMODULE coreclrLib = LoadLibraryA(coreClrPath.c_str());
+ if (coreclrLib == nullptr)
+ {
+ ExtErr("Error: Failed to load %s\n", coreClrPath.c_str());
+ return E_FAIL;
+ }
+
+ void *hostHandle;
+ unsigned int domainId;
+ coreclr_initialize_ptr initializeCoreCLR = (coreclr_initialize_ptr)GetProcAddress(coreclrLib, "coreclr_initialize");
+ if (initializeCoreCLR == nullptr)
+ {
+ ExtErr("Error: coreclr_initialize not found\n");
+ return E_FAIL;
+ }
+
+ std::string tpaList;
+ AddFilesFromDirectoryToTpaList(absolutePath.c_str(), tpaList);
+
+ const char *propertyKeys[] = {
+ "TRUSTED_PLATFORM_ASSEMBLIES", "APP_PATHS", "APP_NI_PATHS",
+ "NATIVE_DLL_SEARCH_DIRECTORIES", "AppDomainCompatSwitch"};
+
+ const char *propertyValues[] = {// TRUSTED_PLATFORM_ASSEMBLIES
+ tpaList.c_str(),
+ // APP_PATHS
+ absolutePath.c_str(),
+ // APP_NI_PATHS
+ absolutePath.c_str(),
+ // NATIVE_DLL_SEARCH_DIRECTORIES
+ absolutePath.c_str(),
+ // AppDomainCompatSwitch
+ "UseLatestBehaviorWhenTFMNotSpecified"};
+
+ std::string entryPointExecutablePath;
+ if (!GetEntrypointExecutableAbsolutePath(entryPointExecutablePath))
+ {
+ ExtErr("Could not get full path to current executable");
+ return E_FAIL;
+ }
+
+ Status = initializeCoreCLR(entryPointExecutablePath.c_str(), "sos",
+ sizeof(propertyKeys) / sizeof(propertyKeys[0]), propertyKeys, propertyValues, &hostHandle, &domainId);
+
+ if (FAILED(Status))
+ {
+ ExtErr("Error: Fail to initialize CoreCLR %08x\n", Status);
+ return Status;
+ }
+
+ coreclr_create_delegate_ptr createDelegate = (coreclr_create_delegate_ptr)GetProcAddress(coreclrLib, "coreclr_create_delegate");
+ if (createDelegate == nullptr)
+ {
+ ExtErr("Error: coreclr_create_delegate not found\n");
+ return E_FAIL;
+ }
+
+ IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "LoadSymbolsForModule", (void **)&loadSymbolsForModuleDelegate));
+ IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "Dispose", (void **)&disposeDelegate));
+ IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "ResolveSequencePoint", (void **)&resolveSequencePointDelegate));
+ IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "GetLocalVariableName", (void **)&getLocalVariableNameDelegate));
+ IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "GetLineByILOffset", (void **)&getLineByILOffsetDelegate));
+
+ return Status;
+}
+
+HRESULT SymbolReader::GetLineByILOffset(___in mdMethodDef methodToken, ___in ULONG64 ilOffset,
+ ___out ULONG *pLinenum, __out_ecount(cchFileName) WCHAR* pwszFileName, ___in ULONG cchFileName)
+{
+ HRESULT Status = S_OK;
+
+ if (m_symbolReaderHandle != 0)
+ {
+ _ASSERTE(getLineByILOffsetDelegate != nullptr);
+
+ BSTR bstrFileName = SysAllocStringLen(0, MAX_LONGPATH);
+ if (bstrFileName == nullptr)
+ {
+ return E_OUTOFMEMORY;
+ }
+ // Source lines with 0xFEEFEE markers are filtered out on the managed side.
+ if ((getLineByILOffsetDelegate(m_symbolReaderHandle, methodToken, ilOffset, pLinenum, &bstrFileName) == FALSE) || (*pLinenum == 0))
+ {
+ SysFreeString(bstrFileName);
+ return E_FAIL;
+ }
+ wcscpy_s(pwszFileName, cchFileName, bstrFileName);
+ SysFreeString(bstrFileName);
+ return S_OK;
+ }
+
+#ifndef FEATURE_PAL
+ if (m_pSymReader == NULL)
+ return E_FAIL;
+
+ ToRelease<ISymUnmanagedMethod> pSymMethod(NULL);
+ IfFailRet(m_pSymReader->GetMethod(methodToken, &pSymMethod));
+
+ ULONG32 seqPointCount = 0;
+ IfFailRet(pSymMethod->GetSequencePointCount(&seqPointCount));
+
+ if (seqPointCount == 0)
+ return E_FAIL;
+
+ // allocate memory for the objects to be fetched
+ ArrayHolder<ULONG32> offsets(new ULONG32[seqPointCount]);
+ ArrayHolder<ULONG32> lines(new ULONG32[seqPointCount]);
+ ArrayHolder<ULONG32> columns(new ULONG32[seqPointCount]);
+ ArrayHolder<ULONG32> endlines(new ULONG32[seqPointCount]);
+ ArrayHolder<ULONG32> endcolumns(new ULONG32[seqPointCount]);
+ ArrayHolder<ToRelease<ISymUnmanagedDocument>> documents(new ToRelease<ISymUnmanagedDocument>[seqPointCount]);
+
+ ULONG32 realSeqPointCount = 0;
+ IfFailRet(pSymMethod->GetSequencePoints(seqPointCount, &realSeqPointCount, offsets, &(documents[0]), lines, columns, endlines, endcolumns));
+
+ const ULONG32 HiddenLine = 0x00feefee;
+ int bestSoFar = -1;
+
+ for (int i = 0; i < (int)realSeqPointCount; i++)
+ {
+ if (offsets[i] > ilOffset)
+ break;
+
+ if (lines[i] != HiddenLine)
+ bestSoFar = i;
+ }
+
+ if (bestSoFar != -1)
+ {
+ ULONG32 cchNeeded = 0;
+ IfFailRet(documents[bestSoFar]->GetURL(cchFileName, &cchNeeded, pwszFileName));
+
+ *pLinenum = lines[bestSoFar];
+ return S_OK;
+ }
+#endif // FEATURE_PAL
+
+ return E_FAIL;
+}
+
+HRESULT SymbolReader::GetNamedLocalVariable(___in ISymUnmanagedScope * pScope, ___in ICorDebugILFrame * pILFrame, ___in mdMethodDef methodToken,
+ ___in ULONG localIndex, __out_ecount(paramNameLen) WCHAR* paramName, ___in ULONG paramNameLen, ICorDebugValue** ppValue)
+{
+ HRESULT Status = S_OK;
+
+ if (m_symbolReaderHandle != 0)
+ {
+ _ASSERTE(getLocalVariableNameDelegate != nullptr);
+
+ BSTR wszParamName = SysAllocStringLen(0, mdNameLen);
+ if (wszParamName == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ if (getLocalVariableNameDelegate(m_symbolReaderHandle, methodToken, localIndex, &wszParamName) == FALSE)
+ {
+ SysFreeString(wszParamName);
+ return E_FAIL;
+ }
+
+ wcscpy_s(paramName, paramNameLen, wszParamName);
+ SysFreeString(wszParamName);
+
+ if (FAILED(pILFrame->GetLocalVariable(localIndex, ppValue)) || (*ppValue == NULL))
+ {
+ *ppValue = NULL;
+ return E_FAIL;
+ }
+ return S_OK;
+ }
+
+#ifndef FEATURE_PAL
+ if (m_pSymReader == NULL)
+ return E_FAIL;
+
+ if (pScope == NULL)
+ {
+ ToRelease<ISymUnmanagedMethod> pSymMethod;
+ IfFailRet(m_pSymReader->GetMethod(methodToken, &pSymMethod));
+
+ ToRelease<ISymUnmanagedScope> pScope;
+ IfFailRet(pSymMethod->GetRootScope(&pScope));
+
+ return GetNamedLocalVariable(pScope, pILFrame, methodToken, localIndex, paramName, paramNameLen, ppValue);
+ }
+ else
+ {
+ ULONG32 numVars = 0;
+ IfFailRet(pScope->GetLocals(0, &numVars, NULL));
+
+ ArrayHolder<ISymUnmanagedVariable*> pLocals = new ISymUnmanagedVariable*[numVars];
+ IfFailRet(pScope->GetLocals(numVars, &numVars, pLocals));
+
+ for (ULONG i = 0; i < numVars; i++)
+ {
+ ULONG32 varIndexInMethod = 0;
+ if (SUCCEEDED(pLocals[i]->GetAddressField1(&varIndexInMethod)))
+ {
+ if (varIndexInMethod != localIndex)
+ continue;
+
+ ULONG32 nameLen = 0;
+ if (FAILED(pLocals[i]->GetName(paramNameLen, &nameLen, paramName)))
+ swprintf_s(paramName, paramNameLen, W("local_%d\0"), localIndex);
+
+ if (SUCCEEDED(pILFrame->GetLocalVariable(varIndexInMethod, ppValue)) && (*ppValue != NULL))
+ {
+ for(ULONG j = 0; j < numVars; j++) pLocals[j]->Release();
+ return S_OK;
+ }
+ else
+ {
+ *ppValue = NULL;
+ for(ULONG j = 0; j < numVars; j++) pLocals[j]->Release();
+ return E_FAIL;
+ }
+ }
+ }
+
+ ULONG32 numChildren = 0;
+ IfFailRet(pScope->GetChildren(0, &numChildren, NULL));
+
+ ArrayHolder<ISymUnmanagedScope*> pChildren = new ISymUnmanagedScope*[numChildren];
+ IfFailRet(pScope->GetChildren(numChildren, &numChildren, pChildren));
+
+ for (ULONG i = 0; i < numChildren; i++)
+ {
+ if (SUCCEEDED(GetNamedLocalVariable(pChildren[i], pILFrame, methodToken, localIndex, paramName, paramNameLen, ppValue)))
+ {
+ for (ULONG j = 0; j < numChildren; j++) pChildren[j]->Release();
+ return S_OK;
+ }
+ }
+
+ for (ULONG j = 0; j < numChildren; j++) pChildren[j]->Release();
+ }
+#endif // FEATURE_PAL
+
+ return E_FAIL;
+}
+
+HRESULT SymbolReader::GetNamedLocalVariable(___in ICorDebugFrame * pFrame, ___in ULONG localIndex, __out_ecount(paramNameLen) WCHAR* paramName,
+ ___in ULONG paramNameLen, ___out ICorDebugValue** ppValue)
+{
+ HRESULT Status = S_OK;
+
+ *ppValue = NULL;
+ paramName[0] = L'\0';
+
+ ToRelease<ICorDebugILFrame> pILFrame;
+ IfFailRet(pFrame->QueryInterface(IID_ICorDebugILFrame, (LPVOID*) &pILFrame));
+
+ ToRelease<ICorDebugFunction> pFunction;
+ IfFailRet(pFrame->GetFunction(&pFunction));
+
+ mdMethodDef methodDef;
+ ToRelease<ICorDebugClass> pClass;
+ ToRelease<ICorDebugModule> pModule;
+ IfFailRet(pFunction->GetClass(&pClass));
+ IfFailRet(pFunction->GetModule(&pModule));
+ IfFailRet(pFunction->GetToken(&methodDef));
+
+ return GetNamedLocalVariable(NULL, pILFrame, methodDef, localIndex, paramName, paramNameLen, ppValue);
+}
+
+HRESULT SymbolReader::ResolveSequencePoint(__in_z WCHAR* pFilename, ___in ULONG32 lineNumber, ___in TADDR mod, ___out mdMethodDef* pToken, ___out ULONG32* pIlOffset)
+{
+ HRESULT Status = S_OK;
+
+ if (m_symbolReaderHandle != 0)
+ {
+ _ASSERTE(resolveSequencePointDelegate != nullptr);
+
+ char szName[mdNameLen];
+ if (WideCharToMultiByte(CP_ACP, 0, pFilename, (int)(_wcslen(pFilename) + 1), szName, mdNameLen, NULL, NULL) == 0)
+ {
+ return E_FAIL;
+ }
+ if (resolveSequencePointDelegate(m_symbolReaderHandle, szName, lineNumber, pToken, pIlOffset) == FALSE)
+ {
+ return E_FAIL;
+ }
+ return S_OK;
+ }
+
+#ifndef FEATURE_PAL
+ if (m_pSymReader == NULL)
+ return E_FAIL;
+
+ ULONG32 cDocs = 0;
+ ULONG32 cDocsNeeded = 0;
+ ArrayHolder<ToRelease<ISymUnmanagedDocument>> pDocs = NULL;
+
+ IfFailRet(m_pSymReader->GetDocuments(cDocs, &cDocsNeeded, NULL));
+ pDocs = new ToRelease<ISymUnmanagedDocument>[cDocsNeeded];
+ cDocs = cDocsNeeded;
+ IfFailRet(m_pSymReader->GetDocuments(cDocs, &cDocsNeeded, &(pDocs[0])));
+
+ ULONG32 filenameLen = (ULONG32) _wcslen(pFilename);
+
+ for (ULONG32 i = 0; i < cDocs; i++)
+ {
+ ULONG32 cchUrl = 0;
+ ULONG32 cchUrlNeeded = 0;
+ ArrayHolder<WCHAR> pUrl = NULL;
+ IfFailRet(pDocs[i]->GetURL(cchUrl, &cchUrlNeeded, pUrl));
+ pUrl = new WCHAR[cchUrlNeeded];
+ cchUrl = cchUrlNeeded;
+ IfFailRet(pDocs[i]->GetURL(cchUrl, &cchUrlNeeded, pUrl));
+
+ // If the URL is exactly as long as the filename then compare the two names directly
+ if (cchUrl-1 == filenameLen)
+ {
+ if (0!=_wcsicmp(pUrl, pFilename))
+ continue;
+ }
+ // does the URL suffix match [back]slash + filename?
+ else if (cchUrl-1 > filenameLen)
+ {
+ WCHAR* slashLocation = pUrl + (cchUrl - filenameLen - 2);
+ if (*slashLocation != L'\\' && *slashLocation != L'/')
+ continue;
+ if (0 != _wcsicmp(slashLocation+1, pFilename))
+ continue;
+ }
+ // URL is too short to match
+ else
+ continue;
+
+ ULONG32 closestLine = 0;
+ if (FAILED(pDocs[i]->FindClosestLine(lineNumber, &closestLine)))
+ continue;
+
+ ToRelease<ISymUnmanagedMethod> pSymUnmanagedMethod;
+ IfFailRet(m_pSymReader->GetMethodFromDocumentPosition(pDocs[i], closestLine, 0, &pSymUnmanagedMethod));
+ IfFailRet(pSymUnmanagedMethod->GetToken(pToken));
+ IfFailRet(pSymUnmanagedMethod->GetOffset(pDocs[i], closestLine, 0, pIlOffset));
+
+ // If this IL
+ if (*pIlOffset == -1)
+ {
+ return E_FAIL;
+ }
+ return S_OK;
+ }
+#endif // FEATURE_PAL
+
+ return E_FAIL;
+}
+
+static void AddAssemblyName(WString& methodOutput, CLRDATA_ADDRESS mdesc)
+{
+ DacpMethodDescData mdescData;
+ if (SUCCEEDED(mdescData.Request(g_sos, mdesc)))
+ {
+ DacpModuleData dmd;
+ if (SUCCEEDED(dmd.Request(g_sos, mdescData.ModulePtr)))
+ {
+ ToRelease<IXCLRDataModule> pModule;
+ if (SUCCEEDED(g_sos->GetModule(mdescData.ModulePtr, &pModule)))
+ {
+ ArrayHolder<WCHAR> wszFileName = new WCHAR[MAX_LONGPATH + 1];
+ ULONG32 nameLen = 0;
+ if (SUCCEEDED(pModule->GetFileName(MAX_LONGPATH, &nameLen, wszFileName)))
+ {
+ if (wszFileName[0] != W('\0'))
+ {
+ WCHAR *pJustName = _wcsrchr(wszFileName, DIRECTORY_SEPARATOR_CHAR_W);
+ if (pJustName == NULL)
+ pJustName = wszFileName - 1;
+ methodOutput += (pJustName + 1);
+ methodOutput += W("!");
+ }
+ }
+ }
+ }
+ }
+}
+
+WString GetFrameFromAddress(TADDR frameAddr, IXCLRDataStackWalk *pStackWalk, BOOL bAssemblyName)
+{
+ TADDR vtAddr;
+ MOVE(vtAddr, frameAddr);
+
+ WString frameOutput;
+ frameOutput += W("[");
+
+ if (SUCCEEDED(g_sos->GetFrameName(TO_CDADDR(vtAddr), mdNameLen, g_mdName, NULL)))
+ frameOutput += g_mdName;
+ else
+ frameOutput += W("Frame");
+
+ frameOutput += WString(W(": ")) + Pointer(frameAddr) + W("] ");
+
+ // Print the frame's associated function info, if it has any.
+ CLRDATA_ADDRESS mdesc = 0;
+ if (SUCCEEDED(g_sos->GetMethodDescPtrFromFrame(frameAddr, &mdesc)))
+ {
+ if (SUCCEEDED(g_sos->GetMethodDescName(mdesc, mdNameLen, g_mdName, NULL)))
+ {
+ if (bAssemblyName)
+ {
+ AddAssemblyName(frameOutput, mdesc);
+ }
+
+ frameOutput += g_mdName;
+ }
+ else
+ {
+ frameOutput += W("<unknown method>");
+ }
+ }
+ else if (pStackWalk)
+ {
+ // The Frame did not have direct function info, so try to get the method instance
+ // (in this case a MethodDesc), and read the name from it.
+ ToRelease<IXCLRDataFrame> frame;
+ if (SUCCEEDED(pStackWalk->GetFrame(&frame)))
+ {
+ ToRelease<IXCLRDataMethodInstance> methodInstance;
+ if (SUCCEEDED(frame->GetMethodInstance(&methodInstance)))
+ {
+ // GetName can return S_FALSE if mdNameLen is not large enough. However we are already
+ // passing a pretty big buffer in. If this returns S_FALSE (meaning the buffer is too
+ // small) then we should not output it anyway.
+ if (methodInstance->GetName(0, mdNameLen, NULL, g_mdName) == S_OK)
+ frameOutput += g_mdName;
+ }
+ }
+ }
+
+ return frameOutput;
+}
+
+WString MethodNameFromIP(CLRDATA_ADDRESS ip, BOOL bSuppressLines, BOOL bAssemblyName, BOOL bDisplacement)
+{
+ ULONG linenum;
+ WString methodOutput;
+ CLRDATA_ADDRESS mdesc = 0;
+
+ if (FAILED(g_sos->GetMethodDescPtrFromIP(ip, &mdesc)))
+ {
+ methodOutput = W("<unknown>");
+ }
+ else
+ {
+ DacpMethodDescData mdescData;
+ if (SUCCEEDED(g_sos->GetMethodDescName(mdesc, mdNameLen, g_mdName, NULL)))
+ {
+ if (bAssemblyName)
+ {
+ AddAssemblyName(methodOutput, mdesc);
+ }
+
+ methodOutput += g_mdName;
+
+ if (bDisplacement)
+ {
+ if (SUCCEEDED(mdescData.Request(g_sos, mdesc)))
+ {
+ ULONG64 disp = (ip - mdescData.NativeCodeAddr);
+ if (disp)
+ {
+ methodOutput += W(" + ");
+ methodOutput += Decimal(disp);
+ }
+ }
+ }
+ }
+ else if (SUCCEEDED(mdescData.Request(g_sos, mdesc)))
+ {
+ DacpModuleData dmd;
+ BOOL bModuleNameWorked = FALSE;
+ ULONG64 addrInModule = ip;
+ if (SUCCEEDED(dmd.Request(g_sos, mdescData.ModulePtr)))
+ {
+ CLRDATA_ADDRESS peFileBase = 0;
+ if (SUCCEEDED(g_sos->GetPEFileBase(dmd.File, &peFileBase)))
+ {
+ if (peFileBase)
+ {
+ addrInModule = peFileBase;
+ }
+ }
+ }
+ ULONG Index;
+ ULONG64 moduleBase;
+ if (SUCCEEDED(g_ExtSymbols->GetModuleByOffset(UL64_TO_CDA(addrInModule), 0, &Index, &moduleBase)))
+ {
+ ArrayHolder<char> szModuleName = new char[MAX_LONGPATH+1];
+
+ if (SUCCEEDED(g_ExtSymbols->GetModuleNames(Index, moduleBase, NULL, 0, NULL, szModuleName, MAX_LONGPATH, NULL, NULL, 0, NULL)))
+ {
+ MultiByteToWideChar (CP_ACP, 0, szModuleName, MAX_LONGPATH, g_mdName, _countof(g_mdName));
+ methodOutput += g_mdName;
+ methodOutput += W("!");
+ }
+ }
+ methodOutput += W("<unknown method>");
+ }
+ else
+ {
+ methodOutput = W("<unknown>");
+ }
+
+ ArrayHolder<WCHAR> wszFileName = new WCHAR[MAX_LONGPATH];
+ if (!bSuppressLines &&
+ SUCCEEDED(GetLineByOffset(TO_CDADDR(ip), &linenum, wszFileName, MAX_LONGPATH)))
+ {
+ methodOutput += WString(W(" [")) + wszFileName + W(" @ ") + Decimal(linenum) + W("]");
+ }
+ }
+
+ return methodOutput;
+}
+
+HRESULT GetGCRefs(ULONG osID, SOSStackRefData **ppRefs, unsigned int *pRefCnt, SOSStackRefError **ppErrors, unsigned int *pErrCount)
+{
+ if (ppRefs == NULL || pRefCnt == NULL)
+ return E_POINTER;
+
+ if (pErrCount)
+ *pErrCount = 0;
+
+ *pRefCnt = 0;
+ unsigned int count = 0;
+ ToRelease<ISOSStackRefEnum> pEnum;
+ if (FAILED(g_sos->GetStackReferences(osID, &pEnum)) || FAILED(pEnum->GetCount(&count)))
+ {
+ ExtOut("Failed to enumerate GC references.\n");
+ return E_FAIL;
+ }
+
+ *ppRefs = new SOSStackRefData[count];
+ if (FAILED(pEnum->Next(count, *ppRefs, pRefCnt)))
+ {
+ ExtOut("Failed to enumerate GC references.\n");
+ return E_FAIL;
+ }
+
+ SOS_Assert(count == *pRefCnt);
+
+ // Enumerate errors found. Any bad HRESULT recieved while enumerating errors is NOT a fatal error.
+ // Hence we return S_FALSE if we encounter one.
+
+ if (ppErrors && pErrCount)
+ {
+ ToRelease<ISOSStackRefErrorEnum> pErrors;
+ if (FAILED(pEnum->EnumerateErrors(&pErrors)))
+ {
+ ExtOut("Failed to enumerate GC reference errors.\n");
+ return S_FALSE;
+ }
+
+ if (FAILED(pErrors->GetCount(&count)))
+ {
+ ExtOut("Failed to enumerate GC reference errors.\n");
+ return S_FALSE;
+ }
+
+ *ppErrors = new SOSStackRefError[count];
+ if (FAILED(pErrors->Next(count, *ppErrors, pErrCount)))
+ {
+ ExtOut("Failed to enumerate GC reference errors.\n");
+ *pErrCount = 0;
+ return S_FALSE;
+ }
+
+ SOS_Assert(count == *pErrCount);
+ }
+ return S_OK;
+}
+
+
+InternalFrameManager::InternalFrameManager() : m_cInternalFramesActual(0), m_iInternalFrameCur(0) {}
+
+HRESULT InternalFrameManager::Init(ICorDebugThread3 * pThread3)
+{
+ _ASSERTE(pThread3 != NULL);
+
+ return pThread3->GetActiveInternalFrames(
+ _countof(m_rgpInternalFrame2),
+ &m_cInternalFramesActual,
+ &(m_rgpInternalFrame2[0]));
+}
+
+HRESULT InternalFrameManager::PrintPrecedingInternalFrames(ICorDebugFrame * pFrame)
+{
+ HRESULT Status;
+
+ for (; m_iInternalFrameCur < m_cInternalFramesActual; m_iInternalFrameCur++)
+ {
+ BOOL bIsCloser = FALSE;
+ IfFailRet(m_rgpInternalFrame2[m_iInternalFrameCur]->IsCloserToLeaf(pFrame, &bIsCloser));
+
+ if (!bIsCloser)
+ {
+ // Current internal frame is now past pFrame, so we're done
+ return S_OK;
+ }
+
+ IfFailRet(PrintCurrentInternalFrame());
+ }
+
+ // Exhausted list of internal frames. Done!
+ return S_OK;
+}
+
+HRESULT InternalFrameManager::PrintCurrentInternalFrame()
+{
+ _ASSERTE(m_iInternalFrameCur < m_cInternalFramesActual);
+
+ HRESULT Status;
+
+ CORDB_ADDRESS address;
+ IfFailRet(m_rgpInternalFrame2[m_iInternalFrameCur]->GetAddress(&address));
+
+ ToRelease<ICorDebugInternalFrame> pInternalFrame;
+ IfFailRet(m_rgpInternalFrame2[m_iInternalFrameCur]->QueryInterface(IID_ICorDebugInternalFrame, (LPVOID *) &pInternalFrame));
+
+ CorDebugInternalFrameType type;
+ IfFailRet(pInternalFrame->GetFrameType(&type));
+
+ LPCSTR szFrameType = NULL;
+ switch(type)
+ {
+ default:
+ szFrameType = "Unknown internal frame.";
+ break;
+
+ case STUBFRAME_M2U:
+ szFrameType = "Managed to Unmanaged transition";
+ break;
+
+ case STUBFRAME_U2M:
+ szFrameType = "Unmanaged to Managed transition";
+ break;
+
+ case STUBFRAME_APPDOMAIN_TRANSITION:
+ szFrameType = "AppDomain transition";
+ break;
+
+ case STUBFRAME_LIGHTWEIGHT_FUNCTION:
+ szFrameType = "Lightweight function";
+ break;
+
+ case STUBFRAME_FUNC_EVAL:
+ szFrameType = "Function evaluation";
+ break;
+
+ case STUBFRAME_INTERNALCALL:
+ szFrameType = "Internal call";
+ break;
+
+ case STUBFRAME_CLASS_INIT:
+ szFrameType = "Class initialization";
+ break;
+
+ case STUBFRAME_EXCEPTION:
+ szFrameType = "Exception";
+ break;
+
+ case STUBFRAME_SECURITY:
+ szFrameType = "Security";
+ break;
+
+ case STUBFRAME_JIT_COMPILATION:
+ szFrameType = "JIT Compilation";
+ break;
+ }
+
+ DMLOut("%p %s ", SOS_PTR(address), SOS_PTR(0));
+ ExtOut("[%s: %p]\n", szFrameType, SOS_PTR(address));
+
+ return S_OK;
+}
diff --git a/src/ToolBox/SOS/Strike/util.h b/src/ToolBox/SOS/Strike/util.h
new file mode 100644
index 0000000000..f444c9fcb2
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/util.h
@@ -0,0 +1,3292 @@
+// 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.
+
+// ==++==
+//
+
+//
+// ==--==
+#ifndef __util_h__
+#define __util_h__
+
+#define LIMITED_METHOD_CONTRACT
+
+// So we can use the PAL_TRY_NAKED family of macros without dependencies on utilcode.
+inline void RestoreSOToleranceState() {}
+
+#include <cor.h>
+#include <corsym.h>
+#include <clrdata.h>
+#include <palclr.h>
+#include <metahost.h>
+#include <new>
+
+#if !defined(FEATURE_PAL)
+#include <dia2.h>
+#endif
+
+#ifdef STRIKE
+#if defined(_MSC_VER)
+#pragma warning(disable:4200)
+#pragma warning(default:4200)
+#endif
+#include "data.h"
+#endif //STRIKE
+
+#include "cordebug.h"
+#include "static_assert.h"
+
+typedef LPCSTR LPCUTF8;
+typedef LPSTR LPUTF8;
+
+DECLARE_HANDLE(OBJECTHANDLE);
+
+struct IMDInternalImport;
+
+#if defined(_TARGET_WIN64_)
+#define WIN64_8SPACES ""
+#define WIN86_8SPACES " "
+#define POINTERSIZE "16"
+#define POINTERSIZE_HEX 16
+#define POINTERSIZE_BYTES 8
+#define POINTERSIZE_TYPE "I64"
+#else
+#define WIN64_8SPACES " "
+#define WIN86_8SPACES ""
+#define POINTERSIZE "8"
+#define POINTERSIZE_HEX 8
+#define POINTERSIZE_BYTES 4
+#define POINTERSIZE_TYPE "I32"
+#endif
+
+#if defined(_MSC_VER)
+#pragma warning(disable:4510 4512 4610)
+#endif
+
+#ifndef _ASSERTE
+#ifdef _DEBUG
+#define _ASSERTE(expr) \
+ do { if (!(expr) ) { ExtErr("_ASSERTE fired:\n\t%s\n", #expr); if (IsDebuggerPresent()) DebugBreak(); } } while (0)
+#else
+#define _ASSERTE(x)
+#endif
+#endif // ASSERTE
+
+#ifdef _DEBUG
+#define ASSERT_CHECK(expr, msg, reason) \
+ do { if (!(expr) ) { ExtOut(reason); ExtOut(msg); ExtOut(#expr); DebugBreak(); } } while (0)
+#endif
+
+// PREFIX macros - Begin
+
+// SOS does not have support for Contracts. Therefore we needed to duplicate
+// some of the PREFIX infrastructure from inc\check.h in here.
+
+// Issue - PREFast_:510 v4.51 does not support __assume(0)
+#if (defined(_MSC_VER) && !defined(_PREFAST_)) || defined(_PREFIX_)
+#if defined(_AMD64_)
+// Empty methods that consist of UNREACHABLE() result in a zero-sized declspec(noreturn) method
+// which causes the pdb file to make the next method declspec(noreturn) as well, thus breaking BBT
+// Remove when we get a VC compiler that fixes VSW 449170
+# define __UNREACHABLE() DebugBreak(); __assume(0);
+#else
+# define __UNREACHABLE() __assume(0)
+#endif
+#else
+#define __UNREACHABLE() do { } while(true)
+#endif
+
+
+#if defined(_PREFAST_) || defined(_PREFIX_)
+#define COMPILER_ASSUME_MSG(_condition, _message) if (!(_condition)) __UNREACHABLE();
+#else
+
+#if defined(DACCESS_COMPILE)
+#define COMPILER_ASSUME_MSG(_condition, _message) do { } while (0)
+#else
+
+#if defined(_DEBUG)
+#define COMPILER_ASSUME_MSG(_condition, _message) \
+ ASSERT_CHECK(_condition, _message, "Compiler optimization assumption invalid")
+#else
+#define COMPILER_ASSUME_MSG(_condition, _message) __assume(_condition)
+#endif // _DEBUG
+
+#endif // DACCESS_COMPILE
+
+#endif // _PREFAST_ || _PREFIX_
+
+#define PREFIX_ASSUME(_condition) \
+ COMPILER_ASSUME_MSG(_condition, "")
+
+// PREFIX macros - End
+
+class MethodTable;
+
+#define MD_NOT_YET_LOADED ((DWORD_PTR)-1)
+/*
+ * HANDLES
+ *
+ * The default type of handle is a strong handle.
+ *
+ */
+#define HNDTYPE_DEFAULT HNDTYPE_STRONG
+#define HNDTYPE_WEAK_DEFAULT HNDTYPE_WEAK_LONG
+#define HNDTYPE_WEAK_SHORT (0)
+#define HNDTYPE_WEAK_LONG (1)
+#define HNDTYPE_STRONG (2)
+#define HNDTYPE_PINNED (3)
+#define HNDTYPE_VARIABLE (4)
+#define HNDTYPE_REFCOUNTED (5)
+#define HNDTYPE_DEPENDENT (6)
+#define HNDTYPE_ASYNCPINNED (7)
+#define HNDTYPE_SIZEDREF (8)
+#define HNDTYPE_WEAK_WINRT (9)
+
+// Anything above this we consider abnormal and stop processing heap information
+const int nMaxHeapSegmentCount = 1000;
+
+class BaseObject
+{
+ MethodTable *m_pMethTab;
+};
+
+
+const BYTE gElementTypeInfo[] = {
+#define TYPEINFO(e,ns,c,s,g,ia,ip,if,im,gv) s,
+#include "cortypeinfo.h"
+#undef TYPEINFO
+};
+
+typedef struct tagLockEntry
+{
+ tagLockEntry *pNext; // next entry
+ tagLockEntry *pPrev; // prev entry
+ DWORD dwULockID;
+ DWORD dwLLockID; // owning lock
+ WORD wReaderLevel; // reader nesting level
+} LockEntry;
+
+#define MAX_CLASSNAME_LENGTH 1024
+
+enum EEFLAVOR {UNKNOWNEE, MSCOREE, MSCORWKS, MSCOREND};
+
+#include "sospriv.h"
+extern IXCLRDataProcess *g_clrData;
+extern ISOSDacInterface *g_sos;
+
+#include "dacprivate.h"
+
+interface ICorDebugProcess;
+extern ICorDebugProcess * g_pCorDebugProcess;
+
+// This class is templated for easy modification. We may need to update the CachedString
+// or related classes to use WCHAR instead of char in the future.
+template <class T, int count, int size>
+class StaticData
+{
+public:
+ StaticData()
+ {
+ for (int i = 0; i < count; ++i)
+ InUse[i] = false;
+ }
+
+ // Whether the individual data pointers in the cache are in use.
+ bool InUse[count];
+
+ // The actual data itself.
+ T Data[count][size];
+
+ // The number of arrays in the cache.
+ static const int Count;
+
+ // The size of each individual array.
+ static const int Size;
+};
+
+class CachedString
+{
+public:
+ CachedString();
+ CachedString(const CachedString &str);
+ ~CachedString();
+
+ const CachedString &operator=(const CachedString &str);
+
+ // Returns the capacity of this string.
+ size_t GetStrLen() const
+ {
+ return mSize;
+ }
+
+ // Returns a mutable character pointer. Be sure not to write past the
+ // length of this string.
+ inline operator char *()
+ {
+ return mPtr;
+ }
+
+ // Returns a const char representation of this string.
+ inline operator const char *() const
+ {
+ return GetPtr();
+ }
+
+ // To ensure no AV's, any time a constant pointer is requested, we will
+ // return an empty string "" if we hit an OOM. This will only happen
+ // if we hit an OOM and do not check for it before using the string.
+ // If you request a non-const char pointer out of this class, it may be
+ // null (see operator char *).
+ inline const char *GetPtr() const
+ {
+ if (!mPtr || IsOOM())
+ return "";
+
+ return mPtr;
+ }
+
+ // Returns true if we ran out of memory trying to allocate the string
+ // or the refcount.
+ bool IsOOM() const
+ {
+ return mIndex == -2;
+ }
+
+ // allocate a string of the specified size. this will Clear() any
+ // previously allocated string. call IsOOM() to check for failure.
+ void Allocate(int size);
+
+private:
+ // Copies rhs into this string.
+ void Copy(const CachedString &rhs);
+
+ // Clears this string, releasing any underlying memory.
+ void Clear();
+
+ // Creates a new string.
+ void Create();
+
+ // Sets an out of memory state.
+ void SetOOM();
+
+private:
+ char *mPtr;
+
+ // The reference count. This may be null if there is only one copy
+ // of this string.
+ mutable unsigned int *mRefCount;
+
+ // mIndex contains the index of the cached pointer we are using, or:
+ // ~0 - poison value we initialize it to for debugging purposes
+ // -1 - mPtr points to a pointer we have new'ed
+ // -2 - We hit an oom trying to allocate either mCount or mPtr
+ int mIndex;
+
+ // contains the size of current string
+ int mSize;
+
+private:
+ static StaticData<char, 4, 1024> cache;
+};
+
+// Things in this namespace should not be directly accessed/called outside of
+// the output-related functions.
+namespace Output
+{
+ extern unsigned int g_bSuppressOutput;
+ extern unsigned int g_Indent;
+ extern unsigned int g_DMLEnable;
+ extern bool g_bDbgOutput;
+ extern bool g_bDMLExposed;
+
+ inline bool IsOutputSuppressed()
+ { return g_bSuppressOutput > 0; }
+
+ inline void ResetIndent()
+ { g_Indent = 0; }
+
+ inline void SetDebugOutputEnabled(bool enabled)
+ { g_bDbgOutput = enabled; }
+
+ inline bool IsDebugOutputEnabled()
+ { return g_bDbgOutput; }
+
+ inline void SetDMLExposed(bool exposed)
+ { g_bDMLExposed = exposed; }
+
+ inline bool IsDMLExposed()
+ { return g_bDMLExposed; }
+
+ enum FormatType
+ {
+ DML_None,
+ DML_MethodTable,
+ DML_MethodDesc,
+ DML_EEClass,
+ DML_Module,
+ DML_IP,
+ DML_Object,
+ DML_Domain,
+ DML_Assembly,
+ DML_ThreadID,
+ DML_ValueClass,
+ DML_DumpHeapMT,
+ DML_ListNearObj,
+ DML_ThreadState,
+ DML_PrintException,
+ DML_RCWrapper,
+ DML_CCWrapper,
+ DML_ManagedVar,
+ };
+
+ /**********************************************************************\
+ * This function builds a DML string for a ValueClass. If DML is *
+ * enabled, this function returns a DML string based on the format *
+ * type. Otherwise this returns a string containing only the hex value *
+ * of addr. *
+ * *
+ * Params: *
+ * mt - the method table of the ValueClass *
+ * addr - the address of the ValueClass *
+ * type - the format type to use to output this object *
+ * fill - whether or not to pad the hex value with zeros *
+ * *
+ \**********************************************************************/
+ CachedString BuildVCValue(CLRDATA_ADDRESS mt, CLRDATA_ADDRESS addr, FormatType type, bool fill = true);
+
+
+ /**********************************************************************\
+ * This function builds a DML string for an object. If DML is enabled, *
+ * this function returns a DML string based on the format type. *
+ * Otherwise this returns a string containing only the hex value of *
+ * addr. *
+ * *
+ * Params: *
+ * addr - the address of the object *
+ * type - the format type to use to output this object *
+ * fill - whether or not to pad the hex value with zeros *
+ * *
+ \**********************************************************************/
+ CachedString BuildHexValue(CLRDATA_ADDRESS addr, FormatType type, bool fill = true);
+
+ /**********************************************************************\
+ * This function builds a DML string for an managed variable name. *
+ * If DML is enabled, this function returns a DML string that will *
+ * enable the expansion of that managed variable using the !ClrStack *
+ * command to display the variable's fields, otherwise it will just *
+ * return the variable's name as a string.
+ * *
+ * Params: *
+ * expansionName - the current variable expansion string *
+ * frame - the frame that contains the variable of interest *
+ * simpleName - simple name of the managed variable *
+ * *
+ \**********************************************************************/
+ CachedString BuildManagedVarValue(__in_z LPCWSTR expansionName, ULONG frame, __in_z LPCWSTR simpleName, FormatType type);
+ CachedString BuildManagedVarValue(__in_z LPCWSTR expansionName, ULONG frame, int indexInArray, FormatType type); //used for array indices (simpleName = "[<indexInArray>]")
+}
+
+class NoOutputHolder
+{
+public:
+ NoOutputHolder(BOOL bSuppress = TRUE);
+ ~NoOutputHolder();
+
+private:
+ BOOL mSuppress;
+};
+
+class EnableDMLHolder
+{
+public:
+ EnableDMLHolder(BOOL enable);
+ ~EnableDMLHolder();
+
+private:
+ BOOL mEnable;
+};
+
+size_t CountHexCharacters(CLRDATA_ADDRESS val);
+
+// Normal output.
+void DMLOut(PCSTR format, ...); /* Prints out DML strings. */
+void IfDMLOut(PCSTR format, ...); /* Prints given DML string ONLY if DML is enabled; prints nothing otherwise. */
+void ExtOut(PCSTR Format, ...); /* Prints out to ExtOut (no DML). */
+void ExtWarn(PCSTR Format, ...); /* Prints out to ExtWarn (no DML). */
+void ExtErr(PCSTR Format, ...); /* Prints out to ExtErr (no DML). */
+void ExtDbgOut(PCSTR Format, ...); /* Prints out to ExtOut in a checked build (no DML). */
+void WhitespaceOut(int count); /* Prints out "count" number of spaces in the output. */
+
+// Change indent for ExtOut
+inline void IncrementIndent() { Output::g_Indent++; }
+inline void DecrementIndent() { if (Output::g_Indent > 0) Output::g_Indent--; }
+inline void ExtOutIndent() { WhitespaceOut(Output::g_Indent << 2); }
+
+// DML Generation Methods
+#define DMLListNearObj(addr) Output::BuildHexValue(addr, Output::DML_ListNearObj).GetPtr()
+#define DMLDumpHeapMT(addr) Output::BuildHexValue(addr, Output::DML_DumpHeapMT).GetPtr()
+#define DMLMethodTable(addr) Output::BuildHexValue(addr, Output::DML_MethodTable).GetPtr()
+#define DMLMethodDesc(addr) Output::BuildHexValue(addr, Output::DML_MethodDesc).GetPtr()
+#define DMLClass(addr) Output::BuildHexValue(addr, Output::DML_EEClass).GetPtr()
+#define DMLModule(addr) Output::BuildHexValue(addr, Output::DML_Module).GetPtr()
+#define DMLIP(ip) Output::BuildHexValue(ip, Output::DML_IP).GetPtr()
+#define DMLObject(addr) Output::BuildHexValue(addr, Output::DML_Object).GetPtr()
+#define DMLDomain(addr) Output::BuildHexValue(addr, Output::DML_Domain).GetPtr()
+#define DMLAssembly(addr) Output::BuildHexValue(addr, Output::DML_Assembly).GetPtr()
+#define DMLThreadID(id) Output::BuildHexValue(id, Output::DML_ThreadID, false).GetPtr()
+#define DMLValueClass(mt, addr) Output::BuildVCValue(mt, addr, Output::DML_ValueClass).GetPtr()
+#define DMLRCWrapper(addr) Output::BuildHexValue(addr, Output::DML_RCWrapper).GetPtr()
+#define DMLCCWrapper(addr) Output::BuildHexValue(addr, Output::DML_CCWrapper).GetPtr()
+#define DMLManagedVar(expansionName,frame,simpleName) Output::BuildManagedVarValue(expansionName, frame, simpleName, Output::DML_ManagedVar).GetPtr()
+
+bool IsDMLEnabled();
+
+
+#ifndef SOS_Assert
+#define SOS_Assert(x)
+#endif
+
+void ConvertToLower(__out_ecount(len) char *buffer, size_t len);
+
+extern const char * const DMLFormats[];
+int GetHex(CLRDATA_ADDRESS addr, __out_ecount(len) char *out, size_t len, bool fill);
+
+// A simple string class for mutable strings. We cannot use STL, so this is a stand in replacement
+// for std::string (though it doesn't use the same interface).
+template <class T, size_t (__cdecl *LEN)(const T *), errno_t (__cdecl *COPY)(T *, size_t, const T * _Src)>
+class BaseString
+{
+public:
+ BaseString()
+ : mStr(0), mSize(0), mLength(0)
+ {
+ const size_t size = 64;
+
+ mStr = new T[size];
+ mSize = size;
+ mStr[0] = 0;
+ }
+
+ BaseString(const T *str)
+ : mStr(0), mSize(0), mLength(0)
+ {
+ CopyFrom(str, LEN(str));
+ }
+
+ BaseString(const BaseString<T, LEN, COPY> &rhs)
+ : mStr(0), mSize(0), mLength(0)
+ {
+ *this = rhs;
+ }
+
+ ~BaseString()
+ {
+ Clear();
+ }
+
+ const BaseString<T, LEN, COPY> &operator=(const BaseString<T, LEN, COPY> &rhs)
+ {
+ Clear();
+ CopyFrom(rhs.mStr, rhs.mLength);
+ return *this;
+ }
+
+ const BaseString<T, LEN, COPY> &operator=(const T *str)
+ {
+ Clear();
+ CopyFrom(str, LEN(str));
+ return *this;
+ }
+
+ const BaseString<T, LEN, COPY> &operator +=(const T *str)
+ {
+ size_t len = LEN(str);
+ CopyFrom(str, len);
+ return *this;
+ }
+
+ const BaseString<T, LEN, COPY> &operator +=(const BaseString<T, LEN, COPY> &str)
+ {
+ CopyFrom(str.mStr, str.mLength);
+ return *this;
+ }
+
+ BaseString<T, LEN, COPY> operator+(const T *str) const
+ {
+ return BaseString<T, LEN, COPY>(mStr, mLength, str, LEN(str));
+ }
+
+ BaseString<T, LEN, COPY> operator+(const BaseString<T, LEN, COPY> &str) const
+ {
+ return BaseString<T, LEN, COPY>(mStr, mLength, str.mStr, str.mLength);
+ }
+
+ operator const T *() const
+ {
+ return mStr;
+ }
+
+ const T *c_str() const
+ {
+ return mStr;
+ }
+
+ size_t GetLength() const
+ {
+ return mLength;
+ }
+
+private:
+ BaseString(const T * str1, size_t len1, const T * str2, size_t len2)
+ : mStr(0), mSize(0), mLength(0)
+ {
+ const size_t size = len1 + len2 + 1 + ((len1 + len2) >> 1);
+ mStr = new T[size];
+ mSize = size;
+
+ CopyFrom(str1, len1);
+ CopyFrom(str2, len2);
+ }
+
+ void Clear()
+ {
+ mLength = 0;
+ mSize = 0;
+ if (mStr)
+ {
+ delete [] mStr;
+ mStr = 0;
+ }
+ }
+
+ void CopyFrom(const T *str, size_t len)
+ {
+ if (mLength + len + 1 >= mSize)
+ Resize(mLength + len + 1);
+
+ COPY(mStr+mLength, mSize-mLength, str);
+ mLength += len;
+ }
+
+ void Resize(size_t size)
+ {
+ /* We always resize at least one half bigger than we need. When CopyFrom requests a resize
+ * it asks for the exact size that's needed to concatenate strings. However in practice
+ * it's common to add multiple strings together in a row, e.g.:
+ * String foo = "One " + "Two " + "Three " + "Four " + "\n";
+ * Ensuring the size of the string is bigger than we need, and that the minimum size is 64,
+ * we will cut down on a lot of needless resizes at the cost of a few bytes wasted in some
+ * cases.
+ */
+ size += size >> 1;
+ if (size < 64)
+ size = 64;
+
+ T *newStr = new T[size];
+
+ if (mStr)
+ {
+ COPY(newStr, size, mStr);
+ delete [] mStr;
+ }
+ else
+ {
+ newStr[0] = 0;
+ }
+
+ mStr = newStr;
+ mSize = size;
+ }
+private:
+ T *mStr;
+ size_t mSize, mLength;
+};
+
+typedef BaseString<char, strlen, strcpy_s> String;
+typedef BaseString<WCHAR, _wcslen, wcscpy_s> WString;
+
+
+template<class T>
+void Flatten(__out_ecount(len) T *data, unsigned int len)
+{
+ for (unsigned int i = 0; i < len; ++i)
+ if (data[i] < 32 || (data[i] > 126 && data[i] <= 255))
+ data[i] = '.';
+ data[len] = 0;
+}
+
+void Flatten(__out_ecount(len) char *data, unsigned int len);
+
+/* Formats for the Format class. We support the following formats:
+ * Pointer - Same as %p.
+ * Hex - Same as %x (same as %p, but does not output preceding zeros.
+ * PrefixHex - Same as %x, but prepends 0x.
+ * Decimal - Same as %d.
+ * Strings and wide strings don't use this.
+ */
+class Formats
+{
+public:
+ enum Format
+ {
+ Default,
+ Pointer,
+ Hex,
+ PrefixHex,
+ Decimal,
+ };
+};
+
+enum Alignment
+{
+ AlignLeft,
+ AlignRight
+};
+
+namespace Output
+{
+ /* Defines how a value will be printed. This class understands how to format
+ * and print values according to the format and DML settings provided.
+ * The raw templated class handles the pointer/integer case. Support for
+ * character arrays and wide character arrays are handled by template
+ * specializations.
+ *
+ * Note that this class is not used directly. Instead use the typedefs and
+ * macros which define the type of data you are outputing (for example ObjectPtr,
+ * MethodTablePtr, etc).
+ */
+ template <class T>
+ class Format
+ {
+ public:
+ Format(T value)
+ : mValue(value), mFormat(Formats::Default), mDml(Output::DML_None)
+ {
+ }
+
+ Format(T value, Formats::Format format, Output::FormatType dmlType)
+ : mValue(value), mFormat(format), mDml(dmlType)
+ {
+ }
+
+ Format(const Format<T> &rhs)
+ : mValue(rhs.mValue), mFormat(rhs.mFormat), mDml(rhs.mDml)
+ {
+ }
+
+ /* Prints out the value according to the Format and DML settings provided.
+ */
+ void Output() const
+ {
+ if (IsDMLEnabled() && mDml != Output::DML_None)
+ {
+ const int len = GetDMLWidth(mDml);
+ char *buffer = (char*)alloca(len);
+
+ BuildDML(buffer, len, (CLRDATA_ADDRESS)mValue, mFormat, mDml);
+ DMLOut(buffer);
+ }
+ else
+ {
+ if (mFormat == Formats::Default || mFormat == Formats::Pointer)
+ {
+ ExtOut("%p", SOS_PTR(mValue));
+ }
+ else
+ {
+ const char *format = NULL;
+ if (mFormat == Formats::PrefixHex)
+ {
+ format = "0x%x";
+ }
+ else if (mFormat == Formats::Hex)
+ {
+ format = "%x";
+ }
+ else if (mFormat == Formats::Decimal)
+ {
+ format = "%d";
+ }
+
+ ExtOut(format, (__int32)mValue);
+ }
+
+ }
+ }
+
+ /* Prints out the value based on a specified width and alignment.
+ * Params:
+ * align - Whether the output should be left or right justified.
+ * width - The output width to fill.
+ * Note:
+ * This function guarantees that exactly width will be printed out (so if width is 24,
+ * exactly 24 characters will be printed), even if the output wouldn't normally fit
+ * in the space provided. This function makes no guarantees as to what part of the
+ * data will be printed in the case that width isn't wide enough.
+ */
+ void OutputColumn(Alignment align, int width) const
+ {
+ bool leftAlign = align == AlignLeft;
+ if (IsDMLEnabled() && mDml != Output::DML_None)
+ {
+ const int len = GetDMLColWidth(mDml, width);
+ char *buffer = (char*)alloca(len);
+
+ BuildDMLCol(buffer, len, (CLRDATA_ADDRESS)mValue, mFormat, mDml, leftAlign, width);
+ DMLOut(buffer);
+ }
+ else
+ {
+ int precision = GetPrecision();
+ if (mFormat == Formats::Default || mFormat == Formats::Pointer)
+ {
+ if (precision > width)
+ precision = width;
+
+ ExtOut(leftAlign ? "%-*.*p" : "%*.*p", width, precision, SOS_PTR(mValue));
+ }
+ else
+ {
+ const char *format = NULL;
+ if (mFormat == Formats::PrefixHex)
+ {
+ format = leftAlign ? "0x%-*.*x" : "0x%*.*x";
+ width -= 2;
+ }
+ else if (mFormat == Formats::Hex)
+ {
+ format = leftAlign ? "%-*.*x" : "%*.*x";
+ }
+ else if (mFormat == Formats::Decimal)
+ {
+ format = leftAlign ? "%-*.*d" : "%*.*d";
+ }
+
+ if (precision > width)
+ precision = width;
+
+ ExtOut(format, width, precision, (__int32)mValue);
+ }
+ }
+ }
+
+ /* Converts this object into a Wide char string. This allows you to write the following code:
+ * WString foo = L"bar " + ObjectPtr(obj);
+ * Where ObjectPtr is a subclass/typedef of this Format class.
+ */
+ operator WString() const
+ {
+ String str = *this;
+ const char *cstr = (const char *)str;
+
+ int len = MultiByteToWideChar(CP_ACP, 0, cstr, -1, NULL, 0);
+ WCHAR *buffer = (WCHAR *)alloca(len*sizeof(WCHAR));
+
+ MultiByteToWideChar(CP_ACP, 0, cstr, -1, buffer, len);
+
+ return WString(buffer);
+ }
+
+ /* Converts this object into a String object. This allows you to write the following code:
+ * String foo = "bar " + ObjectPtr(obj);
+ * Where ObjectPtr is a subclass/typedef of this Format class.
+ */
+ operator String() const
+ {
+ if (IsDMLEnabled() && mDml != Output::DML_None)
+ {
+ const int len = GetDMLColWidth(mDml, 0);
+ char *buffer = (char*)alloca(len);
+
+ BuildDMLCol(buffer, len, (CLRDATA_ADDRESS)mValue, mFormat, mDml, false, 0);
+ return buffer;
+ }
+ else
+ {
+ char buffer[64];
+ if (mFormat == Formats::Default || mFormat == Formats::Pointer)
+ {
+ sprintf_s(buffer, _countof(buffer), "%p", (int *)(SIZE_T)mValue);
+ ConvertToLower(buffer, _countof(buffer));
+ }
+ else
+ {
+ const char *format = NULL;
+ if (mFormat == Formats::PrefixHex)
+ format = "0x%x";
+ else if (mFormat == Formats::Hex)
+ format = "%x";
+ else if (mFormat == Formats::Decimal)
+ format = "%d";
+
+ sprintf_s(buffer, _countof(buffer), format, (__int32)mValue);
+ ConvertToLower(buffer, _countof(buffer));
+ }
+
+ return buffer;
+ }
+ }
+
+ private:
+ int GetPrecision() const
+ {
+ if (mFormat == Formats::Hex || mFormat == Formats::PrefixHex)
+ {
+ ULONGLONG val = mValue;
+ int count = 0;
+ while (val)
+ {
+ val >>= 4;
+ count++;
+ }
+
+ if (count == 0)
+ count = 1;
+
+ return count;
+ }
+
+ else if (mFormat == Formats::Decimal)
+ {
+ T val = mValue;
+ int count = (val > 0) ? 0 : 1;
+ while (val)
+ {
+ val /= 10;
+ count++;
+ }
+
+ return count;
+ }
+
+ // mFormat == Formats::Pointer
+ return sizeof(int*)*2;
+ }
+
+ static inline void BuildDML(__out_ecount(len) char *result, int len, CLRDATA_ADDRESS value, Formats::Format format, Output::FormatType dmlType)
+ {
+ BuildDMLCol(result, len, value, format, dmlType, true, 0);
+ }
+
+ static int GetDMLWidth(Output::FormatType dmlType)
+ {
+ return GetDMLColWidth(dmlType, 0);
+ }
+
+ static void BuildDMLCol(__out_ecount(len) char *result, int len, CLRDATA_ADDRESS value, Formats::Format format, Output::FormatType dmlType, bool leftAlign, int width)
+ {
+ char hex[64];
+ int count = GetHex(value, hex, _countof(hex), format != Formats::Hex);
+ int i = 0;
+
+ if (!leftAlign)
+ {
+ for (; i < width - count; ++i)
+ result[i] = ' ';
+
+ result[i] = 0;
+ }
+
+ int written = sprintf_s(result+i, len - i, DMLFormats[dmlType], hex, hex);
+
+ SOS_Assert(written != -1);
+ if (written != -1)
+ {
+ for (i = i + written; i < width; ++i)
+ result[i] = ' ';
+
+ result[i] = 0;
+ }
+ }
+
+ static int GetDMLColWidth(Output::FormatType dmlType, int width)
+ {
+ return 1 + 4*sizeof(int*) + (int)strlen(DMLFormats[dmlType]) + width;
+ }
+
+ private:
+ T mValue;
+ Formats::Format mFormat;
+ Output::FormatType mDml;
+ };
+
+ /* Format class used for strings.
+ */
+ template <>
+ class Format<const char *>
+ {
+ public:
+ Format(const char *value)
+ : mValue(value)
+ {
+ }
+
+ Format(const Format<const char *> &rhs)
+ : mValue(rhs.mValue)
+ {
+ }
+
+ void Output() const
+ {
+ if (IsDMLEnabled())
+ DMLOut("%s", mValue);
+ else
+ ExtOut("%s", mValue);
+ }
+
+ void OutputColumn(Alignment align, int width) const
+ {
+ int precision = (int)strlen(mValue);
+
+ if (precision > width)
+ precision = width;
+
+ const char *format = align == AlignLeft ? "%-*.*s" : "%*.*s";
+
+ if (IsDMLEnabled())
+ DMLOut(format, width, precision, mValue);
+ else
+ ExtOut(format, width, precision, mValue);
+ }
+
+ private:
+ const char *mValue;
+ };
+
+ /* Format class for wide char strings.
+ */
+ template <>
+ class Format<const WCHAR *>
+ {
+ public:
+ Format(const WCHAR *value)
+ : mValue(value)
+ {
+ }
+
+ Format(const Format<const WCHAR *> &rhs)
+ : mValue(rhs.mValue)
+ {
+ }
+
+ void Output() const
+ {
+ if (IsDMLEnabled())
+ DMLOut("%S", mValue);
+ else
+ ExtOut("%S", mValue);
+ }
+
+ void OutputColumn(Alignment align, int width) const
+ {
+ int precision = (int)_wcslen(mValue);
+ if (precision > width)
+ precision = width;
+
+ const char *format = align == AlignLeft ? "%-*.*S" : "%*.*S";
+
+ if (IsDMLEnabled())
+ DMLOut(format, width, precision, mValue);
+ else
+ ExtOut(format, width, precision, mValue);
+ }
+
+ private:
+ const WCHAR *mValue;
+ };
+
+
+ template <class T>
+ void InternalPrint(const T &t)
+ {
+ Format<T>(t).Output();
+ }
+
+ template <class T>
+ void InternalPrint(const Format<T> &t)
+ {
+ t.Output();
+ }
+
+ inline void InternalPrint(const char t[])
+ {
+ Format<const char *>(t).Output();
+ }
+}
+
+#define DefineFormatClass(name, format, dml) \
+ template <class T> \
+ Output::Format<T> name(T value) \
+ { return Output::Format<T>(value, format, dml); }
+
+DefineFormatClass(EEClassPtr, Formats::Pointer, Output::DML_EEClass);
+DefineFormatClass(ObjectPtr, Formats::Pointer, Output::DML_Object);
+DefineFormatClass(ExceptionPtr, Formats::Pointer, Output::DML_PrintException);
+DefineFormatClass(ModulePtr, Formats::Pointer, Output::DML_Module);
+DefineFormatClass(MethodDescPtr, Formats::Pointer, Output::DML_MethodDesc);
+DefineFormatClass(AppDomainPtr, Formats::Pointer, Output::DML_Domain);
+DefineFormatClass(ThreadState, Formats::Hex, Output::DML_ThreadState);
+DefineFormatClass(ThreadID, Formats::Hex, Output::DML_ThreadID);
+DefineFormatClass(RCWrapper, Formats::Pointer, Output::DML_RCWrapper);
+DefineFormatClass(CCWrapper, Formats::Pointer, Output::DML_CCWrapper);
+DefineFormatClass(InstructionPtr, Formats::Pointer, Output::DML_IP);
+DefineFormatClass(NativePtr, Formats::Pointer, Output::DML_None);
+
+DefineFormatClass(Decimal, Formats::Decimal, Output::DML_None);
+DefineFormatClass(Pointer, Formats::Pointer, Output::DML_None);
+DefineFormatClass(PrefixHex, Formats::PrefixHex, Output::DML_None);
+DefineFormatClass(Hex, Formats::Hex, Output::DML_None);
+
+#undef DefineFormatClass
+
+template <class T0>
+void Print(const T0 &val0)
+{
+ Output::InternalPrint(val0);
+}
+
+template <class T0, class T1>
+void Print(const T0 &val0, const T1 &val1)
+{
+ Output::InternalPrint(val0);
+ Output::InternalPrint(val1);
+}
+
+template <class T0>
+void PrintLn(const T0 &val0)
+{
+ Output::InternalPrint(val0);
+ ExtOut("\n");
+}
+
+template <class T0, class T1>
+void PrintLn(const T0 &val0, const T1 &val1)
+{
+ Output::InternalPrint(val0);
+ Output::InternalPrint(val1);
+ ExtOut("\n");
+}
+
+template <class T0, class T1, class T2>
+void PrintLn(const T0 &val0, const T1 &val1, const T2 &val2)
+{
+ Output::InternalPrint(val0);
+ Output::InternalPrint(val1);
+ Output::InternalPrint(val2);
+ ExtOut("\n");
+}
+
+
+/* This class handles the formatting for output which is in a table format. To use this class you define
+ * how the table is formatted by setting the number of columns in the table, the default column width,
+ * the default column alignment, the indentation (whitespace) for the table, and the amount of padding
+ * (whitespace) between each column. Once this has been setup, you output rows at a time or individual
+ * columns to build the output instead of manually tabbing out space.
+ * Also note that this class was built to work with the Format class. When outputing data, use the
+ * predefined output types to specify the format (such as ObjectPtr, MethodDescPtr, Decimal, etc). This
+ * tells the TableOutput class how to display the data, and where applicable, it automatically generates
+ * the appropriate DML output. See the DefineFormatClass macro.
+ */
+class TableOutput
+{
+public:
+
+ TableOutput()
+ : mColumns(0), mDefaultWidth(0), mIndent(0), mPadding(0), mCurrCol(0), mDefaultAlign(AlignLeft),
+ mWidths(0), mAlignments(0)
+ {
+ }
+ /* Constructor.
+ * Params:
+ * numColumns - the number of columns the table has
+ * defaultColumnWidth - the default width of each column
+ * alignmentDefault - whether columns are by default left aligned or right aligned
+ * indent - the amount of whitespace to prefix at the start of the row (in characters)
+ * padding - the amount of whitespace to place between each column (in characters)
+ */
+ TableOutput(int numColumns, int defaultColumnWidth, Alignment alignmentDefault = AlignLeft, int indent = 0, int padding = 1)
+ : mColumns(numColumns), mDefaultWidth(defaultColumnWidth), mIndent(indent), mPadding(padding), mCurrCol(0), mDefaultAlign(alignmentDefault),
+ mWidths(0), mAlignments(0)
+ {
+ }
+
+ ~TableOutput()
+ {
+ Clear();
+ }
+
+ /* See the documentation for the constructor.
+ */
+ void ReInit(int numColumns, int defaultColumnWidth, Alignment alignmentDefault = AlignLeft, int indent = 0, int padding = 1);
+
+ /* Sets the amount of whitespace to prefix at the start of the row (in characters).
+ */
+ void SetIndent(int indent)
+ {
+ SOS_Assert(indent >= 0);
+
+ mIndent = indent;
+ }
+
+ /* Sets the exact widths for the the given columns.
+ * Params:
+ * columns - the number of columns you are providing the width for, starting at the first column
+ * ... - an int32 for each column (given by the number of columns in the first parameter).
+ * Example:
+ * If you have 5 columns in the table, you can set their widths like so:
+ * tableOutput.SetWidths(5, 2, 3, 5, 7, 13);
+ * Note:
+ * It's fine to pass a value for "columns" less than the number of columns in the table. This
+ * is useful when you set the default column width to be correct for most of the table, and need
+ * to make a minor adjustment to a few.
+ */
+ void SetWidths(int columns, ...);
+
+ /* Individually sets a column to the given width.
+ * Params:
+ * col - the column to set, 0 indexed
+ * width - the width of the column (note this must be non-negative)
+ */
+ void SetColWidth(int col, int width);
+
+ /* Individually sets the column alignment.
+ * Params:
+ * col - the column to set, 0 indexed
+ * align - the new alignment (left or right) for the column
+ */
+ void SetColAlignment(int col, Alignment align);
+
+
+ /* The WriteRow family of functions allows you to write an entire row of the table at once.
+ * The common use case for the TableOutput class is to individually output each column after
+ * calculating what the value should contain. However, this would be tedious if you already
+ * knew the contents of the entire row which usually happenes when you are printing out the
+ * header for the table. To use this, simply pass each column as an individual parameter,
+ * for example:
+ * tableOutput.WriteRow("First Column", "Second Column", Decimal(3), PrefixHex(4), "Fifth Column");
+ */
+ template <class T0, class T1>
+ void WriteRow(T0 t0, T1 t1)
+ {
+ WriteColumn(0, t0);
+ WriteColumn(1, t1);
+ }
+
+ template <class T0, class T1, class T2>
+ void WriteRow(T0 t0, T1 t1, T2 t2)
+ {
+ WriteColumn(0, t0);
+ WriteColumn(1, t1);
+ WriteColumn(2, t2);
+ }
+
+
+ template <class T0, class T1, class T2, class T3>
+ void WriteRow(T0 t0, T1 t1, T2 t2, T3 t3)
+ {
+ WriteColumn(0, t0);
+ WriteColumn(1, t1);
+ WriteColumn(2, t2);
+ WriteColumn(3, t3);
+ }
+
+
+ template <class T0, class T1, class T2, class T3, class T4>
+ void WriteRow(T0 t0, T1 t1, T2 t2, T3 t3, T4 t4)
+ {
+ WriteColumn(0, t0);
+ WriteColumn(1, t1);
+ WriteColumn(2, t2);
+ WriteColumn(3, t3);
+ WriteColumn(4, t4);
+ }
+
+ template <class T0, class T1, class T2, class T3, class T4, class T5>
+ void WriteRow(T0 t0, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
+ {
+ WriteColumn(0, t0);
+ WriteColumn(1, t1);
+ WriteColumn(2, t2);
+ WriteColumn(3, t3);
+ WriteColumn(4, t4);
+ WriteColumn(5, t5);
+ }
+
+ template <class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9>
+ void WriteRow(T0 t0, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9)
+ {
+ WriteColumn(0, t0);
+ WriteColumn(1, t1);
+ WriteColumn(2, t2);
+ WriteColumn(3, t3);
+ WriteColumn(4, t4);
+ WriteColumn(5, t5);
+ WriteColumn(6, t6);
+ WriteColumn(7, t7);
+ WriteColumn(8, t8);
+ WriteColumn(9, t9);
+ }
+
+ /* The WriteColumn family of functions is used to output individual columns in the table.
+ * The intent is that the bulk of the table will be generated in a loop like so:
+ * while (condition) {
+ * int value1 = CalculateFirstColumn();
+ * table.WriteColumn(0, value1);
+ *
+ * String value2 = CalculateSecondColumn();
+ * table.WriteColumn(1, value2);
+ * }
+ * Params:
+ * col - the column to write, 0 indexed
+ * t - the value to write
+ * Note:
+ * You should generally use the specific instances of the Format class to generate output.
+ * For example, use the "Decimal", "Pointer", "ObjectPtr", etc. When passing data to this
+ * function. This tells the Table class how to display the value.
+ */
+ template <class T>
+ void WriteColumn(int col, const Output::Format<T> &t)
+ {
+ SOS_Assert(col >= 0);
+ SOS_Assert(col < mColumns);
+
+ if (col != mCurrCol)
+ OutputBlankColumns(col);
+
+ if (col == 0)
+ OutputIndent();
+
+ bool lastCol = col == mColumns - 1;
+
+ if (!lastCol)
+ t.OutputColumn(GetColAlign(col), GetColumnWidth(col));
+ else
+ t.Output();
+
+ ExtOut(lastCol ? "\n" : GetWhitespace(mPadding));
+
+ if (lastCol)
+ mCurrCol = 0;
+ else
+ mCurrCol = col+1;
+ }
+
+ template <class T>
+ void WriteColumn(int col, T t)
+ {
+ WriteColumn(col, Output::Format<T>(t));
+ }
+
+ void WriteColumn(int col, const String &str)
+ {
+ WriteColumn(col, Output::Format<const char *>(str));
+ }
+
+ void WriteColumn(int col, const WString &str)
+ {
+ WriteColumn(col, Output::Format<const WCHAR *>(str));
+ }
+
+ void WriteColumn(int col, __in_z WCHAR *str)
+ {
+ WriteColumn(col, Output::Format<const WCHAR *>(str));
+ }
+
+ void WriteColumn(int col, const WCHAR *str)
+ {
+ WriteColumn(col, Output::Format<const WCHAR *>(str));
+ }
+
+ inline void WriteColumn(int col, __in_z char *str)
+ {
+ WriteColumn(col, Output::Format<const char *>(str));
+ }
+
+ /* Writes a column using a printf style format. You cannot use the Format class with
+ * this function to specify how the output should look, use printf style formatting
+ * with the appropriate parameters instead.
+ */
+ void WriteColumnFormat(int col, const char *fmt, ...)
+ {
+ SOS_Assert(strstr(fmt, "%S") == NULL);
+
+ char result[128];
+
+ va_list list;
+ va_start(list, fmt);
+ vsprintf_s(result, _countof(result), fmt, list);
+ va_end(list);
+
+ WriteColumn(col, result);
+ }
+
+ void WriteColumnFormat(int col, const WCHAR *fmt, ...)
+ {
+ WCHAR result[128];
+
+ va_list list;
+ va_start(list, fmt);
+ vswprintf_s(result, _countof(result), fmt, list);
+ va_end(list);
+
+ WriteColumn(col, result);
+ }
+
+ /* This function is a shortcut for writing the next column. (That is, the one after the
+ * one you just wrote.)
+ */
+ template <class T>
+ void WriteColumn(T t)
+ {
+ WriteColumn(mCurrCol, t);
+ }
+
+private:
+ void Clear();
+ void AllocWidths();
+ int GetColumnWidth(int col);
+ Alignment GetColAlign(int col);
+ const char *GetWhitespace(int amount);
+ void OutputBlankColumns(int col);
+ void OutputIndent();
+
+private:
+ int mColumns, mDefaultWidth, mIndent, mPadding, mCurrCol;
+ Alignment mDefaultAlign;
+ int *mWidths;
+ Alignment *mAlignments;
+};
+
+HRESULT GetMethodDefinitionsFromName(DWORD_PTR ModulePtr, IXCLRDataModule* mod, const char* name, IXCLRDataMethodDefinition **ppMethodDefinitions, int numMethods, int *numMethodsNeeded);
+HRESULT GetMethodDescsFromName(DWORD_PTR ModulePtr, IXCLRDataModule* mod, const char* name, DWORD_PTR **pOut, int *numMethodDescs);
+
+HRESULT FileNameForModule (DacpModuleData *pModule, __out_ecount (MAX_LONGPATH) WCHAR *fileName);
+HRESULT FileNameForModule (DWORD_PTR pModuleAddr, __out_ecount (MAX_LONGPATH) WCHAR *fileName);
+void IP2MethodDesc (DWORD_PTR IP, DWORD_PTR &methodDesc, JITTypes &jitType,
+ DWORD_PTR &gcinfoAddr);
+const char *ElementTypeName (unsigned type);
+void DisplayFields (CLRDATA_ADDRESS cdaMT, DacpMethodTableData *pMTD, DacpMethodTableFieldData *pMTFD,
+ DWORD_PTR dwStartAddr = 0, BOOL bFirst=TRUE, BOOL bValueClass=FALSE);
+int GetObjFieldOffset(CLRDATA_ADDRESS cdaObj, __in_z LPCWSTR wszFieldName, BOOL bFirst=TRUE);
+int GetObjFieldOffset(CLRDATA_ADDRESS cdaObj, CLRDATA_ADDRESS cdaMT, __in_z LPCWSTR wszFieldName, BOOL bFirst=TRUE);
+
+BOOL IsValidToken(DWORD_PTR ModuleAddr, mdTypeDef mb);
+void NameForToken_s(DacpModuleData *pModule, mdTypeDef mb, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName,
+ bool bClassName=true);
+void NameForToken_s(DWORD_PTR ModuleAddr, mdTypeDef mb, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName,
+ bool bClassName=true);
+HRESULT NameForToken_s(mdTypeDef mb, IMetaDataImport *pImport, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName,
+ bool bClassName);
+HRESULT NameForTokenNew_s(mdTypeDef mb, IMDInternalImport *pImport, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName,
+ bool bClassName);
+
+void vmmap();
+void vmstat();
+
+#ifndef FEATURE_PAL
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Support for managed stack tracing
+//
+
+DWORD_PTR GetDebuggerJitInfo(DWORD_PTR md);
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+#endif // FEATURE_PAL
+
+template <typename SCALAR>
+inline
+int bitidx(SCALAR bitflag)
+{
+ for (int idx = 0; idx < static_cast<int>(sizeof(bitflag))*8; ++idx)
+ {
+ if (bitflag & (1 << idx))
+ {
+ _ASSERTE((bitflag & (~(1 << idx))) == 0);
+ return idx;
+ }
+ }
+ return -1;
+}
+
+HRESULT
+DllsName(
+ ULONG_PTR addrContaining,
+ __out_ecount (MAX_LONGPATH) WCHAR *dllName
+ );
+
+inline
+BOOL IsElementValueType (CorElementType cet)
+{
+ return (cet >= ELEMENT_TYPE_BOOLEAN && cet <= ELEMENT_TYPE_R8)
+ || cet == ELEMENT_TYPE_VALUETYPE || cet == ELEMENT_TYPE_I || cet == ELEMENT_TYPE_U;
+}
+
+
+#define safemove(dst, src) \
+SafeReadMemory (TO_TADDR(src), &(dst), sizeof(dst), NULL)
+
+extern "C" PDEBUG_DATA_SPACES g_ExtData;
+
+template <class T>
+class ArrayHolder
+{
+public:
+ ArrayHolder(T *ptr)
+ : mPtr(ptr)
+ {
+ }
+
+ ~ArrayHolder()
+ {
+ Clear();
+ }
+
+ ArrayHolder(const ArrayHolder &rhs)
+ {
+ mPtr = const_cast<ArrayHolder *>(&rhs)->Detach();
+ }
+
+ ArrayHolder &operator=(T *ptr)
+ {
+ Clear();
+ mPtr = ptr;
+ return *this;
+ }
+
+ const T &operator[](int i) const
+ {
+ return mPtr[i];
+ }
+
+ T &operator[](int i)
+ {
+ return mPtr[i];
+ }
+
+ operator const T *() const
+ {
+ return mPtr;
+ }
+
+ operator T *()
+ {
+ return mPtr;
+ }
+
+ T **operator&()
+ {
+ return &mPtr;
+ }
+
+ T *GetPtr()
+ {
+ return mPtr;
+ }
+
+ T *Detach()
+ {
+ T *ret = mPtr;
+ mPtr = NULL;
+ return ret;
+ }
+
+private:
+ void Clear()
+ {
+ if (mPtr)
+ {
+ delete [] mPtr;
+ mPtr = NULL;
+ }
+ }
+
+private:
+ T *mPtr;
+};
+
+
+
+// This class acts a smart pointer which calls the Release method on any object
+// you place in it when the ToRelease class falls out of scope. You may use it
+// just like you would a standard pointer to a COM object (including if (foo),
+// if (!foo), if (foo == 0), etc) except for two caveats:
+// 1. This class never calls AddRef and it always calls Release when it
+// goes out of scope.
+// 2. You should never use & to try to get a pointer to a pointer unless
+// you call Release first, or you will leak whatever this object contains
+// prior to updating its internal pointer.
+template<class T>
+class ToRelease
+{
+public:
+ ToRelease()
+ : m_ptr(NULL)
+ {}
+
+ ToRelease(T* ptr)
+ : m_ptr(ptr)
+ {}
+
+ ~ToRelease()
+ {
+ Release();
+ }
+
+ void operator=(T *ptr)
+ {
+ Release();
+
+ m_ptr = ptr;
+ }
+
+ T* operator->()
+ {
+ return m_ptr;
+ }
+
+ operator T*()
+ {
+ return m_ptr;
+ }
+
+ T** operator&()
+ {
+ return &m_ptr;
+ }
+
+ T* GetPtr() const
+ {
+ return m_ptr;
+ }
+
+ T* Detach()
+ {
+ T* pT = m_ptr;
+ m_ptr = NULL;
+ return pT;
+ }
+
+ void Release()
+ {
+ if (m_ptr != NULL)
+ {
+ m_ptr->Release();
+ m_ptr = NULL;
+ }
+ }
+
+private:
+ T* m_ptr;
+};
+
+struct ModuleInfo
+{
+ ULONG64 baseAddr;
+ ULONG64 size;
+ BOOL hasPdb;
+};
+extern ModuleInfo moduleInfo[];
+
+BOOL InitializeHeapData();
+BOOL IsServerBuild ();
+UINT GetMaxGeneration();
+UINT GetGcHeapCount();
+BOOL GetGcStructuresValid();
+
+ULONG GetILSize(DWORD_PTR ilAddr); // REturns 0 if error occurs
+HRESULT DecodeILFromAddress(IMetaDataImport *pImport, TADDR ilAddr);
+void DecodeIL(IMetaDataImport *pImport, BYTE *buffer, ULONG bufSize);
+void DecodeDynamicIL(BYTE *data, ULONG Size, DacpObjectData& tokenArray);
+
+BOOL IsRetailBuild (size_t base);
+EEFLAVOR GetEEFlavor ();
+HRESULT InitCorDebugInterface();
+VOID UninitCorDebugInterface();
+#ifndef FEATURE_PAL
+BOOL GetEEVersion(VS_FIXEDFILEINFO *pFileInfo);
+BOOL GetSOSVersion(VS_FIXEDFILEINFO *pFileInfo);
+#endif
+
+BOOL IsDumpFile ();
+
+// IsMiniDumpFile will return true if 1) we are in
+// a small format minidump, and g_InMinidumpSafeMode is true.
+extern BOOL g_InMinidumpSafeMode;
+
+BOOL IsMiniDumpFile();
+void ReportOOM();
+
+BOOL SafeReadMemory (TADDR offset, PVOID lpBuffer, ULONG cb, PULONG lpcbBytesRead);
+#if !defined(_TARGET_WIN64_) && !defined(_ARM64_)
+// on 64-bit platforms TADDR and CLRDATA_ADDRESS are identical
+inline BOOL SafeReadMemory (CLRDATA_ADDRESS offset, PVOID lpBuffer, ULONG cb, PULONG lpcbBytesRead)
+{ return SafeReadMemory(TO_TADDR(offset), lpBuffer, cb, lpcbBytesRead); }
+#endif
+
+BOOL NameForMD_s (DWORD_PTR pMD, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName);
+BOOL NameForMT_s (DWORD_PTR MTAddr, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName);
+
+WCHAR *CreateMethodTableName(TADDR mt, TADDR cmt = NULL);
+
+void isRetAddr(DWORD_PTR retAddr, DWORD_PTR* whereCalled);
+DWORD_PTR GetValueFromExpression (___in __in_z const char *const str);
+
+enum ModuleHeapType
+{
+ ModuleHeapType_ThunkHeap,
+ ModuleHeapType_LookupTableHeap
+};
+
+HRESULT PrintDomainHeapInfo(const char *name, CLRDATA_ADDRESS adPtr, DWORD_PTR *size, DWORD_PTR *wasted = 0);
+DWORD_PTR PrintModuleHeapInfo(DWORD_PTR *moduleList, int count, ModuleHeapType type, DWORD_PTR *wasted = 0);
+void PrintHeapSize(DWORD_PTR total, DWORD_PTR wasted);
+void DomainInfo(DacpAppDomainData *pDomain);
+void AssemblyInfo(DacpAssemblyData *pAssembly);
+DWORD_PTR LoaderHeapInfo(CLRDATA_ADDRESS pLoaderHeapAddr, DWORD_PTR *wasted = 0);
+DWORD_PTR JitHeapInfo();
+DWORD_PTR VSDHeapInfo(CLRDATA_ADDRESS appDomain, DWORD_PTR *wasted = 0);
+
+DWORD GetNumComponents(TADDR obj);
+
+struct GenUsageStat
+{
+ size_t allocd;
+ size_t freed;
+ size_t unrooted;
+};
+
+struct HeapUsageStat
+{
+ GenUsageStat genUsage[4]; // gen0, 1, 2, LOH
+};
+
+extern DacpUsefulGlobalsData g_special_usefulGlobals;
+BOOL GCHeapUsageStats(const DacpGcHeapDetails& heap, BOOL bIncUnreachable, HeapUsageStat *hpUsage);
+
+class HeapStat
+{
+protected:
+ struct Node
+ {
+ DWORD_PTR data;
+ DWORD count;
+ size_t totalSize;
+ Node* left;
+ Node* right;
+ Node ()
+ : data(0), count(0), totalSize(0), left(NULL), right(NULL)
+ {
+ }
+ };
+ BOOL bHasStrings;
+ Node *head;
+ BOOL fLinear;
+public:
+ HeapStat ()
+ : bHasStrings(FALSE), head(NULL), fLinear(FALSE)
+ {}
+ ~HeapStat()
+ {
+ Delete();
+ }
+ // TODO: Change the aSize argument to size_t when we start supporting
+ // TODO: object sizes above 4GB
+ void Add (DWORD_PTR aData, DWORD aSize);
+ void Sort ();
+ void Print (const char* label = NULL);
+ void Delete ();
+ void HasStrings(BOOL abHasStrings)
+ {
+ bHasStrings = abHasStrings;
+ }
+private:
+ int CompareData(DWORD_PTR n1, DWORD_PTR n2);
+ void SortAdd (Node *&root, Node *entry);
+ void LinearAdd (Node *&root, Node *entry);
+ void ReverseLeftMost (Node *root);
+ void Linearize();
+};
+
+class CGCDesc;
+
+// The information MethodTableCache returns.
+struct MethodTableInfo
+{
+ bool IsInitialized() { return BaseSize != 0; }
+
+ DWORD BaseSize; // Caching BaseSize and ComponentSize for a MethodTable
+ DWORD ComponentSize; // here has HUGE perf benefits in heap traversals.
+ BOOL bContainsPointers;
+ DWORD_PTR* GCInfoBuffer; // Start of memory of GC info
+ CGCDesc* GCInfo; // Just past GC info (which is how it is stored)
+ bool ArrayOfVC;
+};
+
+class MethodTableCache
+{
+protected:
+
+ struct Node
+ {
+ DWORD_PTR data; // This is the key (the method table pointer)
+ MethodTableInfo info; // The info associated with this MethodTable
+ Node* left;
+ Node* right;
+ Node (DWORD_PTR aData) : data(aData), left(NULL), right(NULL)
+ {
+ info.BaseSize = 0;
+ info.ComponentSize = 0;
+ info.bContainsPointers = false;
+ info.GCInfo = NULL;
+ info.ArrayOfVC = false;
+ info.GCInfoBuffer = NULL;
+ }
+ };
+ Node *head;
+public:
+ MethodTableCache ()
+ : head(NULL)
+ {}
+ ~MethodTableCache() { Clear(); }
+
+ // Always succeeds, if it is not present it adds an empty Info struct and returns that
+ // Thus you must call 'IsInitialized' on the returned value before using it
+ MethodTableInfo* Lookup(DWORD_PTR aData);
+
+ void Clear ();
+private:
+ int CompareData(DWORD_PTR n1, DWORD_PTR n2);
+ void ReverseLeftMost (Node *root);
+};
+
+extern MethodTableCache g_special_mtCache;
+
+struct DumpArrayFlags
+{
+ DWORD_PTR startIndex;
+ DWORD_PTR Length;
+ BOOL bDetail;
+ LPSTR strObject;
+ BOOL bNoFieldsForElement;
+
+ DumpArrayFlags ()
+ : startIndex(0), Length((DWORD_PTR)-1), bDetail(FALSE), strObject (0), bNoFieldsForElement(FALSE)
+ {}
+ ~DumpArrayFlags ()
+ {
+ if (strObject)
+ delete [] strObject;
+ }
+}; //DumpArrayFlags
+
+
+
+// -----------------------------------------------------------------------
+
+#define BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX 0x08000000
+#define BIT_SBLK_FINALIZER_RUN 0x40000000
+#define BIT_SBLK_SPIN_LOCK 0x10000000
+#define SBLK_MASK_LOCK_THREADID 0x000003FF // special value of 0 + 1023 thread ids
+#define SBLK_MASK_LOCK_RECLEVEL 0x0000FC00 // 64 recursion levels
+#define SBLK_APPDOMAIN_SHIFT 16 // shift right this much to get appdomain index
+#define SBLK_MASK_APPDOMAININDEX 0x000007FF // 2048 appdomain indices
+#define SBLK_RECLEVEL_SHIFT 10 // shift right this much to get recursion level
+#define BIT_SBLK_IS_HASHCODE 0x04000000
+#define MASK_HASHCODE ((1<<HASHCODE_BITS)-1)
+#define SYNCBLOCKINDEX_BITS 26
+#define MASK_SYNCBLOCKINDEX ((1<<SYNCBLOCKINDEX_BITS)-1)
+
+HRESULT GetMTOfObject(TADDR obj, TADDR *mt);
+
+struct needed_alloc_context
+{
+ BYTE* alloc_ptr; // starting point for next allocation
+ BYTE* alloc_limit; // ending point for allocation region/quantum
+};
+
+struct AllocInfo
+{
+ needed_alloc_context *array;
+ int num; // number of allocation contexts in array
+
+ AllocInfo()
+ : array(NULL)
+ , num(0)
+ {}
+ void Init()
+ {
+ extern void GetAllocContextPtrs(AllocInfo *pallocInfo);
+ GetAllocContextPtrs(this);
+ }
+ ~AllocInfo()
+ {
+ if (array != NULL)
+ delete[] array;
+ }
+};
+
+struct GCHandleStatistics
+{
+ HeapStat hs;
+
+ DWORD strongHandleCount;
+ DWORD pinnedHandleCount;
+ DWORD asyncPinnedHandleCount;
+ DWORD refCntHandleCount;
+ DWORD weakLongHandleCount;
+ DWORD weakShortHandleCount;
+ DWORD variableCount;
+ DWORD sizedRefCount;
+ DWORD dependentCount;
+ DWORD weakWinRTHandleCount;
+ DWORD unknownHandleCount;
+ GCHandleStatistics()
+ : strongHandleCount(0), pinnedHandleCount(0), asyncPinnedHandleCount(0), refCntHandleCount(0),
+ weakLongHandleCount(0), weakShortHandleCount(0), variableCount(0), sizedRefCount(0),
+ dependentCount(0), weakWinRTHandleCount(0), unknownHandleCount(0)
+ {}
+ ~GCHandleStatistics()
+ {
+ hs.Delete();
+ }
+};
+
+struct SegmentLookup
+{
+ DacpHeapSegmentData *m_segments;
+ int m_iSegmentsSize;
+ int m_iSegmentCount;
+
+ SegmentLookup();
+ ~SegmentLookup();
+
+ void Clear();
+ BOOL AddSegment(DacpHeapSegmentData *pData);
+ CLRDATA_ADDRESS GetHeap(CLRDATA_ADDRESS object, BOOL& bFound);
+};
+
+class GCHeapSnapshot
+{
+private:
+ BOOL m_isBuilt;
+ DacpGcHeapDetails *m_heapDetails;
+ DacpGcHeapData m_gcheap;
+ SegmentLookup m_segments;
+
+ BOOL AddSegments(DacpGcHeapDetails& details);
+public:
+ GCHeapSnapshot();
+
+ BOOL Build();
+ void Clear();
+ BOOL IsBuilt() { return m_isBuilt; }
+
+ DacpGcHeapData *GetHeapData() { return &m_gcheap; }
+
+ int GetHeapCount() { return m_gcheap.HeapCount; }
+
+ DacpGcHeapDetails *GetHeap(CLRDATA_ADDRESS objectPointer);
+ int GetGeneration(CLRDATA_ADDRESS objectPointer);
+
+
+};
+extern GCHeapSnapshot g_snapshot;
+
+BOOL IsSameModuleName (const char *str1, const char *str2);
+BOOL IsModule (DWORD_PTR moduleAddr);
+BOOL IsMethodDesc (DWORD_PTR value);
+BOOL IsMethodTable (DWORD_PTR value);
+BOOL IsStringObject (size_t obj);
+BOOL IsObjectArray (DWORD_PTR objPointer);
+BOOL IsObjectArray (DacpObjectData *pData);
+
+/* Returns a list of all modules in the process.
+ * Params:
+ * name - The name of the module you would like. If mName is NULL the all modules are returned.
+ * numModules - The number of modules in the array returned.
+ * Returns:
+ * An array of modules whose length is *numModules, NULL if an error occurred. Note that if this
+ * function succeeds but finds no modules matching the name given, this function returns a valid
+ * array, but *numModules will equal 0.
+ * Note:
+ * You must clean up the return value of this array by calling delete [] on it, or using the
+ * ArrayHolder class.
+ */
+DWORD_PTR *ModuleFromName(__in_opt LPSTR name, int *numModules);
+void GetInfoFromName(DWORD_PTR ModuleAddr, const char* name);
+void GetInfoFromModule (DWORD_PTR ModuleAddr, ULONG token, DWORD_PTR *ret=NULL);
+
+
+typedef void (*VISITGCHEAPFUNC)(DWORD_PTR objAddr,size_t Size,DWORD_PTR methodTable,LPVOID token);
+BOOL GCHeapsTraverse(VISITGCHEAPFUNC pFunc, LPVOID token, BOOL verify=true);
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+struct strobjInfo
+{
+ size_t methodTable;
+ DWORD m_StringLength;
+};
+
+// Just to make figuring out which fill pointer element matches a generation
+// a bit less confusing. This gen_segment function is ported from gc.cpp.
+inline unsigned int gen_segment (int gen)
+{
+ return (DAC_NUMBERGENERATIONS - gen - 1);
+}
+
+inline CLRDATA_ADDRESS SegQueue(DacpGcHeapDetails& heapDetails, int seg)
+{
+ return heapDetails.finalization_fill_pointers[seg - 1];
+}
+
+inline CLRDATA_ADDRESS SegQueueLimit(DacpGcHeapDetails& heapDetails, int seg)
+{
+ return heapDetails.finalization_fill_pointers[seg];
+}
+
+#define FinalizerListSeg (DAC_NUMBERGENERATIONS+1)
+#define CriticalFinalizerListSeg (DAC_NUMBERGENERATIONS)
+
+void GatherOneHeapFinalization(DacpGcHeapDetails& heapDetails, HeapStat *stat, BOOL bAllReady, BOOL bShort);
+
+CLRDATA_ADDRESS GetAppDomainForMT(CLRDATA_ADDRESS mtPtr);
+CLRDATA_ADDRESS GetAppDomain(CLRDATA_ADDRESS objPtr);
+void GCHeapInfo(const DacpGcHeapDetails &heap, DWORD_PTR &total_size);
+BOOL GCObjInHeap(TADDR taddrObj, const DacpGcHeapDetails &heap,
+ TADDR_SEGINFO& trngSeg, int& gen, TADDR_RANGE& allocCtx, BOOL &bLarge);
+
+BOOL VerifyObject(const DacpGcHeapDetails &heap, const DacpHeapSegmentData &seg, DWORD_PTR objAddr, DWORD_PTR MTAddr, size_t objSize,
+ BOOL bVerifyMember);
+BOOL VerifyObject(const DacpGcHeapDetails &heap, DWORD_PTR objAddr, DWORD_PTR MTAddr, size_t objSize,
+ BOOL bVerifyMember);
+
+BOOL IsMTForFreeObj(DWORD_PTR pMT);
+void DumpStackObjectsHelper (TADDR StackTop, TADDR StackBottom, BOOL verifyFields);
+
+
+enum ARGTYPE {COBOOL,COSIZE_T,COHEX,COSTRING};
+struct CMDOption
+{
+ const char* name;
+ void *vptr;
+ ARGTYPE type;
+ BOOL hasValue;
+ BOOL hasSeen;
+};
+struct CMDValue
+{
+ void *vptr;
+ ARGTYPE type;
+};
+BOOL GetCMDOption(const char *string, CMDOption *option, size_t nOption,
+ CMDValue *arg, size_t maxArg, size_t *nArg);
+
+void DumpMDInfo(DWORD_PTR dwStartAddr, CLRDATA_ADDRESS dwRequestedIP = 0, BOOL fStackTraceFormat = FALSE);
+void DumpMDInfoFromMethodDescData(DacpMethodDescData * pMethodDescData, BOOL fStackTraceFormat);
+void GetDomainList(DWORD_PTR *&domainList, int &numDomain);
+HRESULT GetThreadList(DWORD_PTR **threadList, int *numThread);
+CLRDATA_ADDRESS GetCurrentManagedThread(); // returns current managed thread if any
+void GetAllocContextPtrs(AllocInfo *pallocInfo);
+
+void ReloadSymbolWithLineInfo();
+
+size_t FunctionType (size_t EIP);
+
+size_t Align (size_t nbytes);
+// Aligns large objects
+size_t AlignLarge (size_t nbytes);
+
+ULONG OSPageSize ();
+size_t NextOSPageAddress (size_t addr);
+
+// This version of objectsize reduces the lookup of methodtables in the DAC.
+// It uses g_special_mtCache for it's work.
+BOOL GetSizeEfficient(DWORD_PTR dwAddrCurrObj,
+ DWORD_PTR dwAddrMethTable, BOOL bLarge, size_t& s, BOOL& bContainsPointers);
+
+// ObjSize now uses the methodtable cache for its work too.
+size_t ObjectSize (DWORD_PTR obj, BOOL fIsLargeObject=FALSE);
+size_t ObjectSize(DWORD_PTR obj, DWORD_PTR mt, BOOL fIsValueClass, BOOL fIsLargeObject=FALSE);
+
+void CharArrayContent(TADDR pos, ULONG num, bool widechar);
+void StringObjectContent (size_t obj, BOOL fLiteral=FALSE, const int length=-1); // length=-1: dump everything in the string object.
+
+UINT FindAllPinnedAndStrong (DWORD_PTR handlearray[],UINT arraySize);
+void PrintNotReachableInRange(TADDR rngStart, TADDR rngEnd, BOOL bExcludeReadyForFinalization,
+ HeapStat* stat, BOOL bShort);
+
+const char *EHTypeName(EHClauseType et);
+
+template<typename T>
+inline const LPCSTR GetTransparency(const T &t)
+{
+ if (!t.bHasCriticalTransparentInfo)
+ {
+ return "Not calculated";
+ }
+ else if (t.bIsCritical && !t.bIsTreatAsSafe)
+ {
+ return "Critical";
+ }
+ else if (t.bIsCritical)
+ {
+ return "Safe critical";
+ }
+ else
+ {
+ return "Transparent";
+ }
+}
+
+struct StringHolder
+{
+ LPSTR data;
+ StringHolder() : data(NULL) { }
+ ~StringHolder() { if(data) delete [] data; }
+};
+
+
+ULONG DebuggeeType();
+
+inline BOOL IsKernelDebugger ()
+{
+ return DebuggeeType() == DEBUG_CLASS_KERNEL;
+}
+
+void ResetGlobals(void);
+HRESULT LoadClrDebugDll(void);
+extern "C" void UnloadClrDebugDll(void);
+
+extern IMetaDataImport* MDImportForModule (DacpModuleData *pModule);
+extern IMetaDataImport* MDImportForModule (DWORD_PTR pModule);
+
+//*****************************************************************************
+//
+// **** CQuickBytes
+// This helper class is useful for cases where 90% of the time you allocate 512
+// or less bytes for a data structure. This class contains a 512 byte buffer.
+// Alloc() will return a pointer to this buffer if your allocation is small
+// enough, otherwise it asks the heap for a larger buffer which is freed for
+// you. No mutex locking is required for the small allocation case, making the
+// code run faster, less heap fragmentation, etc... Each instance will allocate
+// 520 bytes, so use accordinly.
+//
+//*****************************************************************************
+template <DWORD SIZE, DWORD INCREMENT>
+class CQuickBytesBase
+{
+public:
+ CQuickBytesBase() :
+ pbBuff(0),
+ iSize(0),
+ cbTotal(SIZE)
+ { }
+
+ void Destroy()
+ {
+ if (pbBuff)
+ {
+ delete[] (BYTE*)pbBuff;
+ pbBuff = 0;
+ }
+ }
+
+ void *Alloc(SIZE_T iItems)
+ {
+ iSize = iItems;
+ if (iItems <= SIZE)
+ {
+ cbTotal = SIZE;
+ return (&rgData[0]);
+ }
+ else
+ {
+ if (pbBuff)
+ delete[] (BYTE*)pbBuff;
+ pbBuff = new BYTE[iItems];
+ cbTotal = pbBuff ? iItems : 0;
+ return (pbBuff);
+ }
+ }
+
+ // This is for conformity to the CQuickBytesBase that is defined by the runtime so
+ // that we can use it inside of some GC code that SOS seems to include as well.
+ //
+ // The plain vanilla "Alloc" version on this CQuickBytesBase doesn't throw either,
+ // so we'll just forward the call.
+ void *AllocNoThrow(SIZE_T iItems)
+ {
+ return Alloc(iItems);
+ }
+
+ HRESULT ReSize(SIZE_T iItems)
+ {
+ void *pbBuffNew;
+ if (iItems <= cbTotal)
+ {
+ iSize = iItems;
+ return NOERROR;
+ }
+
+ pbBuffNew = new BYTE[iItems + INCREMENT];
+ if (!pbBuffNew)
+ return E_OUTOFMEMORY;
+ if (pbBuff)
+ {
+ memcpy(pbBuffNew, pbBuff, cbTotal);
+ delete[] (BYTE*)pbBuff;
+ }
+ else
+ {
+ _ASSERTE(cbTotal == SIZE);
+ memcpy(pbBuffNew, rgData, SIZE);
+ }
+ cbTotal = iItems + INCREMENT;
+ iSize = iItems;
+ pbBuff = pbBuffNew;
+ return NOERROR;
+
+ }
+
+ operator PVOID()
+ { return ((pbBuff) ? pbBuff : &rgData[0]); }
+
+ void *Ptr()
+ { return ((pbBuff) ? pbBuff : &rgData[0]); }
+
+ SIZE_T Size()
+ { return (iSize); }
+
+ SIZE_T MaxSize()
+ { return (cbTotal); }
+
+ void *pbBuff;
+ SIZE_T iSize; // number of bytes used
+ SIZE_T cbTotal; // total bytes allocated in the buffer
+ // use UINT64 to enforce the alignment of the memory
+ UINT64 rgData[(SIZE+sizeof(UINT64)-1)/sizeof(UINT64)];
+};
+
+#define CQUICKBYTES_BASE_SIZE 512
+#define CQUICKBYTES_INCREMENTAL_SIZE 128
+
+class CQuickBytesNoDtor : public CQuickBytesBase<CQUICKBYTES_BASE_SIZE, CQUICKBYTES_INCREMENTAL_SIZE>
+{
+};
+
+class CQuickBytes : public CQuickBytesNoDtor
+{
+public:
+ CQuickBytes() { }
+
+ ~CQuickBytes()
+ {
+ Destroy();
+ }
+};
+
+template <DWORD CQUICKBYTES_BASE_SPECIFY_SIZE>
+class CQuickBytesNoDtorSpecifySize : public CQuickBytesBase<CQUICKBYTES_BASE_SPECIFY_SIZE, CQUICKBYTES_INCREMENTAL_SIZE>
+{
+};
+
+template <DWORD CQUICKBYTES_BASE_SPECIFY_SIZE>
+class CQuickBytesSpecifySize : public CQuickBytesNoDtorSpecifySize<CQUICKBYTES_BASE_SPECIFY_SIZE>
+{
+public:
+ CQuickBytesSpecifySize() { }
+
+ ~CQuickBytesSpecifySize()
+ {
+ CQuickBytesNoDtorSpecifySize<CQUICKBYTES_BASE_SPECIFY_SIZE>::Destroy();
+ }
+};
+
+
+#define STRING_SIZE 10
+class CQuickString : public CQuickBytesBase<STRING_SIZE, STRING_SIZE>
+{
+public:
+ CQuickString() { }
+
+ ~CQuickString()
+ {
+ Destroy();
+ }
+
+ void *Alloc(SIZE_T iItems)
+ {
+ return CQuickBytesBase<STRING_SIZE, STRING_SIZE>::Alloc(iItems*sizeof(WCHAR));
+ }
+
+ HRESULT ReSize(SIZE_T iItems)
+ {
+ return CQuickBytesBase<STRING_SIZE, STRING_SIZE>::ReSize(iItems * sizeof(WCHAR));
+ }
+
+ SIZE_T Size()
+ {
+ return CQuickBytesBase<STRING_SIZE, STRING_SIZE>::Size() / sizeof(WCHAR);
+ }
+
+ SIZE_T MaxSize()
+ {
+ return CQuickBytesBase<STRING_SIZE, STRING_SIZE>::MaxSize() / sizeof(WCHAR);
+ }
+
+ WCHAR* String()
+ {
+ return (WCHAR*) Ptr();
+ }
+
+};
+
+enum GetSignatureStringResults
+{
+ GSS_SUCCESS,
+ GSS_ERROR,
+ GSS_INSUFFICIENT_DATA,
+};
+
+GetSignatureStringResults GetMethodSignatureString (PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, DWORD_PTR dwModuleAddr, CQuickBytes *sigString);
+GetSignatureStringResults GetSignatureString (PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, DWORD_PTR dwModuleAddr, CQuickBytes *sigString);
+void GetMethodName(mdMethodDef methodDef, IMetaDataImport * pImport, CQuickBytes *fullName);
+
+#ifndef _TARGET_WIN64_
+#define itoa_s_ptr _itoa_s
+#define itow_s_ptr _itow_s
+#define itoa_ptr _itoa
+#define itow_ptr _itow
+#else
+#define itoa_s_ptr _i64toa_s
+#define itow_s_ptr _i64tow_s
+#define itoa_ptr _i64toa
+#define itow_ptr _i64tow
+#endif
+
+#ifdef FEATURE_PAL
+extern "C"
+int _itoa_s( int inValue, char* outBuffer, size_t inDestBufferSize, int inRadix );
+extern "C"
+int _ui64toa_s( unsigned __int64 inValue, char* outBuffer, size_t inDestBufferSize, int inRadix );
+#endif // FEATURE_PAL
+
+struct MemRange
+{
+ MemRange (ULONG64 s = NULL, size_t l = 0, MemRange * n = NULL)
+ : start(s), len (l), next (n)
+ {}
+
+ bool InRange (ULONG64 addr)
+ {
+ return addr >= start && addr < start + len;
+ }
+
+ ULONG64 start;
+ size_t len;
+ MemRange * next;
+}; //struct MemRange
+
+#ifndef FEATURE_PAL
+
+class StressLogMem
+{
+private:
+ // use a linked list for now, could be optimazied later
+ MemRange * list;
+
+ void AddRange (ULONG64 s, size_t l)
+ {
+ list = new MemRange (s, l, list);
+ }
+
+public:
+ StressLogMem () : list (NULL)
+ {}
+ ~StressLogMem ();
+ bool Init (ULONG64 stressLogAddr, IDebugDataSpaces* memCallBack);
+ bool IsInStressLog (ULONG64 addr);
+}; //class StressLogMem
+
+// An adapter class that DIA consumes so that it can read PE data from the an image
+// This implementation gets the backing data from the image loaded in debuggee memory
+// that has been layed out identical to the disk format (ie not seperated by section)
+class PEOffsetMemoryReader : IDiaReadExeAtOffsetCallback
+{
+public:
+ PEOffsetMemoryReader(TADDR moduleBaseAddress);
+
+ // IUnknown implementation
+ HRESULT __stdcall QueryInterface(REFIID riid, VOID** ppInterface);
+ ULONG __stdcall AddRef();
+ ULONG __stdcall Release();
+
+ // IDiaReadExeAtOffsetCallback implementation
+ HRESULT __stdcall ReadExecutableAt(DWORDLONG fileOffset, DWORD cbData, DWORD* pcbData, BYTE data[]);
+
+private:
+ TADDR m_moduleBaseAddress;
+ volatile ULONG m_refCount;
+};
+
+// An adapter class that DIA consumes so that it can read PE data from the an image
+// This implementation gets the backing data from the image loaded in debuggee memory
+// that has been layed out in LoadLibrary format
+class PERvaMemoryReader : IDiaReadExeAtRVACallback
+{
+public:
+ PERvaMemoryReader(TADDR moduleBaseAddress);
+
+ // IUnknown implementation
+ HRESULT __stdcall QueryInterface(REFIID riid, VOID** ppInterface);
+ ULONG __stdcall AddRef();
+ ULONG __stdcall Release();
+
+ // IDiaReadExeAtOffsetCallback implementation
+ HRESULT __stdcall ReadExecutableAtRVA(DWORD relativeVirtualAddress, DWORD cbData, DWORD* pcbData, BYTE data[]);
+
+private:
+ TADDR m_moduleBaseAddress;
+ volatile ULONG m_refCount;
+};
+
+#endif // !FEATURE_PAL
+
+static const char *SymbolReaderDllName = "SOS.NETCore";
+static const char *SymbolReaderClassName = "SOS.SymbolReader";
+
+typedef int (*ReadMemoryDelegate)(ULONG64, char *, int);
+typedef ULONG64 (*LoadSymbolsForModuleDelegate)(const char*, BOOL, ULONG64, int, ULONG64, int, ReadMemoryDelegate);
+typedef void (*DisposeDelegate)(ULONG64);
+typedef BOOL (*ResolveSequencePointDelegate)(ULONG64, const char*, unsigned int, unsigned int*, unsigned int*);
+typedef BOOL (*GetLocalVariableName)(ULONG64, int, int, BSTR*);
+typedef BOOL (*GetLineByILOffsetDelegate)(ULONG64, mdMethodDef, ULONG64, ULONG *, BSTR*);
+
+class SymbolReader
+{
+private:
+#ifndef FEATURE_PAL
+ ISymUnmanagedReader* m_pSymReader;
+#endif
+ ULONG64 m_symbolReaderHandle;
+
+ static LoadSymbolsForModuleDelegate loadSymbolsForModuleDelegate;
+ static DisposeDelegate disposeDelegate;
+ static ResolveSequencePointDelegate resolveSequencePointDelegate;
+ static GetLocalVariableName getLocalVariableNameDelegate;
+ static GetLineByILOffsetDelegate getLineByILOffsetDelegate;
+ static HRESULT PrepareSymbolReader();
+
+ HRESULT GetNamedLocalVariable(___in ISymUnmanagedScope* pScope, ___in ICorDebugILFrame* pILFrame, ___in mdMethodDef methodToken, ___in ULONG localIndex,
+ __out_ecount(paramNameLen) WCHAR* paramName, ___in ULONG paramNameLen, ___out ICorDebugValue** ppValue);
+ HRESULT LoadSymbolsForWindowsPDB(___in IMetaDataImport* pMD, ___in ULONG64 peAddress, __in_z WCHAR* pModuleName, ___in BOOL isFileLayout);
+ HRESULT LoadSymbolsForPortablePDB(__in_z WCHAR* pModuleName, ___in BOOL isInMemory, ___in BOOL isFileLayout, ___in ULONG64 peAddress, ___in ULONG64 peSize,
+ ___in ULONG64 inMemoryPdbAddress, ___in ULONG64 inMemoryPdbSize);
+
+public:
+ SymbolReader()
+ {
+#ifndef FEATURE_PAL
+ m_pSymReader = NULL;
+#endif
+ m_symbolReaderHandle = 0;
+ }
+
+ ~SymbolReader()
+ {
+#ifndef FEATURE_PAL
+ if(m_pSymReader != NULL)
+ {
+ m_pSymReader->Release();
+ m_pSymReader = NULL;
+ }
+#endif
+ if (m_symbolReaderHandle != 0)
+ {
+ disposeDelegate(m_symbolReaderHandle);
+ m_symbolReaderHandle = 0;
+ }
+ }
+
+ HRESULT LoadSymbols(___in IMetaDataImport* pMD, ___in ICorDebugModule* pModule);
+ HRESULT LoadSymbols(___in IMetaDataImport* pMD, ___in IXCLRDataModule* pModule);
+ HRESULT GetLineByILOffset(___in mdMethodDef MethodToken, ___in ULONG64 IlOffset, ___out ULONG *pLinenum, __out_ecount(cchFileName) WCHAR* pwszFileName, ___in ULONG cchFileName);
+ HRESULT GetNamedLocalVariable(___in ICorDebugFrame * pFrame, ___in ULONG localIndex, __out_ecount(paramNameLen) WCHAR* paramName, ___in ULONG paramNameLen, ___out ICorDebugValue** ppValue);
+ HRESULT ResolveSequencePoint(__in_z WCHAR* pFilename, ___in ULONG32 lineNumber, ___in TADDR mod, ___out mdMethodDef* ___out pToken, ___out ULONG32* pIlOffset);
+};
+
+HRESULT
+GetLineByOffset(
+ ___in ULONG64 IP,
+ ___out ULONG *pLinenum,
+ __out_ecount(cchFileName) WCHAR* pwszFileName,
+ ___in ULONG cchFileName);
+
+/// X86 Context
+#define X86_SIZE_OF_80387_REGISTERS 80
+#define X86_MAXIMUM_SUPPORTED_EXTENSION 512
+
+typedef struct {
+ DWORD ControlWord;
+ DWORD StatusWord;
+ DWORD TagWord;
+ DWORD ErrorOffset;
+ DWORD ErrorSelector;
+ DWORD DataOffset;
+ DWORD DataSelector;
+ BYTE RegisterArea[X86_SIZE_OF_80387_REGISTERS];
+ DWORD Cr0NpxState;
+} X86_FLOATING_SAVE_AREA;
+
+typedef struct {
+
+ DWORD ContextFlags;
+ DWORD Dr0;
+ DWORD Dr1;
+ DWORD Dr2;
+ DWORD Dr3;
+ DWORD Dr6;
+ DWORD Dr7;
+
+ X86_FLOATING_SAVE_AREA FloatSave;
+
+ DWORD SegGs;
+ DWORD SegFs;
+ DWORD SegEs;
+ DWORD SegDs;
+
+ DWORD Edi;
+ DWORD Esi;
+ DWORD Ebx;
+ DWORD Edx;
+ DWORD Ecx;
+ DWORD Eax;
+
+ DWORD Ebp;
+ DWORD Eip;
+ DWORD SegCs;
+ DWORD EFlags;
+ DWORD Esp;
+ DWORD SegSs;
+
+ BYTE ExtendedRegisters[X86_MAXIMUM_SUPPORTED_EXTENSION];
+
+} X86_CONTEXT;
+
+typedef struct {
+ ULONGLONG Low;
+ LONGLONG High;
+} M128A_XPLAT;
+
+
+/// AMD64 Context
+typedef struct {
+ WORD ControlWord;
+ WORD StatusWord;
+ BYTE TagWord;
+ BYTE Reserved1;
+ WORD ErrorOpcode;
+ DWORD ErrorOffset;
+ WORD ErrorSelector;
+ WORD Reserved2;
+ DWORD DataOffset;
+ WORD DataSelector;
+ WORD Reserved3;
+ DWORD MxCsr;
+ DWORD MxCsr_Mask;
+ M128A_XPLAT FloatRegisters[8];
+
+#if defined(_WIN64)
+ M128A_XPLAT XmmRegisters[16];
+ BYTE Reserved4[96];
+#else
+ M128A_XPLAT XmmRegisters[8];
+ BYTE Reserved4[220];
+
+ DWORD Cr0NpxState;
+#endif
+
+} AMD64_XMM_SAVE_AREA32;
+
+typedef struct {
+
+ DWORD64 P1Home;
+ DWORD64 P2Home;
+ DWORD64 P3Home;
+ DWORD64 P4Home;
+ DWORD64 P5Home;
+ DWORD64 P6Home;
+
+ DWORD ContextFlags;
+ DWORD MxCsr;
+
+ WORD SegCs;
+ WORD SegDs;
+ WORD SegEs;
+ WORD SegFs;
+ WORD SegGs;
+ WORD SegSs;
+ DWORD EFlags;
+
+ DWORD64 Dr0;
+ DWORD64 Dr1;
+ DWORD64 Dr2;
+ DWORD64 Dr3;
+ DWORD64 Dr6;
+ DWORD64 Dr7;
+
+ DWORD64 Rax;
+ DWORD64 Rcx;
+ DWORD64 Rdx;
+ DWORD64 Rbx;
+ DWORD64 Rsp;
+ DWORD64 Rbp;
+ DWORD64 Rsi;
+ DWORD64 Rdi;
+ DWORD64 R8;
+ DWORD64 R9;
+ DWORD64 R10;
+ DWORD64 R11;
+ DWORD64 R12;
+ DWORD64 R13;
+ DWORD64 R14;
+ DWORD64 R15;
+
+ DWORD64 Rip;
+
+ union {
+ AMD64_XMM_SAVE_AREA32 FltSave;
+ struct {
+ M128A_XPLAT Header[2];
+ M128A_XPLAT Legacy[8];
+ M128A_XPLAT Xmm0;
+ M128A_XPLAT Xmm1;
+ M128A_XPLAT Xmm2;
+ M128A_XPLAT Xmm3;
+ M128A_XPLAT Xmm4;
+ M128A_XPLAT Xmm5;
+ M128A_XPLAT Xmm6;
+ M128A_XPLAT Xmm7;
+ M128A_XPLAT Xmm8;
+ M128A_XPLAT Xmm9;
+ M128A_XPLAT Xmm10;
+ M128A_XPLAT Xmm11;
+ M128A_XPLAT Xmm12;
+ M128A_XPLAT Xmm13;
+ M128A_XPLAT Xmm14;
+ M128A_XPLAT Xmm15;
+ } DUMMYSTRUCTNAME;
+ } DUMMYUNIONNAME;
+
+ M128A_XPLAT VectorRegister[26];
+ DWORD64 VectorControl;
+
+ DWORD64 DebugControl;
+ DWORD64 LastBranchToRip;
+ DWORD64 LastBranchFromRip;
+ DWORD64 LastExceptionToRip;
+ DWORD64 LastExceptionFromRip;
+
+} AMD64_CONTEXT;
+
+typedef struct{
+ __int64 LowPart;
+ __int64 HighPart;
+} FLOAT128_XPLAT;
+
+
+/// ARM Context
+#define ARM_MAX_BREAKPOINTS_CONST 8
+#define ARM_MAX_WATCHPOINTS_CONST 4
+typedef struct {
+
+ DWORD ContextFlags;
+
+ DWORD R0;
+ DWORD R1;
+ DWORD R2;
+ DWORD R3;
+ DWORD R4;
+ DWORD R5;
+ DWORD R6;
+ DWORD R7;
+ DWORD R8;
+ DWORD R9;
+ DWORD R10;
+ DWORD R11;
+ DWORD R12;
+
+ DWORD Sp;
+ DWORD Lr;
+ DWORD Pc;
+ DWORD Cpsr;
+
+ DWORD Fpscr;
+ union {
+ M128A_XPLAT Q[16];
+ ULONGLONG D[32];
+ DWORD S[32];
+ } DUMMYUNIONNAME;
+
+ DWORD Bvr[ARM_MAX_BREAKPOINTS_CONST];
+ DWORD Bcr[ARM_MAX_BREAKPOINTS_CONST];
+ DWORD Wvr[ARM_MAX_WATCHPOINTS_CONST];
+ DWORD Wcr[ARM_MAX_WATCHPOINTS_CONST];
+
+} ARM_CONTEXT;
+
+// On ARM this mask is or'ed with the address of code to get an instruction pointer
+#ifndef THUMB_CODE
+#define THUMB_CODE 1
+#endif
+
+///ARM64 Context
+#define ARM64_MAX_BREAKPOINTS 8
+#define ARM64_MAX_WATCHPOINTS 2
+typedef struct {
+
+ DWORD ContextFlags;
+ DWORD Cpsr; // NZVF + DAIF + CurrentEL + SPSel
+ union {
+ struct {
+ DWORD64 X0;
+ DWORD64 X1;
+ DWORD64 X2;
+ DWORD64 X3;
+ DWORD64 X4;
+ DWORD64 X5;
+ DWORD64 X6;
+ DWORD64 X7;
+ DWORD64 X8;
+ DWORD64 X9;
+ DWORD64 X10;
+ DWORD64 X11;
+ DWORD64 X12;
+ DWORD64 X13;
+ DWORD64 X14;
+ DWORD64 X15;
+ DWORD64 X16;
+ DWORD64 X17;
+ DWORD64 X18;
+ DWORD64 X19;
+ DWORD64 X20;
+ DWORD64 X21;
+ DWORD64 X22;
+ DWORD64 X23;
+ DWORD64 X24;
+ DWORD64 X25;
+ DWORD64 X26;
+ DWORD64 X27;
+ DWORD64 X28;
+ };
+
+ DWORD64 X[29];
+ };
+
+ DWORD64 Fp;
+ DWORD64 Lr;
+ DWORD64 Sp;
+ DWORD64 Pc;
+
+
+ M128A_XPLAT V[32];
+ DWORD Fpcr;
+ DWORD Fpsr;
+
+ DWORD Bcr[ARM64_MAX_BREAKPOINTS];
+ DWORD64 Bvr[ARM64_MAX_BREAKPOINTS];
+ DWORD Wcr[ARM64_MAX_WATCHPOINTS];
+ DWORD64 Wvr[ARM64_MAX_WATCHPOINTS];
+
+} ARM64_CONTEXT;
+
+typedef struct _CROSS_PLATFORM_CONTEXT {
+
+ _CROSS_PLATFORM_CONTEXT() {}
+
+ union {
+ X86_CONTEXT X86Context;
+ AMD64_CONTEXT Amd64Context;
+ ARM_CONTEXT ArmContext;
+ ARM64_CONTEXT Arm64Context;
+ };
+
+} CROSS_PLATFORM_CONTEXT, *PCROSS_PLATFORM_CONTEXT;
+
+
+
+WString BuildRegisterOutput(const SOSStackRefData &ref, bool printObj = true);
+WString MethodNameFromIP(CLRDATA_ADDRESS methodDesc, BOOL bSuppressLines = FALSE, BOOL bAssemblyName = FALSE, BOOL bDisplacement = FALSE);
+HRESULT GetGCRefs(ULONG osID, SOSStackRefData **ppRefs, unsigned int *pRefCnt, SOSStackRefError **ppErrors, unsigned int *pErrCount);
+WString GetFrameFromAddress(TADDR frameAddr, IXCLRDataStackWalk *pStackwalk = NULL, BOOL bAssemblyName = FALSE);
+
+/* This cache is used to read data from the target process if the reads are known
+ * to be sequential.
+ */
+class LinearReadCache
+{
+public:
+ LinearReadCache(ULONG pageSize = 0x10000);
+ ~LinearReadCache();
+
+ /* Reads an address out of the target process, caching the page of memory read.
+ * Params:
+ * addr - The address to read out of the target process.
+ * t - A pointer to the data to stuff it in. We will read sizeof(T) data
+ * from the process and write it into the location t points to. This
+ * parameter must be non-null.
+ * Returns:
+ * True if the read succeeded. False if it did not, usually as a result
+ * of the memory simply not being present in the target process.
+ * Note:
+ * The state of *t is undefined if this function returns false. We may
+ * have written partial data to it if we return false, so you must
+ * absolutely NOT use it if Read returns false.
+ */
+ template <class T>
+ bool Read(TADDR addr, T *t, bool update = true)
+ {
+ _ASSERTE(t);
+
+ // Unfortunately the ctor can fail the alloc for the byte array. In this case
+ // we'll just fall back to non-cached reads.
+ if (mPage == NULL)
+ return MisalignedRead(addr, t);
+
+ // Is addr on the current page? If not read the page of memory addr is on.
+ // If this fails, we will fall back to a raw read out of the process (which
+ // is what MisalignedRead does).
+ if ((addr < mCurrPageStart) || (addr - mCurrPageStart > mCurrPageSize))
+ if (!update || !MoveToPage(addr))
+ return MisalignedRead(addr, t);
+
+ // If MoveToPage succeeds, we MUST be on the right page.
+ _ASSERTE(addr >= mCurrPageStart);
+
+ // However, the amount of data requested may fall off of the page. In that case,
+ // fall back to MisalignedRead.
+ TADDR offset = addr - mCurrPageStart;
+ if (offset + sizeof(T) > mCurrPageSize)
+ return MisalignedRead(addr, t);
+
+ // If we reach here we know we are on the right page of memory in the cache, and
+ // that the read won't fall off of the end of the page.
+#ifdef _DEBUG
+ mReads++;
+#endif
+
+ *t = *reinterpret_cast<T*>(mPage+offset);
+ return true;
+ }
+
+ void EnsureRangeInCache(TADDR start, unsigned int size)
+ {
+ if (mCurrPageStart == start)
+ {
+ if (size <= mCurrPageSize)
+ return;
+
+ // Total bytes to read, don't overflow buffer.
+ unsigned int total = size + mCurrPageSize;
+ if (total + mCurrPageSize > mPageSize)
+ total = mPageSize-mCurrPageSize;
+
+ // Read into the middle of the buffer, update current page size.
+ ULONG read = 0;
+ HRESULT hr = g_ExtData->ReadVirtual(mCurrPageStart+mCurrPageSize, mPage+mCurrPageSize, total, &read);
+ mCurrPageSize += read;
+
+ if (hr != S_OK)
+ {
+ mCurrPageStart = 0;
+ mCurrPageSize = 0;
+ }
+ }
+ else
+ {
+ MoveToPage(start, size);
+ }
+ }
+
+ void ClearStats()
+ {
+#ifdef _DEBUG
+ mMisses = 0;
+ mReads = 0;
+ mMisaligned = 0;
+#endif
+ }
+
+ void PrintStats(const char *func)
+ {
+#ifdef _DEBUG
+ char buffer[1024];
+ sprintf_s(buffer, _countof(buffer), "Cache (%s): %d reads (%2.1f%% hits), %d misses (%2.1f%%), %d misaligned (%2.1f%%).\n",
+ func, mReads, 100*(mReads-mMisses)/(float)(mReads+mMisaligned), mMisses,
+ 100*mMisses/(float)(mReads+mMisaligned), mMisaligned, 100*mMisaligned/(float)(mReads+mMisaligned));
+ OutputDebugStringA(buffer);
+#endif
+ }
+
+private:
+ /* Sets the cache to the page specified by addr, or false if we could not move to
+ * that page.
+ */
+ bool MoveToPage(TADDR addr, unsigned int size = 0x18);
+
+ /* Attempts to read from the target process if the data is possibly hanging off
+ * the end of a page.
+ */
+ template<class T>
+ inline bool MisalignedRead(TADDR addr, T *t)
+ {
+ ULONG fetched = 0;
+ HRESULT hr = g_ExtData->ReadVirtual(addr, (BYTE*)t, sizeof(T), &fetched);
+
+ if (FAILED(hr) || fetched != sizeof(T))
+ return false;
+
+ mMisaligned++;
+ return true;
+ }
+
+private:
+ TADDR mCurrPageStart;
+ ULONG mPageSize, mCurrPageSize;
+ BYTE *mPage;
+
+ int mMisses, mReads, mMisaligned;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+//
+// Methods for creating a database out of the gc heap and it's roots in xml format or CLRProfiler format
+//
+
+#include <unordered_map>
+#include <unordered_set>
+#include <list>
+
+class TypeTree;
+enum { FORMAT_XML=0, FORMAT_CLRPROFILER=1 };
+enum { TYPE_START=0,TYPE_TYPES=1,TYPE_ROOTS=2,TYPE_OBJECTS=3,TYPE_HIGHEST=4};
+class HeapTraverser
+{
+private:
+ TypeTree *m_pTypeTree;
+ size_t m_curNID;
+ FILE *m_file;
+ int m_format; // from the enum above
+ size_t m_objVisited; // for UI updates
+ bool m_verify;
+ LinearReadCache mCache;
+
+ std::unordered_map<TADDR, std::list<TADDR>> mDependentHandleMap;
+
+public:
+ HeapTraverser(bool verify);
+ ~HeapTraverser();
+
+ FILE *getFile() { return m_file; }
+
+ BOOL Initialize();
+ BOOL CreateReport (FILE *fp, int format);
+
+private:
+ // First all types are added to a tree
+ void insert(size_t mTable);
+ size_t getID(size_t mTable);
+
+ // Functions for writing to the output file.
+ void PrintType(size_t ID,LPCWSTR name);
+
+ void PrintObjectHead(size_t objAddr,size_t typeID,size_t Size);
+ void PrintObjectMember(size_t memberValue, bool dependentHandle);
+ void PrintObjectTail();
+
+ void PrintRootHead();
+ void PrintRoot(LPCWSTR kind,size_t Value);
+ void PrintRootTail();
+
+ void PrintSection(int Type,BOOL bOpening);
+
+ // Root and object member helper functions
+ void FindGCRootOnStacks();
+ void PrintRefs(size_t obj, size_t methodTable, size_t size);
+
+ // Callback functions used during traversals
+ static void GatherTypes(DWORD_PTR objAddr,size_t Size,DWORD_PTR methodTable, LPVOID token);
+ static void PrintHeap(DWORD_PTR objAddr,size_t Size,DWORD_PTR methodTable, LPVOID token);
+ static void PrintOutTree(size_t methodTable, size_t ID, LPVOID token);
+ void TraceHandles();
+};
+
+
+class GCRootImpl
+{
+private:
+ struct MTInfo
+ {
+ TADDR MethodTable;
+ WCHAR *TypeName;
+
+ TADDR *Buffer;
+ CGCDesc *GCDesc;
+
+ bool ArrayOfVC;
+ bool ContainsPointers;
+ size_t BaseSize;
+ size_t ComponentSize;
+
+ const WCHAR *GetTypeName()
+ {
+ if (!TypeName)
+ TypeName = CreateMethodTableName(MethodTable);
+
+ if (!TypeName)
+ return W("<error>");
+
+ return TypeName;
+ }
+
+ MTInfo()
+ : MethodTable(0), TypeName(0), Buffer(0), GCDesc(0),
+ ArrayOfVC(false), ContainsPointers(false), BaseSize(0), ComponentSize(0)
+ {
+ }
+
+ ~MTInfo()
+ {
+ if (Buffer)
+ delete [] Buffer;
+
+ if (TypeName)
+ delete [] TypeName;
+ }
+ };
+
+ struct RootNode
+ {
+ RootNode *Next;
+ RootNode *Prev;
+ TADDR Object;
+ MTInfo *MTInfo;
+
+ bool FilledRefs;
+ bool FromDependentHandle;
+ RootNode *GCRefs;
+
+
+ const WCHAR *GetTypeName()
+ {
+ if (!MTInfo)
+ return W("<unknown>");
+
+ return MTInfo->GetTypeName();
+ }
+
+ RootNode()
+ : Next(0), Prev(0)
+ {
+ Clear();
+ }
+
+ void Clear()
+ {
+ if (Next && Next->Prev == this)
+ Next->Prev = NULL;
+
+ if (Prev && Prev->Next == this)
+ Prev->Next = NULL;
+
+ Next = 0;
+ Prev = 0;
+ Object = 0;
+ MTInfo = 0;
+ FilledRefs = false;
+ FromDependentHandle = false;
+ GCRefs = 0;
+ }
+
+ void Remove(RootNode *&list)
+ {
+ RootNode *curr_next = Next;
+
+ // We've already considered this object, remove it.
+ if (Prev == NULL)
+ {
+ // If we've filtered out the head, update it.
+ list = curr_next;
+
+ if (curr_next)
+ curr_next->Prev = NULL;
+ }
+ else
+ {
+ // Otherwise remove the current item from the list
+ Prev->Next = curr_next;
+
+ if (curr_next)
+ curr_next->Prev = Prev;
+ }
+ }
+ };
+
+public:
+ static void GetDependentHandleMap(std::unordered_map<TADDR, std::list<TADDR>> &map);
+
+public:
+ // Finds all objects which root "target" and prints the path from the root
+ // to "target". If all is true, all possible paths to the object are printed.
+ // If all is false, only completely unique paths will be printed.
+ int PrintRootsForObject(TADDR obj, bool all, bool noStacks);
+
+ // Finds a path from root to target if it exists and prints it out. Returns
+ // true if it found a path, false otherwise.
+ bool PrintPathToObject(TADDR root, TADDR target);
+
+ // Calculates the size of the closure of objects kept alive by root.
+ size_t ObjSize(TADDR root);
+
+ // Walks each root, printing out the total amount of memory held alive by it.
+ void ObjSize();
+
+ // Returns the set of all live objects in the process.
+ const std::unordered_set<TADDR> &GetLiveObjects(bool excludeFQ = false);
+
+ // See !FindRoots.
+ int FindRoots(int gen, TADDR target);
+
+private:
+ // typedefs
+ typedef void (*ReportCallback)(TADDR root, RootNode *path, bool printHeader);
+
+ // Book keeping and debug.
+ void ClearAll();
+ void ClearNodes();
+ void ClearSizeData();
+
+ // Printing roots
+ int PrintRootsOnHandleTable(int gen = -1);
+ int PrintRootsOnAllThreads();
+ int PrintRootsOnThread(DWORD osThreadId);
+ int PrintRootsOnFQ(bool notReadyForFinalization = false);
+ int PrintRootsInOlderGen();
+ int PrintRootsInRange(LinearReadCache &cache, TADDR start, TADDR stop, ReportCallback func, bool printHeader);
+
+ // Calculate gc root
+ RootNode *FilterRoots(RootNode *&list);
+ RootNode *FindPathToTarget(TADDR root);
+ RootNode *GetGCRefs(RootNode *path, RootNode *node);
+
+ void InitDependentHandleMap();
+
+ //Reporting:
+ void ReportOneHandlePath(const SOSHandleData &handle, RootNode *node, bool printHeader);
+ void ReportOnePath(DWORD thread, const SOSStackRefData &stackRef, RootNode *node, bool printThread, bool printFrame);
+ static void ReportOneFQEntry(TADDR root, RootNode *path, bool printHeader);
+ static void ReportOlderGenEntry(TADDR root, RootNode *path, bool printHeader);
+ void ReportSizeInfo(const SOSHandleData &handle, TADDR obj);
+ void ReportSizeInfo(DWORD thread, const SOSStackRefData &ref, TADDR obj);
+
+ // Data reads:
+ TADDR ReadPointer(TADDR location);
+ TADDR ReadPointerCached(TADDR location);
+
+ // Object/MT data:
+ MTInfo *GetMTInfo(TADDR mt);
+ DWORD GetComponents(TADDR obj, TADDR mt);
+ size_t GetSizeOfObject(TADDR obj, MTInfo *info);
+
+ // RootNode management:
+ RootNode *NewNode(TADDR obj = 0, MTInfo *mtinfo = 0, bool fromDependent = false);
+ void DeleteNode(RootNode *node);
+
+private:
+
+ bool mAll, // Print all roots or just unique roots?
+ mSize; // Print rooting information or total size info?
+
+ std::list<RootNode*> mCleanupList; // A list of RootNode's we've newed up. This is only used to delete all of them later.
+ std::list<RootNode*> mRootNewList; // A list of unused RootNodes that are free to use instead of having to "new" up more.
+
+ std::unordered_map<TADDR, MTInfo*> mMTs; // The MethodTable cache which maps from MT -> MethodTable data (size, gcdesc, string typename)
+ std::unordered_map<TADDR, RootNode*> mTargets; // The objects that we are searching for.
+ std::unordered_set<TADDR> mConsidered; // A hashtable of objects we've already visited.
+ std::unordered_map<TADDR, size_t> mSizes; // A mapping from object address to total size of data the object roots.
+
+ std::unordered_map<TADDR, std::list<TADDR>> mDependentHandleMap;
+
+ LinearReadCache mCache; // A linear cache which stops us from having to read from the target process more than 1-2 times per object.
+};
+
+//
+// Helper class used for type-safe bitflags
+// T - the enum type specifying the individual bit flags
+// U - the underlying/storage type
+// Requirement:
+// sizeof(T) <= sizeof(U)
+//
+template <typename T, typename U>
+struct Flags
+{
+ typedef T UnderlyingType;
+ typedef U BitFlagEnumType;
+
+ static_assert_no_msg(sizeof(BitFlagEnumType) <= sizeof(UnderlyingType));
+
+ Flags(UnderlyingType v)
+ : m_val(v)
+ { }
+
+ Flags(BitFlagEnumType v)
+ : m_val(v)
+ { }
+
+ Flags(const Flags& other)
+ : m_val(other.m_val)
+ { }
+
+ Flags& operator = (const Flags& other)
+ { m_val = other.m_val; return *this; }
+
+ Flags operator | (Flags other) const
+ { return Flags<T, U>(m_val | other._val); }
+
+ void operator |= (Flags other)
+ { m_val |= other.m_val; }
+
+ Flags operator & (Flags other) const
+ { return Flags<T, U>(m_val & other.m_val); }
+
+ void operator &= (Flags other)
+ { m_val &= other.m_val; }
+
+ Flags operator ^ (Flags other) const
+ { return Flags<T, U>(m_val ^ other._val); }
+
+ void operator ^= (Flags other)
+ { m_val ^= other.m_val; }
+
+ BOOL operator == (Flags other) const
+ { return m_val == other.m_val; }
+
+ BOOL operator != (Flags other) const
+ { return m_val != other.m_val; }
+
+
+private:
+ UnderlyingType m_val;
+};
+
+#ifndef FEATURE_PAL
+
+// Flags defining activation policy for COM objects
+enum CIOptionsBits
+{
+ cciLatestFx = 0x01, // look in the most recent .NETFx installation
+ cciMatchFx = 0x02, // NYI: Look in the .NETFx installation matching the debuggee's runtime
+ cciAnyFx = 0x04, // look in any .NETFx installation
+ cciFxMask = 0x0f,
+ cciDbiColocated = 0x10, // NYI: Look next to the already loaded DBI module
+ cciDacColocated = 0x20, // Look next to the already loaded DAC module
+ cciDbgPath = 0x40, // Look in all folders in the debuggers symbols and binary path
+};
+
+typedef Flags<DWORD, CIOptionsBits> CIOptions;
+
+/**********************************************************************\
+* Routine Description: *
+* *
+* CreateInstanceCustom() provides a way to activate a COM object w/o *
+* triggering the FeatureOnDemand dialog. In order to do this we *
+* must avoid using the CoCreateInstance() API, which, on a machine *
+* with v4+ installed and w/o v2, would trigger this. *
+* CreateInstanceCustom() activates the requested COM object according *
+* to the specified passed in CIOptions, in the following order *
+* (skipping the steps not enabled in the CIOptions flags passed in): *
+* 1. Attempt to activate the COM object using a framework install: *
+* a. If the debugger machine has a V4+ shell shim use the shim *
+* to activate the object *
+* b. Otherwise simply call CoCreateInstance *
+* 2. If unsuccessful attempt to activate looking for the dllName in *
+* the same folder as the DAC was loaded from *
+* 3. If unsuccessful attempt to activate the COM object looking in *
+* every path specified in the debugger's .exepath and .sympath *
+\**********************************************************************/
+HRESULT CreateInstanceCustom(
+ REFCLSID clsid,
+ REFIID iid,
+ LPCWSTR dllName,
+ CIOptions cciOptions,
+ void** ppItf);
+
+
+//------------------------------------------------------------------------
+// A typesafe version of GetProcAddress
+//------------------------------------------------------------------------
+template <typename T>
+BOOL
+GetProcAddressT(
+ ___in PCSTR FunctionName,
+ __in_opt PCWSTR DllName,
+ __inout T* OutFunctionPointer,
+ __inout HMODULE* InOutDllHandle
+ )
+{
+ _ASSERTE(InOutDllHandle != NULL);
+ _ASSERTE(OutFunctionPointer != NULL);
+
+ T FunctionPointer = NULL;
+ HMODULE DllHandle = *InOutDllHandle;
+ if (DllHandle == NULL)
+ {
+ DllHandle = LoadLibraryExW(DllName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
+ if (DllHandle != NULL)
+ *InOutDllHandle = DllHandle;
+ }
+ if (DllHandle != NULL)
+ {
+ FunctionPointer = (T) GetProcAddress(DllHandle, FunctionName);
+ }
+ *OutFunctionPointer = FunctionPointer;
+ return FunctionPointer != NULL;
+}
+
+
+#endif // FEATURE_PAL
+
+struct ImageInfo
+{
+ ULONG64 modBase;
+};
+
+// Helper class used in ClrStackFromPublicInterface() to keep track of explicit EE Frames
+// (i.e., "internal frames") on the stack. Call Init() with the appropriate
+// ICorDebugThread3, and this class will initialize itself with the set of internal
+// frames. You can then call PrintPrecedingInternalFrames during your stack walk to
+// have this class output any internal frames that "precede" (i.e., that are closer to
+// the leaf than) the specified ICorDebugFrame.
+class InternalFrameManager
+{
+private:
+ // TODO: Verify constructor AND destructor is called for each array element
+ // TODO: Comment about hard-coding 1000
+ ToRelease<ICorDebugInternalFrame2> m_rgpInternalFrame2[1000];
+ ULONG32 m_cInternalFramesActual;
+ ULONG32 m_iInternalFrameCur;
+
+public:
+ InternalFrameManager();
+ HRESULT Init(ICorDebugThread3 * pThread3);
+ HRESULT PrintPrecedingInternalFrames(ICorDebugFrame * pFrame);
+
+private:
+ HRESULT PrintCurrentInternalFrame();
+};
+
+#endif // __util_h__
diff --git a/src/ToolBox/SOS/Strike/vm.cpp b/src/ToolBox/SOS/Strike/vm.cpp
new file mode 100644
index 0000000000..e7e5701fc6
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/vm.cpp
@@ -0,0 +1,732 @@
+// 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.
+
+// ==++==
+//
+
+//
+// ==--==
+/*++
+
+Module Name:
+
+ vm.cxx
+
+Abstract:
+
+ This module contains an NTSD debugger extension for dumping various
+ virtual memory statistics.
+
+Revision History:
+
+--*/
+
+#ifndef FEATURE_PAL
+#include <tchar.h>
+
+
+#include "strike.h"
+#include "util.h"
+#include "gcinfo.h"
+#include "disasm.h"
+#include <dbghelp.h>
+
+#include "corhdr.h"
+#include "cor.h"
+#include "dacprivate.h"
+
+
+
+//
+// Private constants.
+//
+
+#define SMALL_REGION (64 * 1024)
+#define MEDIUM_REGION (1 * 1024 * 1024)
+
+#define IS_SMALL(c) ((c) <= SMALL_REGION)
+#define IS_MEDIUM(c) (((c) > SMALL_REGION) && ((c) <= MEDIUM_REGION))
+#define IS_LARGE(c) ((c) > MEDIUM_REGION)
+
+#define PRINTF_FORMAT_HEAD "%-7s %*s %*s %*s %*s %*s\n"
+#define PRINTF_FORMAT "%-7s %*sK %*sK %*sK %*s %*sK\n"
+
+#define CCH_ULONGLONG_COMMAS _countof("18,446,744,073,709,551,616")
+#define CCH_ULONGLONG_MINIMUM_COMMAS (CCH_ULONGLONG_COMMAS - 3)
+#define CCH_ULONGLONG_BLOCKCOUNT_COMMAS sizeof("1,000,000")
+
+
+//
+// Private types.
+//
+
+typedef struct _INDIVIDUAL_STAT
+{
+ SIZE_T MinimumSize;
+ SIZE_T MaximumSize;
+ SIZE_T TotalSize;
+ SIZE_T BlockCount;
+
+} INDIVIDUAL_STAT, *PINDIVIDUAL_STAT;
+
+typedef struct _VM_STATS
+{
+ INDIVIDUAL_STAT Summary;
+ INDIVIDUAL_STAT Small;
+ INDIVIDUAL_STAT Medium;
+ INDIVIDUAL_STAT Large;
+
+} VM_STATS, *PVM_STATS;
+
+typedef struct PROTECT_MASK
+{
+ DWORD Bit;
+ PSTR Name;
+
+} PROTECT_MASK, *PPROTECT_MASK;
+
+
+//
+// Private globals.
+//
+
+PROTECT_MASK ProtectMasks[] =
+ {
+ {
+ PAGE_NOACCESS,
+ "NA"
+ },
+
+ {
+ PAGE_NOCACHE,
+ "NC"
+ },
+
+ {
+ PAGE_GUARD,
+ "G"
+ },
+
+ {
+ PAGE_READONLY,
+ "Rd"
+ },
+
+ {
+ PAGE_READWRITE,
+ "RdWr"
+ },
+
+ {
+ PAGE_WRITECOPY,
+ "WrCp"
+ },
+
+ {
+ PAGE_EXECUTE,
+ "Ex"
+ },
+
+ {
+ PAGE_EXECUTE_READ,
+ "ExRd"
+ },
+
+ {
+ PAGE_EXECUTE_READWRITE,
+ "ExRdWr"
+ },
+
+ {
+ PAGE_EXECUTE_WRITECOPY,
+ "ExWrCp"
+ }
+ };
+
+#define NUM_PROTECT_MASKS (sizeof(ProtectMasks) / sizeof(ProtectMasks[0]))
+
+//
+// Private functions.
+//
+
+
+PSTR
+ULongLongToString(
+ IN ULONGLONG Value,
+ __out_ecount (CCH_ULONGLONG_COMMAS) OUT PSTR Buffer
+ )
+{
+
+ PSTR p1;
+ PSTR p2;
+ CHAR ch;
+ INT digit;
+ INT count;
+ BOOL needComma;
+ INT length;
+
+ //
+ // Handling zero specially makes everything else a bit easier.
+ //
+
+ if( Value == 0 ) {
+ Buffer[0] = '0';
+ Buffer[1] = '\0';
+ return Buffer;
+ }
+
+ //
+ // Pull the least signifigant digits off the value and store them
+ // into the buffer. Note that this will store the digits in the
+ // reverse order.
+ //
+
+ p1 = p2 = Buffer;
+ count = 3;
+ needComma = FALSE;
+
+ while( Value != 0 ) {
+
+ if( needComma ) {
+ *p1++ = ',';
+ needComma = FALSE;
+ }
+
+ digit = (INT)( Value % 10 );
+ Value = Value / 10;
+
+ *p1++ = '0' + (CHAR) digit;
+
+ count--;
+ if( count == 0 ) {
+ count = 3;
+ needComma = TRUE;
+ }
+
+ }
+
+ length = (INT)(((size_t)p1) - ((size_t)Buffer));
+
+ //
+ // Reverse the digits in the buffer.
+ //
+
+ *p1-- = '\0';
+
+ while( p1 > p2 ) {
+
+ ch = *p1;
+ *p1 = *p2;
+ *p2 = ch;
+
+ p2++;
+ p1--;
+
+ }
+
+ return Buffer;
+
+} // ULongLongToString
+
+VOID
+InitVmStats(
+ OUT PVM_STATS Stats
+ )
+{
+ ZeroMemory( Stats, sizeof(*Stats) );
+ Stats->Summary.MinimumSize = (SIZE_T)-1L;
+ Stats->Small.MinimumSize = (SIZE_T)-1L;
+ Stats->Medium.MinimumSize = (SIZE_T)-1L;
+ Stats->Large.MinimumSize = (SIZE_T)-1L;
+
+} // InitVmStats
+
+VOID
+UpdateIndividualStat(
+ IN OUT PINDIVIDUAL_STAT Stat,
+ IN SIZE_T BlockSize
+ )
+{
+ Stat->BlockCount++;
+ Stat->TotalSize += BlockSize;
+
+ if( BlockSize > Stat->MaximumSize ) {
+ Stat->MaximumSize = BlockSize;
+ }
+
+ if( BlockSize < Stat->MinimumSize ) {
+ Stat->MinimumSize = BlockSize;
+ }
+
+} // UpdateIndividualStat
+
+VOID
+UpdateVmStats(
+ IN OUT PVM_STATS Stats,
+ IN SIZE_T BlockSize
+ )
+{
+ UpdateIndividualStat( &Stats->Summary, BlockSize );
+
+ if( IS_SMALL(BlockSize) ) {
+ UpdateIndividualStat( &Stats->Small, BlockSize );
+ }
+
+ if( IS_MEDIUM(BlockSize) ) {
+ UpdateIndividualStat( &Stats->Medium, BlockSize );
+ }
+
+ if( IS_LARGE(BlockSize) ) {
+ UpdateIndividualStat( &Stats->Large, BlockSize );
+ }
+
+} // UpdateVmStats
+
+VOID
+PrintVmStatsHeader(
+ VOID
+ )
+{
+ ExtOut(
+ PRINTF_FORMAT_HEAD,
+ "TYPE",
+ CCH_ULONGLONG_MINIMUM_COMMAS,
+ "MINIMUM",
+ CCH_ULONGLONG_COMMAS,
+ "MAXIMUM",
+ CCH_ULONGLONG_COMMAS,
+ "AVERAGE",
+ CCH_ULONGLONG_BLOCKCOUNT_COMMAS,
+ "BLK COUNT",
+ CCH_ULONGLONG_COMMAS,
+ "TOTAL"
+ );
+
+ ExtOut(
+ PRINTF_FORMAT_HEAD,
+ "~~~~",
+ CCH_ULONGLONG_MINIMUM_COMMAS,
+ "~~~~~~~",
+ CCH_ULONGLONG_COMMAS,
+ "~~~~~~~",
+ CCH_ULONGLONG_COMMAS,
+ "~~~~~~~",
+ CCH_ULONGLONG_BLOCKCOUNT_COMMAS,
+ "~~~~~~~~~",
+ CCH_ULONGLONG_COMMAS,
+ "~~~~~"
+ );
+
+} // PrintVmStatsHeader
+
+#define BYTES_TO_K(x) (x/1024)
+
+VOID
+PrintIndividualStat(
+ ___in __in_z IN PSTR Name,
+ IN PINDIVIDUAL_STAT Stat
+ )
+{
+ SIZE_T average;
+ SIZE_T minsize;
+ CHAR minStr[CCH_ULONGLONG_COMMAS];
+ CHAR maxStr[CCH_ULONGLONG_COMMAS];
+ CHAR avgStr[CCH_ULONGLONG_COMMAS];
+ CHAR countStr[CCH_ULONGLONG_COMMAS];
+ CHAR totalStr[CCH_ULONGLONG_COMMAS];
+
+ if( Stat->BlockCount == 0 ) {
+ average = 0;
+ minsize = 0;
+ } else {
+ average = Stat->TotalSize / Stat->BlockCount;
+ minsize = Stat->MinimumSize;
+ }
+
+ ExtOut(
+ PRINTF_FORMAT,
+ Name,
+ CCH_ULONGLONG_MINIMUM_COMMAS,
+ ULongLongToString(
+ (ULONGLONG)BYTES_TO_K(minsize),
+ minStr
+ ),
+ CCH_ULONGLONG_COMMAS,
+ ULongLongToString(
+ (ULONGLONG)BYTES_TO_K(Stat->MaximumSize),
+ maxStr
+ ),
+ CCH_ULONGLONG_COMMAS,
+ ULongLongToString(
+ (ULONGLONG)BYTES_TO_K(average),
+ avgStr
+ ),
+ CCH_ULONGLONG_BLOCKCOUNT_COMMAS,
+ ULongLongToString(
+ (ULONGLONG)Stat->BlockCount,
+ countStr
+ ),
+ CCH_ULONGLONG_COMMAS,
+ ULongLongToString(
+ (ULONGLONG)BYTES_TO_K(Stat->BlockCount * average),
+ totalStr
+ )
+ );
+
+} // PrintIndividualStat
+
+
+VOID
+PrintVmStats(
+ ___in __in_z IN PSTR Name,
+ IN PVM_STATS Stats
+ )
+{
+ ExtOut( "%s:\n", Name );
+
+ PrintIndividualStat( "Small", &Stats->Small );
+ PrintIndividualStat( "Medium", &Stats->Medium );
+ PrintIndividualStat( "Large", &Stats->Large );
+ PrintIndividualStat( "Summary", &Stats->Summary );
+
+ ExtOut( "\n" );
+
+} // PrintVmStats
+
+PSTR
+VmProtectToString(
+ IN DWORD Protect,
+ __out_ecount(capacity_Buffer) OUT PSTR Buffer,
+ size_t capacity_Buffer
+ )
+{
+ INT i;
+ PPROTECT_MASK mask;
+
+ Buffer[0] = '\0';
+
+ for( i = 0, mask = &ProtectMasks[0] ;
+ (i < NUM_PROTECT_MASKS) && (Protect != 0) ;
+ i++, mask++ ) {
+ if( mask->Bit & Protect ) {
+ Protect &= ~mask->Bit;
+ if( Buffer[0] != '\0' ) {
+ strcat_s(Buffer,capacity_Buffer, "|" );
+ }
+ strcat_s( Buffer, capacity_Buffer, mask->Name );
+ }
+ }
+
+ if( Protect != 0 ) {
+ if( Buffer[0] != '\0' ) {
+ strcat_s( Buffer, capacity_Buffer, "|" );
+ }
+ size_t len_Buffer = strlen(Buffer);
+ size_t cbSizeInBytes = 0;
+ if (!ClrSafeInt<size_t>::subtraction(capacity_Buffer, len_Buffer, cbSizeInBytes))
+ {
+ ExtOut("<integer underflow>\n");
+ return Buffer;
+ }
+ sprintf_s( Buffer + len_Buffer, cbSizeInBytes, "%08lx", Protect );
+ }
+
+ return Buffer;
+
+} // VmProtectToString
+
+PSTR
+VmStateToString(
+ IN DWORD State,
+ __out_ecount(capacity_Buffer) OUT PSTR Buffer,
+ size_t capacity_Buffer
+ )
+{
+ PSTR result;
+ CHAR invalidStr[sizeof("12345678")];
+
+ switch( State )
+ {
+ case MEM_COMMIT:
+ result = "Commit";
+ break;
+
+ case MEM_RESERVE:
+ result = "Reserve";
+ break;
+
+ case MEM_FREE:
+ result = "Free";
+ break;
+
+ default:
+ sprintf_s(invalidStr,_countof(invalidStr), "%08lx", State );
+ result = invalidStr;
+ break;
+ }
+
+ strcpy_s( Buffer, capacity_Buffer, result );
+ return Buffer;
+
+} // VmStateToString
+
+PSTR
+VmTypeToString(
+ IN DWORD Type,
+ __out_ecount(capacity_Buffer) OUT PSTR Buffer,
+ size_t capacity_Buffer
+ )
+{
+ PSTR result;
+ CHAR invalidStr[sizeof("12345678")];
+
+ switch( Type )
+ {
+ case MEM_PRIVATE:
+ result = "Private";
+ break;
+
+ case MEM_MAPPED:
+ result = "Mapped";
+ break;
+
+ case MEM_IMAGE:
+ result = "Image";
+ break;
+
+ case 0:
+ result = "";
+ break;
+
+ default:
+ sprintf_s(invalidStr,_countof(invalidStr), "%08lx", Type );
+ result = invalidStr;
+ break;
+ }
+ strcpy_s( Buffer,capacity_Buffer, result );
+ return Buffer;
+
+} // VmTypeToString
+
+/************************************************************
+ * Dump Virtual Memory Info
+ ************************************************************/
+
+void vmstat()
+
+/*++
+
+Routine Description:
+
+ This function is called as an NTSD extension to format and dump
+ virtual memory statistics.
+
+Arguments:
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ NTSTATUS status;
+ ULONG64 address;
+ MEMORY_BASIC_INFORMATION64 memInfo;
+ VM_STATS freeStats;
+ VM_STATS reserveStats;
+ VM_STATS commitStats;
+ VM_STATS privateStats;
+ VM_STATS mappedStats;
+ VM_STATS imageStats;
+
+ //
+ // Setup.
+ //
+
+ InitVmStats( &freeStats );
+ InitVmStats( &reserveStats );
+ InitVmStats( &commitStats );
+ InitVmStats( &privateStats );
+ InitVmStats( &mappedStats );
+ InitVmStats( &imageStats );
+
+ address = 0;
+
+ //
+ // Scan the virtual address space.
+ //
+
+ for( ; ; ) {
+ status = g_ExtData2->QueryVirtual(address, &memInfo);
+
+ if( !NT_SUCCESS(status) ) {
+ break;
+ }
+
+ //
+ // Interpret the memory state.
+ //
+
+ SIZE_T regionSize = (SIZE_T) memInfo.RegionSize;
+
+ switch( memInfo.State ) {
+
+ case MEM_FREE:
+ UpdateVmStats( &freeStats, regionSize );
+ break;
+
+ case MEM_RESERVE:
+ UpdateVmStats( &reserveStats, regionSize );
+ break;
+
+ case MEM_COMMIT:
+ UpdateVmStats( &commitStats, regionSize );
+ break;
+ }
+
+ //
+ // Interpret the memory type.
+ //
+
+ switch( memInfo.Type ) {
+ case MEM_PRIVATE:
+ UpdateVmStats( &privateStats, regionSize );
+ break;
+
+ case MEM_MAPPED:
+ UpdateVmStats( &mappedStats, regionSize );
+ break;
+
+ case MEM_IMAGE:
+ UpdateVmStats( &imageStats, regionSize );
+ break;
+ }
+
+ //
+ // Advance to the next block.
+ //
+
+ address += memInfo.RegionSize;
+ }
+
+ //
+ // Dump it.
+ //
+
+ PrintVmStatsHeader();
+ PrintVmStats( "Free", &freeStats );
+ PrintVmStats( "Reserve", &reserveStats );
+ PrintVmStats( "Commit", &commitStats );
+ PrintVmStats( "Private", &privateStats );
+ PrintVmStats( "Mapped", &mappedStats );
+ PrintVmStats( "Image", &imageStats );
+
+} // DECLARE_API( vmstat )
+
+
+void vmmap()
+
+/*++
+
+Routine Description:
+
+ This function is called as an NTSD extension to format and dump
+ the debugee's virtual memory address space.
+
+Arguments:
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ NTSTATUS status;
+ ULONG64 address;
+ MEMORY_BASIC_INFORMATION64 memInfo;
+ CHAR protectStr[32];
+ CHAR aprotectStr[32];
+ CHAR stateStr[16];
+ CHAR typeStr[16];
+
+ //
+ // Setup.
+ //
+
+ address = 0;
+
+ ExtOut(
+ "%-*s %-*s %-*s %-13s %-13s %-8s %-8s\n",
+ sizeof(PVOID) * 2,
+ "Start",
+ sizeof(PVOID) * 2,
+ "Stop",
+ sizeof(PVOID) * 2,
+ "Length",
+ "AllocProtect",
+ "Protect",
+ "State",
+ "Type"
+ );
+
+ //
+ // Scan the virtual address space.
+ //
+
+ for( ; ; ) {
+
+ if (IsInterrupt())
+ break;
+
+ status = g_ExtData2->QueryVirtual(address, &memInfo);
+
+ if( !NT_SUCCESS(status) ) {
+ break;
+ }
+
+ //
+ // Dump the current entry.
+ //
+
+ ExtOut(
+ "%p-%p %p %-13s %-13s %-8s %-8s\n",
+ SOS_PTR(memInfo.BaseAddress),
+ SOS_PTR(((ULONG_PTR)memInfo.BaseAddress + memInfo.RegionSize - 1)),
+ SOS_PTR(memInfo.RegionSize),
+ VmProtectToString( memInfo.AllocationProtect, aprotectStr, _countof(aprotectStr) ),
+ VmProtectToString( memInfo.Protect, protectStr, _countof(protectStr) ),
+ VmStateToString( memInfo.State, stateStr, _countof(stateStr) ),
+ VmTypeToString( memInfo.Type, typeStr , _countof(typeStr))
+ );
+
+ //
+ // Advance to the next block.
+ //
+
+ address += memInfo.RegionSize;
+ }
+
+} // DECLARE_API( vmmap )
+
+#else
+
+#include <rotor_pal.h>
+#include <assert.h>
+
+void vmstat()
+{
+ assert(false);
+}
+
+void vmmap()
+{
+
+ assert(false);
+}
+
+#endif // #ifndef FEATURE_PAL
diff --git a/src/ToolBox/SOS/Strike/xplat/.gitmirror b/src/ToolBox/SOS/Strike/xplat/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/xplat/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/ToolBox/SOS/Strike/xplat/dbgeng.h b/src/ToolBox/SOS/Strike/xplat/dbgeng.h
new file mode 100644
index 0000000000..b4562271a6
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/xplat/dbgeng.h
@@ -0,0 +1,485 @@
+// 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.
+
+//----------------------------------------------------------------------------
+//
+// Debugger engine interface subset implemented with ILLDBServices
+//
+//----------------------------------------------------------------------------
+
+#ifndef __DBGENG_H__
+#define __DBGENG_H__
+
+#include <unknwn.h>
+#include <rpc.h>
+#include <lldbservices.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+class DebugClient
+{
+private:
+ LONG m_ref;
+ ILLDBServices *m_lldbservices;
+
+public:
+ DebugClient(ILLDBServices *lldbservices) :
+ m_ref(1),
+ m_lldbservices(lldbservices)
+ {
+ m_lldbservices->AddRef();
+ }
+
+ //----------------------------------------------------------------------------
+ // IUnknown
+ //----------------------------------------------------------------------------
+
+ HRESULT
+ QueryInterface(
+ REFIID InterfaceId,
+ PVOID* Interface);
+
+ ULONG AddRef();
+
+ ULONG Release();
+
+ //----------------------------------------------------------------------------
+ // IDebugControl2
+ //----------------------------------------------------------------------------
+
+ // Checks for a user interrupt, such a Ctrl-C
+ // or stop button.
+ // This method is reentrant.
+ HRESULT
+ GetInterrupt()
+ {
+ return m_lldbservices->GetInterrupt();
+ }
+
+ // 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
+ Output(
+ ULONG mask,
+ PCSTR format,
+ ...)
+ {
+ va_list args;
+ va_start (args, format);
+ HRESULT result = m_lldbservices->OutputVaList(mask, format, args);
+ va_end (args);
+ return result;
+ }
+
+ HRESULT
+ OutputVaList(
+ ULONG mask,
+ PCSTR format,
+ va_list args)
+ {
+ char str[4096];
+ int length = PAL__vsnprintf(str, sizeof(str), format, args);
+ if (length > 0)
+ {
+ return Output(mask, "%s", str);
+ }
+ return E_FAIL;
+ }
+
+ // 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
+ 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
+ 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
+ GetDebuggeeType(
+ PULONG debugClass,
+ PULONG qualifier)
+ {
+ return m_lldbservices->GetDebuggeeType(debugClass, qualifier);
+ }
+
+ // Returns the page size for the currently executing
+ // processor context. The page size may vary between
+ // processor types.
+ HRESULT
+ GetPageSize(
+ PULONG size)
+ {
+ return m_lldbservices->GetPageSize(size);
+ }
+
+ HRESULT
+ GetExecutingProcessorType(
+ PULONG type)
+ {
+ return m_lldbservices->GetExecutingProcessorType(type);
+ }
+
+ HRESULT
+ Execute(
+ ULONG outputControl,
+ PCSTR command,
+ ULONG flags)
+ {
+ return m_lldbservices->Execute(outputControl, command, flags);
+ }
+
+ HRESULT
+ GetLastEventInformation(
+ PULONG type,
+ PULONG processId,
+ PULONG threadId,
+ PVOID extraInformation,
+ ULONG extraInformationSize,
+ PULONG extraInformationUsed,
+ PSTR description,
+ ULONG descriptionSize,
+ PULONG descriptionUsed)
+ {
+ return m_lldbservices->GetLastEventInformation(type, processId, threadId, extraInformation,
+ extraInformationSize, extraInformationUsed, description, descriptionSize, descriptionUsed);
+ }
+
+ HRESULT
+ Disassemble(
+ ULONG64 offset,
+ ULONG flags,
+ PSTR buffer,
+ ULONG bufferSize,
+ PULONG disassemblySize,
+ PULONG64 endOffset)
+ {
+ return m_lldbservices->Disassemble(offset, flags, buffer, bufferSize, disassemblySize, endOffset);
+ }
+
+ //----------------------------------------------------------------------------
+ // 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.
+ HRESULT
+ GetContextStackTrace(
+ PVOID startContext,
+ ULONG startContextSize,
+ PDEBUG_STACK_FRAME frames,
+ ULONG framesSize,
+ PVOID frameContexts,
+ ULONG frameContextsSize,
+ ULONG frameContextsEntrySize,
+ PULONG framesFilled)
+ {
+ return m_lldbservices->GetContextStackTrace(startContext, startContextSize, frames,
+ framesSize, frameContexts, frameContextsSize, frameContextsEntrySize, framesFilled);
+ }
+
+ //----------------------------------------------------------------------------
+ // IDebugDataSpaces
+ //----------------------------------------------------------------------------
+
+ HRESULT
+ ReadVirtual(
+ ULONG64 offset,
+ PVOID buffer,
+ ULONG bufferSize,
+ PULONG bytesRead)
+ {
+ return m_lldbservices->ReadVirtual(offset, buffer, bufferSize, bytesRead);
+ }
+
+ HRESULT
+ WriteVirtual(
+ ULONG64 offset,
+ PVOID buffer,
+ ULONG bufferSize,
+ PULONG bytesWritten)
+ {
+ return m_lldbservices->WriteVirtual(offset, buffer, bufferSize, bytesWritten);
+ }
+
+ //----------------------------------------------------------------------------
+ // IDebugSymbols
+ //----------------------------------------------------------------------------
+
+ HRESULT
+ GetSymbolOptions(
+ PULONG options)
+ {
+ return m_lldbservices->GetSymbolOptions(options);
+ }
+
+ HRESULT
+ GetNameByOffset(
+ ULONG64 offset,
+ PSTR nameBuffer,
+ ULONG nameBufferSize,
+ PULONG nameSize,
+ PULONG64 displacement)
+ {
+ return m_lldbservices->GetNameByOffset(offset, nameBuffer, nameBufferSize, nameSize, displacement);
+ }
+
+ HRESULT
+ GetNumberModules(
+ PULONG loaded,
+ PULONG unloaded)
+ {
+ return m_lldbservices->GetNumberModules(loaded, unloaded);
+ }
+
+ HRESULT GetModuleByIndex(
+ ULONG index,
+ PULONG64 base)
+ {
+ return m_lldbservices->GetModuleByIndex(index, base);
+ }
+
+ HRESULT
+ GetModuleByModuleName(
+ PCSTR name,
+ ULONG startIndex,
+ PULONG index,
+ PULONG64 base)
+ {
+ return m_lldbservices->GetModuleByModuleName(name, startIndex, index, base);
+ }
+
+ HRESULT
+ GetModuleByOffset(
+ ULONG64 offset,
+ ULONG startIndex,
+ PULONG index,
+ PULONG64 base)
+ {
+ return m_lldbservices->GetModuleByOffset(offset, startIndex, index, 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)
+ {
+ return m_lldbservices->GetModuleNames(index, base, imageNameBuffer, imageNameBufferSize, imageNameSize, moduleNameBuffer,
+ moduleNameBufferSize, moduleNameSize, loadedImageNameBuffer, loadedImageNameBufferSize, loadedImageNameSize);
+ }
+
+ HRESULT
+ GetLineByOffset(
+ ULONG64 offset,
+ PULONG line,
+ PSTR fileBuffer,
+ ULONG fileBufferSize,
+ PULONG fileSize,
+ PULONG64 displacement)
+ {
+ return m_lldbservices->GetLineByOffset(offset, line, fileBuffer, fileBufferSize, fileSize, displacement);
+ }
+
+ HRESULT
+ GetSourceFileLineOffsets(
+ PCSTR file,
+ PULONG64 buffer,
+ ULONG bufferLines,
+ PULONG fileLines)
+ {
+ return m_lldbservices->GetSourceFileLineOffsets(file, buffer, bufferLines, fileLines);
+ }
+
+ // 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
+ FindSourceFile(
+ ULONG startElement,
+ PCSTR file,
+ ULONG flags,
+ PULONG foundElement,
+ PSTR buffer,
+ ULONG bufferSize,
+ PULONG foundSize)
+ {
+ return m_lldbservices->FindSourceFile(startElement, file, flags, foundElement, buffer, bufferSize, foundSize);
+ }
+
+ //----------------------------------------------------------------------------
+ // IDebugSystemObjects
+ //----------------------------------------------------------------------------
+
+ HRESULT
+ GetCurrentProcessId(
+ PULONG id)
+ {
+ return m_lldbservices->GetCurrentProcessId(id);
+ }
+
+ HRESULT
+ GetCurrentThreadId(
+ PULONG id)
+ {
+ return m_lldbservices->GetCurrentThreadId(id);
+ }
+
+ HRESULT
+ SetCurrentThreadId(
+ ULONG id)
+ {
+ return m_lldbservices->SetCurrentThreadId(id);
+ }
+
+ HRESULT
+ GetCurrentThreadSystemId(
+ PULONG sysId)
+ {
+ return m_lldbservices->GetCurrentThreadSystemId(sysId);
+ }
+
+ HRESULT
+ GetThreadIdBySystemId(
+ ULONG sysId,
+ PULONG threadId)
+ {
+ return m_lldbservices->GetThreadIdBySystemId(sysId, threadId);
+ }
+
+ HRESULT
+ GetThreadContextById(
+ /* in */ ULONG32 threadID,
+ /* in */ ULONG32 contextFlags,
+ /* in */ ULONG32 contextSize,
+ /* out */ PBYTE context)
+ {
+ return m_lldbservices->GetThreadContextById(threadID, contextFlags, contextSize, context);
+ }
+
+ //----------------------------------------------------------------------------
+ // IDebugRegisters
+ //----------------------------------------------------------------------------
+
+ HRESULT
+ GetValueByName(
+ PCSTR name,
+ PDWORD_PTR debugValue)
+ {
+ return m_lldbservices->GetValueByName(name, debugValue);
+ }
+
+ HRESULT
+ GetInstructionOffset(
+ PULONG64 offset)
+ {
+ return m_lldbservices->GetInstructionOffset(offset);
+ }
+
+ HRESULT
+ GetStackOffset(
+ PULONG64 offset)
+ {
+ return m_lldbservices->GetStackOffset(offset);
+ }
+
+ HRESULT
+ GetFrameOffset(
+ PULONG64 offset)
+ {
+ return m_lldbservices->GetFrameOffset(offset);
+ }
+};
+
+MIDL_INTERFACE("d4366723-44df-4bed-8c7e-4c05424f4588")
+IDebugControl2 : DebugClient
+{
+};
+
+MIDL_INTERFACE("94e60ce9-9b41-4b19-9fc0-6d9eb35272b3")
+IDebugControl4 : DebugClient
+{
+};
+
+MIDL_INTERFACE("88f7dfab-3ea7-4c3a-aefb-c4e8106173aa")
+IDebugDataSpaces : DebugClient
+{
+};
+
+MIDL_INTERFACE("8c31e98c-983a-48a5-9016-6fe5d667a950")
+IDebugSymbols : DebugClient
+{
+};
+
+MIDL_INTERFACE("6b86fe2c-2c4f-4f0c-9da2-174311acc327")
+IDebugSystemObjects : DebugClient
+{
+};
+
+MIDL_INTERFACE("ce289126-9e84-45a7-937e-67bb18691493")
+IDebugRegisters : DebugClient
+{
+};
+
+typedef interface ILLDBServices* PDEBUG_CLIENT;
+typedef interface IDebugControl2* PDEBUG_CONTROL2;
+typedef interface IDebugControl4* PDEBUG_CONTROL4;
+typedef interface IDebugDataSpaces* PDEBUG_DATA_SPACES;
+typedef interface IDebugSymbols* PDEBUG_SYMBOLS;
+typedef interface IDebugSystemObjects* PDEBUG_SYSTEM_OBJECTS;
+typedef interface IDebugRegisters* PDEBUG_REGISTERS;
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // #ifndef __DBGENG_H__
diff --git a/src/ToolBox/SOS/Strike/xplat/dbghelp.h b/src/ToolBox/SOS/Strike/xplat/dbghelp.h
new file mode 100644
index 0000000000..7086b335fd
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/xplat/dbghelp.h
@@ -0,0 +1,17 @@
+// 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.
+
+/*
+
+Module Name:
+
+ dbghelp.h
+
+Abstract:
+
+ Dummy include file for LLDB sos extension.
+
+Revision History:
+
+--*/ \ No newline at end of file
diff --git a/src/ToolBox/SOS/Strike/xplat/wdbgexts.h b/src/ToolBox/SOS/Strike/xplat/wdbgexts.h
new file mode 100644
index 0000000000..d5a1a31c0a
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/xplat/wdbgexts.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.
+
+/*++
+
+Module Name:
+
+ wdbgexts.h
+
+Abstract:
+
+ Dummy include file for LLDB sos extension.
+
+Environment:
+
+Revision History:
+
+--*/
+
+#ifndef _WDBGEXTS_
+#define _WDBGEXTS_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+#endif // _WDBGEXTS_