diff options
author | Mike McLaughlin <mikem@microsoft.com> | 2019-06-21 00:08:55 +0300 |
---|---|---|
committer | Jan Vorlicek <janvorli@microsoft.com> | 2019-06-21 00:08:55 +0300 |
commit | 010bbc8844bebf349a8f4d6a5cf8e1122127bd12 (patch) | |
tree | a1f85efe1238a707e1b9422e67b16976f1016cee | |
parent | 86982886580280e5d3542617e5ec5836c8984463 (diff) |
Remove SOS and lldb plugin (dotnet/coreclr#25220)
* Remove SOS and lldb plugin
Issue: https://github.com/dotnet/coreclr/issues/24092
Commit migrated from https://github.com/dotnet/coreclr/commit/6b9a3d3a87825b1a34bd8f114c9b181ce75b3b2e
111 files changed, 44 insertions, 85011 deletions
diff --git a/docs/coreclr/building/buildinglldb.md b/docs/coreclr/building/buildinglldb.md deleted file mode 100644 index 5c4b775c17d..00000000000 --- a/docs/coreclr/building/buildinglldb.md +++ /dev/null @@ -1,87 +0,0 @@ -Building LLDB -============= - -1. Clone the llvm, clang, and lldb repos like this: - - llvm - | - `-- tools - | - +-- clang - | - `-- lldb - - ``` - cd $HOME - git clone http://llvm.org/git/llvm.git - cd $HOME/llvm/tools - git clone http://llvm.org/git/clang.git - git clone http://llvm.org/git/lldb.git - ``` - -2. Checkout the "release_39" branches in llvm/clang/lldb: - - ``` - cd $HOME/llvm - git checkout release_39 - cd $HOME/llvm/tools/clang - git checkout release_39 - cd $HOME/llvm/tools/lldb - git checkout release_39 - ``` - -3. Install the prerequisites: - - For Linux (Debian or Ubuntu): - ``` - sudo apt-get install build-essential subversion swig python2.7-dev libedit-dev libncurses5-dev - ``` - - For OSX, the latest Xcode needs to be installed and I use Homebrew to install the rest: - ``` - brew install python swig doxygen ocaml - ``` - - There may be more prerequisites required, when building the cmake files it should let - you know if there are any I missed. - - See http://lldb.llvm.org/build.html for more details on these preliminaries. - -4. If building on OSX, carefully following the signing directions (before you build) - here: $HOME/llvm/tools/lldb/docs/code-signing.txt. Even though those build directions - say to use Xcode to build lldb, I never got it to work, but cmake/make works. - -5. Building the cmake files (you can build either debug or release or both). - - For debug: - ``` - mkdir -p $HOME/build/debug - cd $HOME/build/debug - cmake -DCMAKE_BUILD_TYPE=debug $HOME/llvm - ``` - For release: - ``` - mkdir -p $HOME/build/release - cd $HOME/build/release - cmake -DCMAKE_BUILD_TYPE=release $HOME/llvm - ``` -6. Build lldb (release was picked in this example, but can be replaced with "debug"): - ``` - cd $HOME/build/release/tools/lldb - make -j16 - ``` - When you build with -j16 (parallel build with 16 jobs), sometimes it fails. Just start again with just make. - - For OS X, building in remote ssh shell won't sign properly, use a terminal window on the machine itself. - -7. To use the newly built lldb and to build the coreclr SOS plugin for it, set these environment variables in your .profile: - ``` - export LLDB_INCLUDE_DIR=$HOME/llvm/tools/lldb/include - export LLDB_LIB_DIR=$HOME/build/release/lib - PATH=$HOME/build/release/bin:$PATH - ``` - For OS X also set: - ``` - export LLDB_DEBUGSERVER_PATH=$HOME/build/release/bin/debugserver - ``` - It also seems to be necessary to run lldb as superuser e.g. `sudo -E $HOME/build/release/bin/lldb` (the -E is necessary so the above debug server environment variable is passed) if using a remote ssh, but it isn't necessary if run it in a local terminal session. diff --git a/docs/coreclr/building/debugging-instructions.md b/docs/coreclr/building/debugging-instructions.md index 86468b6bbff..f99b498b415 100644 --- a/docs/coreclr/building/debugging-instructions.md +++ b/docs/coreclr/building/debugging-instructions.md @@ -1,7 +1,9 @@ Debugging CoreCLR ================= -These instructions will lead you through debugging CoreCLR on Windows and Linux. They will be expanded to support macOS when we have good instructions for that. +These instructions will lead you through debugging CoreCLR. + +SOS has moved to the diagnostics repo. For more information on SOS, installation and how to use it click [here](https://github.com/dotnet/diagnostics#net-core-diagnostics-repo). Debugging CoreCLR on Windows ============================ @@ -24,29 +26,25 @@ Steps 1-8 only need to be done once, and then (9) can be repeated whenever you w ### Using SOS with windbg or cdb on Windows ### -If you know the path of the `sos.dll` for the version of your runtime, load it like `.load c:\path\to\sos\sos.dll`. Use can use the `lm` command to find the path of the "coreclr.dll" module. `.loadby sos coreclr` should also work. - -For more information on SOS commands click [here](https://msdn.microsoft.com/en-us/library/bb190764(v=vs.110).aspx). +See the SOS installation instructions [here](https://github.com/dotnet/diagnostics/blob/master/documentation/installing-sos-windows-instructions.md). -Debugging CoreCLR on OS X -========================== +For more information on SOS commands click [here](https://github.com/dotnet/diagnostics/blob/master/documentation/sos-debugging-extension-windows.md). -To use lldb on OS X, you first need to build it and the SOS plugin on the machine you intend to use it. See the instructions in [building lldb](buildinglldb.md). The rest of instructions on how to use lldb for Linux on are the same. +Debugging CoreCLR on Linux and macOS +==================================== -Debugging CoreCLR on Linux -========================== +See the SOS installation instructions [here](https://github.com/dotnet/diagnostics/blob/master/documentation/installing-sos-windows-instructions.md). After SOS is installed, it will automatically be loaded by lldb. -Only lldb is supported by the SOS plugin. gdb can be used to debug the coreclr code but with no SOS support. Visual Studio 2015 RTM remote debugging isn't currently supported. +Only lldb is supported by SOS. Gdb can be used to debug the coreclr code but with no SOS support. 1. Perform a build of the coreclr repo. 2. Install the corefx managed assemblies to the binaries directory. 3. cd to build's binaries: `cd ~/coreclr/bin/Product/Linux.x64.Debug` -4. Start lldb (the version the plugin was built with, currently 3.9): `lldb-3.9 corerun HelloWorld.exe linux` -5. Now at the lldb command prompt, load SOS plugin: `plugin load libsosplugin.so` +4. Start lldb: `lldb-3.9 corerun HelloWorld.exe linux` 6. Launch program: `process launch -s` 7. To stop annoying breaks on SIGUSR1/SIGUSR2 signals used by the runtime run: `process handle -s false SIGUSR1 SIGUSR2` 8. Get to a point where coreclr is initialized by setting a breakpoint (i.e. `breakpoint set -n LoadLibraryExW` and then `process continue`) or stepping into the runtime. -9. Run a SOS command like `sos ClrStack` or `sos VerifyHeap`. The command name is case sensitive. +9. Run a SOS command like `clrstack` or `sos VerifyHeap`. The command name is case sensitive. You can combine steps 4-8 and pass everything on the lldb command line: @@ -56,120 +54,10 @@ For .NET Core version 1.x and 2.0.x, libsosplugin.so is built for and will only **Note:** _corerun_ is a simple host that does not support resolving NuGet dependencies. It relies on libraries being locatable via the `CORE_LIBRARIES` environment variable or present in the same directory as the corerun executable. The instructions above are equally applicable to the _dotnet_ host, however - e.g. for step 4 `lldb-3.9 dotnet bin/Debug/netcoreapp2.1/MvcApplication.dll` will let you debug _MvcApplication_ in the same manner. -### SOS commands ### - -This is the full list of commands currently supported by SOS. lldb is case-sensitive, unlike windbg. - - 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) CreateDump (createdump) - 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) - -### Aliases ### - -By default you can reach all the SOS commands by using: _sos [command\_name]_ -However the common commands have been aliased so that you don't need the SOS prefix: - - bpmd -> sos bpmd - clrstack -> sos ClrStack - clrthreads -> sos Threads - clru -> sos U - createdump -> sos CreateDump - dso -> sos DumpStackObjects - dumpclass -> sos DumpClass - dumpheap -> sos DumpHeap - dumpil -> sos DumpIL - dumplog -> sos DumpLog - dumpmd -> sos DumpMD - dumpmodule -> sos DumpModule - dumpmt -> sos DumpMT - dumpobj -> sos DumpObj - dumpstack -> sos DumpStack - eeheap -> sos EEHeap - eestack -> sos EEStack - gcroot -> sos GCRoot - histinit -> sos HistInit - histroot -> sos HistRoot - histobj -> sos HistObj - histobjfind -> sos HistObjFind - histclear -> sos HistClear - ip2md -> sos IP2MD - name2ee -> sos Name2EE - pe -> sos PrintException - soshelp -> sos Help - ### Debugging core dumps with lldb -It is also possible to debug .NET Core crash dumps using lldb and SOS. In order to do this, you need all of the following: - -- The crash dump file. We have a service called "Dumpling" which collects, uploads, and archives crash dump files during all of our CI jobs and official builds. -- On Linux, there is a utility called `createdump` (see [doc](https://github.com/dotnet/coreclr/blob/master/Documentation/botr/xplat-minidump-generation.md#configurationpolicy)) that can be setup to generate core dumps when a managed app throws an unhandled exception or faults. -- To get matching runtime and symbol binaries for the core dump use the symbol downloader CLI extension: - - Install the [.NET Core 2.1 SDK](https://www.microsoft.com/net/download/). - - Install the symbol downloader extension: `dotnet tool install -g dotnet-symbol`. Make sure you are not in any project directory with a NuGet.Config that doesn't include nuget.org as a source. - - Run `dotnet symbol coredump` to download the runtime binaries and symbols. - - Check out the coreclr and corefx repositories at the appropriate commit for the appropriate source. - - For further information see: [dotnet-symbol](https://github.com/dotnet/symstore/blob/master/src/dotnet-symbol/README.md). -- lldb version 3.9. The SOS plugin (i.e. libsosplugin.so) provided is now built for lldb 3.9. In order to install lldb 3.9 just run the following commands: -``` -~$ echo "deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.9 main" | sudo tee /etc/apt/sources.list.d/llvm.list -~$ wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add - -~$ sudo apt-get update -~$ sudo apt-get install lldb-3.9 -``` - -Once you have everything listed above, you are ready to start debugging. You need to specify an extra parameter to lldb in order for it to correctly resolve the symbols for libcoreclr.so. Use a command like this: - -``` -lldb-3.9 -O "settings set target.exec-search-paths <runtime-path>" -o "plugin load <path-to-libsosplugin.so>" --core <core-file-path> <host-path> -``` - -- `<runtime-path>`: The path containing libcoreclr.so.dbg, as well as the rest of the runtime and framework assemblies. -- `<core-file-path>`: The path to the core dump you are attempting to debug. -- `<host-path>`: The path to the dotnet or corerun executable, potentially in the `<runtime-path>` folder. -- `<path-to-libsosplugin.so>`: The path to libsosplugin.so, should be in the `<runtime-path>` folder. - -lldb should start debugging successfully at this point. You should see stack traces with resolved symbols for libcoreclr.so. At this point, you can run `plugin load <libsosplugin.so-path>`, and begin using SOS commands, as above. - -##### Example - -``` -lldb-3.9 -O "settings set target.exec-search-paths /home/parallels/Downloads/System.Drawing.Common.Tests/home/helixbot/dotnetbuild/work/2a74cf82-3018-4e08-9e9a-744bb492869e/Payload/shared/Microsoft.NETCore.App/9.9.9/" -o "plugin load /home/parallels/Downloads/System.Drawing.Common.Tests/home/helixbot/dotnetbuild/work/2a74cf82-3018-4e08-9e9a-744bb492869e/Payload/shared/Microsoft.NETCore.App/9.9.9/libsosplugin.so" --core /home/parallels/Downloads/System.Drawing.Common.Tests/home/helixbot/dotnetbuild/work/2a74cf82-3018-4e08-9e9a-744bb492869e/Work/f6414a62-9b41-4144-baed-756321e3e075/Unzip/core /home/parallels/Downloads/System.Drawing.Common.Tests/home/helixbot/dotnetbuild/work/2a74cf82-3018-4e08-9e9a-744bb492869e/Payload/shared/Microsoft.NETCore.App/9.9.9/dotnet -``` +See this [link](https://github.com/dotnet/diagnostics/blob/master/documentation/debugging-coredump.md) in the diagnostics repo. + Disabling Managed Attach/Debugging ================================== diff --git a/src/coreclr/CMakeLists.txt b/src/coreclr/CMakeLists.txt index ab5c441785e..21f2031b954 100644 --- a/src/coreclr/CMakeLists.txt +++ b/src/coreclr/CMakeLists.txt @@ -158,7 +158,6 @@ if(CLR_CMAKE_PLATFORM_UNIX AND NOT DEFINED CLR_CROSS_COMPONENTS_BUILD) endif() if(CLR_CMAKE_PLATFORM_UNIX) - add_subdirectory(src/ToolBox/SOS/lldbplugin) add_subdirectory(src/pal) add_subdirectory(src/coreclr/hosts) add_subdirectory(src/ildasm/unixcoreclrloader) diff --git a/src/coreclr/build.cmd b/src/coreclr/build.cmd index 395c8238753..94f1f132a2f 100644 --- a/src/coreclr/build.cmd +++ b/src/coreclr/build.cmd @@ -88,7 +88,6 @@ set __UnprocessedBuildArgs= set __CommonMSBuildArgs= set __BuildCoreLib=1 -set __BuildSOS=1 set __BuildNative=1 set __BuildCrossArchNative=0 set __SkipCrossArchNative=0 @@ -166,11 +165,11 @@ if [!__PassThroughArgs!]==[] ( set __PassThroughArgs=%__PassThroughArgs% %1 ) -if /i "%1" == "-freebsdmscorlib" (set __BuildSOS=0&set __BuildNativeCoreLib=0&set __BuildNative=0&set __BuildTests=0&set __BuildPackages=0&set __BuildManagedTools=0&set __BuildOS=FreeBSD&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) -if /i "%1" == "-linuxmscorlib" (set __BuildSOS=0&set __BuildNativeCoreLib=0&set __BuildNative=0&set __BuildTests=0&set __BuildPackages=0&set __BuildManagedTools=0&set __BuildOS=Linux&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) -if /i "%1" == "-netbsdmscorlib" (set __BuildSOS=0&set __BuildNativeCoreLib=0&set __BuildNative=0&set __BuildTests=0&set __BuildPackages=0&set __BuildManagedTools=0&set __BuildOS=NetBSD&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) -if /i "%1" == "-osxmscorlib" (set __BuildSOS=0&set __BuildNativeCoreLib=0&set __BuildNative=0&set __BuildTests=0&set __BuildPackages=0&set __BuildManagedTools=0&set __BuildOS=OSX&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) -if /i "%1" == "-windowsmscorlib" (set __BuildSOS=0&set __BuildNativeCoreLib=0&set __BuildNative=0&set __BuildTests=0&set __BuildPackages=0&set __BuildManagedTools=0&set __BuildOS=Windows_NT&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) +if /i "%1" == "-freebsdmscorlib" (set __BuildNativeCoreLib=0&set __BuildNative=0&set __BuildTests=0&set __BuildPackages=0&set __BuildManagedTools=0&set __BuildOS=FreeBSD&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) +if /i "%1" == "-linuxmscorlib" (set __BuildNativeCoreLib=0&set __BuildNative=0&set __BuildTests=0&set __BuildPackages=0&set __BuildManagedTools=0&set __BuildOS=Linux&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) +if /i "%1" == "-netbsdmscorlib" (set __BuildNativeCoreLib=0&set __BuildNative=0&set __BuildTests=0&set __BuildPackages=0&set __BuildManagedTools=0&set __BuildOS=NetBSD&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) +if /i "%1" == "-osxmscorlib" (set __BuildNativeCoreLib=0&set __BuildNative=0&set __BuildTests=0&set __BuildPackages=0&set __BuildManagedTools=0&set __BuildOS=OSX&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) +if /i "%1" == "-windowsmscorlib" (set __BuildNativeCoreLib=0&set __BuildNative=0&set __BuildTests=0&set __BuildPackages=0&set __BuildManagedTools=0&set __BuildOS=Windows_NT&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "-nativemscorlib" (set __BuildNativeCoreLib=1&set __BuildCoreLib=0&set __BuildNative=0&set __BuildTests=0&set __BuildPackages=0&set __BuildManagedTools=0&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "-configureonly" (set __ConfigureOnly=1&set __BuildNative=1&set __BuildNativeCoreLib=0&set __BuildCoreLib=0&set __BuildTests=0&set __BuildPackages=0&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "-skipconfigure" (set __SkipConfigure=1&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) @@ -196,11 +195,11 @@ if /i "%1" == "-OfficialBuildId" (set __OfficialBuildIdArg=/p:OfficialBuildI REM TODO these are deprecated remove them eventually REM don't add more, use the - syntax instead -if /i "%1" == "freebsdmscorlib" (set __BuildSOS=0&set __BuildNativeCoreLib=0&set __BuildNative=0&set __BuildTests=0&set __BuildPackages=0&set __BuildManagedTools=0&set __BuildOS=FreeBSD&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) -if /i "%1" == "linuxmscorlib" (set __BuildSOS=0&set __BuildNativeCoreLib=0&set __BuildNative=0&set __BuildTests=0&set __BuildPackages=0&set __BuildManagedTools=0&set __BuildOS=Linux&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) -if /i "%1" == "netbsdmscorlib" (set __BuildSOS=0&set __BuildNativeCoreLib=0&set __BuildNative=0&set __BuildTests=0&set __BuildPackages=0&set __BuildManagedTools=0&set __BuildOS=NetBSD&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) -if /i "%1" == "osxmscorlib" (set __BuildSOS=0&set __BuildNativeCoreLib=0&set __BuildNative=0&set __BuildTests=0&set __BuildPackages=0&set __BuildManagedTools=0&set __BuildOS=OSX&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) -if /i "%1" == "windowsmscorlib" (set __BuildSOS=0&set __BuildNativeCoreLib=0&set __BuildNative=0&set __BuildTests=0&set __BuildPackages=0&set __BuildManagedTools=0&set __BuildOS=Windows_NT&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) +if /i "%1" == "freebsdmscorlib" (set __BuildNativeCoreLib=0&set __BuildNative=0&set __BuildTests=0&set __BuildPackages=0&set __BuildManagedTools=0&set __BuildOS=FreeBSD&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) +if /i "%1" == "linuxmscorlib" (set __BuildNativeCoreLib=0&set __BuildNative=0&set __BuildTests=0&set __BuildPackages=0&set __BuildManagedTools=0&set __BuildOS=Linux&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) +if /i "%1" == "netbsdmscorlib" (set __BuildNativeCoreLib=0&set __BuildNative=0&set __BuildTests=0&set __BuildPackages=0&set __BuildManagedTools=0&set __BuildOS=NetBSD&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) +if /i "%1" == "osxmscorlib" (set __BuildNativeCoreLib=0&set __BuildNative=0&set __BuildTests=0&set __BuildPackages=0&set __BuildManagedTools=0&set __BuildOS=OSX&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) +if /i "%1" == "windowsmscorlib" (set __BuildNativeCoreLib=0&set __BuildNative=0&set __BuildTests=0&set __BuildPackages=0&set __BuildManagedTools=0&set __BuildOS=Windows_NT&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "nativemscorlib" (set __BuildNativeCoreLib=1&set __BuildCoreLib=0&set __BuildNative=0&set __BuildTests=0&set __BuildPackages=0&set __BuildManagedTools=0&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "configureonly" (set __ConfigureOnly=1&set __BuildNative=1&set __BuildNativeCoreLib=0&set __BuildCoreLib=0&set __BuildTests=0&set __BuildPackages=0&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "skipconfigure" (set __SkipConfigure=1&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) @@ -614,10 +613,6 @@ if %__BuildCoreLib% EQU 1 ( set __ExtraBuildArgs= - if "%__BuildSOS%" == "0" ( - set __ExtraBuildArgs=!__ExtraBuildArgs! /p:SkipSOS=true - ) - if "%__BuildManagedTools%" == "1" ( set __ExtraBuildArgs=!__ExtraBuildArgs! /p:BuildManagedTools=true ) diff --git a/src/coreclr/src/.nuget/Microsoft.NETCore.Runtime.CoreCLR/runtime.FreeBSD.Microsoft.NETCore.Runtime.CoreCLR.props b/src/coreclr/src/.nuget/Microsoft.NETCore.Runtime.CoreCLR/runtime.FreeBSD.Microsoft.NETCore.Runtime.CoreCLR.props index fcf0d78a0b9..3219da6648c 100644 --- a/src/coreclr/src/.nuget/Microsoft.NETCore.Runtime.CoreCLR/runtime.FreeBSD.Microsoft.NETCore.Runtime.CoreCLR.props +++ b/src/coreclr/src/.nuget/Microsoft.NETCore.Runtime.CoreCLR/runtime.FreeBSD.Microsoft.NETCore.Runtime.CoreCLR.props @@ -6,12 +6,11 @@ <NativeBinary Include="$(BinDir)libdbgshim.so" /> <NativeBinary Include="$(BinDir)libmscordaccore.so" /> <NativeBinary Include="$(BinDir)libmscordbi.so" /> - <NativeBinary Include="$(BinDir)libsos.so" /> - <NativeBinary Condition="'$(_PlatformDoesNotSupportSosPlugin)' != 'true'" Include="$(BinDir)libsosplugin.so" /> <NativeBinary Include="$(BinDir)System.Globalization.Native.so" /> - <NativeBinary Include="$(BinDir)sosdocsunix.txt" /> + <File Include="$(BinDir)SOS_README.md"> + <TargetPath>runtimes\$(PackageTargetRuntime)\native</TargetPath> + </File> <CrossGenBinary Include="$(BinDir)System.Private.CoreLib.dll" /> - <ArchitectureSpecificLibFile Include="$(BinDir)SOS.NETCore.dll" /> <ArchitectureSpecificToolFile Include="$(BinDir)crossgen" /> <CrossArchitectureSpecificToolFile Condition="'$(HasCrossTargetComponents)' == 'true'" Include="$(BinDir)$(CrossTargetComponentFolder)\crossgen" /> </ItemGroup> diff --git a/src/coreclr/src/.nuget/Microsoft.NETCore.Runtime.CoreCLR/runtime.Linux.Microsoft.NETCore.Runtime.CoreCLR.props b/src/coreclr/src/.nuget/Microsoft.NETCore.Runtime.CoreCLR/runtime.Linux.Microsoft.NETCore.Runtime.CoreCLR.props index d74974ddd7f..a046f0ba189 100644 --- a/src/coreclr/src/.nuget/Microsoft.NETCore.Runtime.CoreCLR/runtime.Linux.Microsoft.NETCore.Runtime.CoreCLR.props +++ b/src/coreclr/src/.nuget/Microsoft.NETCore.Runtime.CoreCLR/runtime.Linux.Microsoft.NETCore.Runtime.CoreCLR.props @@ -5,7 +5,6 @@ <_PlatformDoesNotSupportCreatedump Condition="'$(_runtimeOSFamily)' == 'tizen'">true</_PlatformDoesNotSupportCreatedump> <_PlatformDoesNotSupportEventTrace Condition="'$(_runtimeOSFamily)' == 'tizen'">true</_PlatformDoesNotSupportEventTrace> <_PlatformDoesNotSupportEventTrace Condition="'$(Platform)' == 'x86'">true</_PlatformDoesNotSupportEventTrace> - <_PlatformDoesNotSupportSosPlugin Condition="'$(_runtimeOSFamily)' == 'android'">true</_PlatformDoesNotSupportSosPlugin> </PropertyGroup> <ItemGroup> <NativeBinary Include="$(BinDir)libcoreclr.so" /> @@ -13,13 +12,12 @@ <NativeBinary Include="$(BinDir)libdbgshim.so" /> <NativeBinary Include="$(BinDir)libmscordaccore.so" /> <NativeBinary Include="$(BinDir)libmscordbi.so" /> - <NativeBinary Include="$(BinDir)libsos.so" /> - <NativeBinary Condition="'$(_PlatformDoesNotSupportSosPlugin)' != 'true'" Include="$(BinDir)libsosplugin.so" /> <NativeBinary Include="$(BinDir)System.Globalization.Native.so" /> - <NativeBinary Include="$(BinDir)sosdocsunix.txt" /> + <File Include="$(BinDir)SOS_README.md"> + <TargetPath>runtimes\$(PackageTargetRuntime)\native</TargetPath> + </File> <NativeBinary Condition="'$(_PlatformDoesNotSupportCreatedump)' != 'true'" Include="$(BinDir)createdump" /> <CrossGenBinary Include="$(BinDir)System.Private.CoreLib.dll" /> - <ArchitectureSpecificLibFile Include="$(BinDir)SOS.NETCore.dll" /> <ArchitectureSpecificToolFile Include="$(BinDir)crossgen" /> <CrossArchitectureSpecificToolFile Condition="'$(HasCrossTargetComponents)' == 'true'" Include="$(BinDir)$(CrossTargetComponentFolder)\crossgen" /> </ItemGroup> diff --git a/src/coreclr/src/.nuget/Microsoft.NETCore.Runtime.CoreCLR/runtime.OSX.Microsoft.NETCore.Runtime.CoreCLR.props b/src/coreclr/src/.nuget/Microsoft.NETCore.Runtime.CoreCLR/runtime.OSX.Microsoft.NETCore.Runtime.CoreCLR.props index 9a641975ff4..7fa9c74bf26 100644 --- a/src/coreclr/src/.nuget/Microsoft.NETCore.Runtime.CoreCLR/runtime.OSX.Microsoft.NETCore.Runtime.CoreCLR.props +++ b/src/coreclr/src/.nuget/Microsoft.NETCore.Runtime.CoreCLR/runtime.OSX.Microsoft.NETCore.Runtime.CoreCLR.props @@ -5,11 +5,11 @@ <NativeBinary Include="$(BinDir)libdbgshim.dylib" /> <NativeBinary Include="$(BinDir)libmscordaccore.dylib" /> <NativeBinary Include="$(BinDir)libmscordbi.dylib" /> - <NativeBinary Include="$(BinDir)libsos.dylib" /> + <File Include="$(BinDir)SOS_README.md"> + <TargetPath>runtimes\$(PackageTargetRuntime)\native</TargetPath> + </File> <NativeBinary Include="$(BinDir)System.Globalization.Native.dylib" /> - <NativeBinary Include="$(BinDir)sosdocsunix.txt" /> <CrossGenBinary Include="$(BinDir)System.Private.CoreLib.dll" /> - <ArchitectureSpecificLibFile Include="$(BinDir)SOS.NETCore.dll" /> <ArchitectureSpecificToolFile Include="$(BinDir)crossgen" /> </ItemGroup> </Project> diff --git a/src/coreclr/src/.nuget/Microsoft.NETCore.Runtime.CoreCLR/runtime.Windows_NT.Microsoft.NETCore.Runtime.CoreCLR.props b/src/coreclr/src/.nuget/Microsoft.NETCore.Runtime.CoreCLR/runtime.Windows_NT.Microsoft.NETCore.Runtime.CoreCLR.props index 58912b85de1..3e006903d4d 100644 --- a/src/coreclr/src/.nuget/Microsoft.NETCore.Runtime.CoreCLR/runtime.Windows_NT.Microsoft.NETCore.Runtime.CoreCLR.props +++ b/src/coreclr/src/.nuget/Microsoft.NETCore.Runtime.CoreCLR/runtime.Windows_NT.Microsoft.NETCore.Runtime.CoreCLR.props @@ -18,15 +18,15 @@ <NativeBinary Include="$(BinDir)mscordbi.dll" /> <NativeBinary Include="$(BinDir)mscorrc.debug.dll" /> <NativeBinary Include="$(BinDir)mscorrc.dll" /> - <NativeBinary Include="$(BinDir)sos.dll" /> + <File Include="$(BinDir)SOS_README.md"> + <TargetPath>runtimes\$(PackageTargetRuntime)\native</TargetPath> + </File> <NativeBinary Include="$(BinDir)Redist\ucrt\DLLs\$(BuildArch)\*.dll" Condition="'$(BuildType)'=='Release' AND '$(BuildArch)' != 'arm64'" /> <CrossGenBinary Include="$(BinDir)System.Private.CoreLib.dll" /> - <ArchitectureSpecificLibFile Include="$(BinDir)SOS.NETCore.dll" /> <ArchitectureSpecificToolFile Include="$(BinDir)crossgen.exe" /> <CrossArchitectureSpecificToolFile Include="$(BinDir)$(CrossTargetComponentFolder)\crossgen.exe" /> <CrossArchitectureSpecificToolFile Include="$(BinDir)$(CrossTargetComponentFolder)\mscordaccore.dll" /> <CrossArchitectureSpecificToolFile Include="$(BinDir)$(CrossTargetComponentFolder)\mscordbi.dll" /> - <CrossArchitectureSpecificToolFile Include="$(BinDir)$(CrossTargetComponentFolder)\sos.dll" /> <!-- prevent accidental inclusion in AOT projects. --> <File Include="$(PlaceholderFile)"> @@ -37,12 +37,10 @@ </File> <!-- Create long-name files by including the same files again with a different target path. --> - <LongNameFile Include="$(BinDir)mscordaccore.dll; - $(BinDir)sos.dll"> + <LongNameFile Include="$(BinDir)mscordaccore.dll"> <TargetPath>runtimes\$(PackageTargetRuntime)\native\%(FileName)$(LongNameSuffix)%(Extension)</TargetPath> </LongNameFile> - <LongNameFile Include="$(BinDir)$(CrossTargetComponentFolder)\mscordaccore.dll; - $(BinDir)$(CrossTargetComponentFolder)\sos.dll" + <LongNameFile Include="$(BinDir)$(CrossTargetComponentFolder)\mscordaccore.dll" Condition="'$(HasCrossTargetComponents)'=='true'"> <TargetPath>tools\$(CrossTargetComponentFolder)_$(Platform)\%(FileName)$(CrossTargetLongNameSuffix)%(Extension)</TargetPath> </LongNameFile> diff --git a/src/coreclr/src/CMakeLists.txt b/src/coreclr/src/CMakeLists.txt index f4542b31332..1b3c90eda33 100644 --- a/src/coreclr/src/CMakeLists.txt +++ b/src/coreclr/src/CMakeLists.txt @@ -20,8 +20,6 @@ if(CLR_CMAKE_PLATFORM_UNIX) add_subdirectory(debug/createdump) endif(CLR_CMAKE_PLATFORM_LINUX) - add_subdirectory(ToolBox/SOS/Strike) - # Include the dummy c++ include files include_directories("pal/inc/rt/cpp") diff --git a/src/coreclr/src/ToolBox/SOS/CMakeLists.txt b/src/coreclr/src/ToolBox/SOS/CMakeLists.txt index 5ecf76089cc..56d38c4073a 100644 --- a/src/coreclr/src/ToolBox/SOS/CMakeLists.txt +++ b/src/coreclr/src/ToolBox/SOS/CMakeLists.txt @@ -2,5 +2,6 @@ if(WIN32) if (CMAKE_GENERATOR MATCHES "Visual Studio .*") add_subdirectory(DacTableGen) endif() - add_subdirectory(Strike) endif(WIN32) + +_install(FILES SOS_README.md DESTINATION .) diff --git a/src/coreclr/src/ToolBox/SOS/NETCore/SOS.NETCore.csproj b/src/coreclr/src/ToolBox/SOS/NETCore/SOS.NETCore.csproj deleted file mode 100644 index 5e888a2c9f1..00000000000 --- a/src/coreclr/src/ToolBox/SOS/NETCore/SOS.NETCore.csproj +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project Sdk="Microsoft.NET.Sdk"> - <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" /> - - <PropertyGroup> - <TargetFramework>netcoreapp2.0</TargetFramework> - <AssemblyName>SOS.NETCore</AssemblyName> - <AllowUnsafeBlocks>true</AllowUnsafeBlocks> - <NoWarn>;1591;1701</NoWarn> - <Description>.NET Core SOS</Description> - <OutputPath>$(BinDir)</OutputPath> - <EnsureRuntimePackageDependencies>false</EnsureRuntimePackageDependencies> - </PropertyGroup> - - <ItemGroup> - <PackageReference Include="System.Reflection.Metadata" Version="$(SystemReflectionMetadataVersion)" /> - </ItemGroup> -</Project> diff --git a/src/coreclr/src/ToolBox/SOS/NETCore/SOS.NETCore.sln b/src/coreclr/src/ToolBox/SOS/NETCore/SOS.NETCore.sln deleted file mode 100644 index 0106883b9cb..00000000000 --- a/src/coreclr/src/ToolBox/SOS/NETCore/SOS.NETCore.sln +++ /dev/null @@ -1,22 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SOS.NETCore", "SOS.NETCore.csproj", "{20513BA2-A156-4A17-4C70-5AC2DBD4F833}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {20513BA2-A156-4A17-4C70-5AC2DBD4F833}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {20513BA2-A156-4A17-4C70-5AC2DBD4F833}.Debug|Any CPU.Build.0 = Debug|Any CPU - {20513BA2-A156-4A17-4C70-5AC2DBD4F833}.Release|Any CPU.ActiveCfg = Release|Any CPU - {20513BA2-A156-4A17-4C70-5AC2DBD4F833}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/src/coreclr/src/ToolBox/SOS/NETCore/SymbolReader.cs b/src/coreclr/src/ToolBox/SOS/NETCore/SymbolReader.cs deleted file mode 100644 index 7a4bb521021..00000000000 --- a/src/coreclr/src/ToolBox/SOS/NETCore/SymbolReader.cs +++ /dev/null @@ -1,782 +0,0 @@ -// 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. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Reflection.Metadata; -using System.Reflection.Metadata.Ecma335; -using System.Reflection.PortableExecutable; -using System.Runtime.InteropServices; - -namespace SOS -{ - internal class SymbolReader - { - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - internal struct DebugInfo - { - public int lineNumber; - public int ilOffset; - public string fileName; - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - internal struct LocalVarInfo - { - public int startOffset; - public int endOffset; - public string name; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct MethodDebugInfo - { - public IntPtr points; - public int size; - public IntPtr locals; - public int localsSize; - - } - - /// <summary> - /// Read memory callback - /// </summary> - /// <returns>number of bytes read or 0 for error</returns> - internal unsafe delegate int ReadMemoryDelegate(ulong address, byte* buffer, int count); - - private sealed class OpenedReader : IDisposable - { - public readonly MetadataReaderProvider Provider; - public readonly MetadataReader Reader; - - public OpenedReader(MetadataReaderProvider provider, MetadataReader reader) - { - Debug.Assert(provider != null); - Debug.Assert(reader != null); - - Provider = provider; - Reader = reader; - } - - public void Dispose() => Provider.Dispose(); - } - - /// <summary> - /// Stream implementation to read debugger target memory for in-memory PDBs - /// </summary> - private class TargetStream : Stream - { - readonly ulong _address; - readonly ReadMemoryDelegate _readMemory; - - public override long Position { get; set; } - public override long Length { get; } - public override bool CanSeek { get { return true; } } - public override bool CanRead { get { return true; } } - public override bool CanWrite { get { return false; } } - - public TargetStream(ulong address, int size, ReadMemoryDelegate readMemory) - : base() - { - _address = address; - _readMemory = readMemory; - Length = size; - Position = 0; - } - - public override int Read(byte[] buffer, int offset, int count) - { - if (Position + count > Length) - { - throw new ArgumentOutOfRangeException(); - } - unsafe - { - fixed (byte* p = &buffer[offset]) - { - int read = _readMemory(_address + (ulong)Position, p, count); - Position += read; - return read; - } - } - } - - public override long Seek(long offset, SeekOrigin origin) - { - switch (origin) - { - case SeekOrigin.Begin: - Position = offset; - break; - case SeekOrigin.End: - Position = Length + offset; - break; - case SeekOrigin.Current: - Position += offset; - break; - } - return Position; - } - - public override void Flush() - { - } - - public override void SetLength(long value) - { - throw new NotImplementedException(); - } - - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotImplementedException(); - } - } - - /// <summary> - /// Quick fix for Path.GetFileName which incorrectly handles Windows-style paths on Linux - /// </summary> - /// <param name="pathName"> File path to be processed </param> - /// <returns>Last component of path</returns> - private static string GetFileName(string pathName) - { - int pos = pathName.LastIndexOfAny(new char[] { '/', '\\'}); - if (pos < 0) - return pathName; - return pathName.Substring(pos + 1); - } - - /// <summary> - /// Checks availability of debugging information for given assembly. - /// </summary> - /// <param name="assemblyPath"> - /// File path of the assembly or null if the module is in-memory or dynamic (generated by Reflection.Emit) - /// </param> - /// <param name="isFileLayout">type of in-memory PE layout, if true, file based layout otherwise, loaded layout</param> - /// <param name="loadedPeAddress"> - /// Loaded PE image address or zero if the module is dynamic (generated by Reflection.Emit). - /// Dynamic modules have their PDBs (if any) generated to an in-memory stream - /// (pointed to by <paramref name="inMemoryPdbAddress"/> and <paramref name="inMemoryPdbSize"/>). - /// </param> - /// <param name="loadedPeSize">loaded PE image size</param> - /// <param name="inMemoryPdbAddress">in memory PDB address or zero</param> - /// <param name="inMemoryPdbSize">in memory PDB size</param> - /// <returns>Symbol reader handle or zero if error</returns> - internal static IntPtr LoadSymbolsForModule(string assemblyPath, bool isFileLayout, ulong loadedPeAddress, int loadedPeSize, - ulong inMemoryPdbAddress, int inMemoryPdbSize, ReadMemoryDelegate readMemory) - { - try - { - TargetStream peStream = null; - if (assemblyPath == null && loadedPeAddress != 0) - { - peStream = new TargetStream(loadedPeAddress, loadedPeSize, readMemory); - } - TargetStream pdbStream = null; - if (inMemoryPdbAddress != 0) - { - pdbStream = new TargetStream(inMemoryPdbAddress, inMemoryPdbSize, readMemory); - } - OpenedReader openedReader = GetReader(assemblyPath, isFileLayout, peStream, pdbStream); - if (openedReader != null) - { - GCHandle gch = GCHandle.Alloc(openedReader); - return GCHandle.ToIntPtr(gch); - } - } - catch - { - } - return IntPtr.Zero; - } - - /// <summary> - /// Cleanup and dispose of symbol reader handle - /// </summary> - /// <param name="symbolReaderHandle">symbol reader handle returned by LoadSymbolsForModule</param> - internal static void Dispose(IntPtr symbolReaderHandle) - { - Debug.Assert(symbolReaderHandle != IntPtr.Zero); - try - { - GCHandle gch = GCHandle.FromIntPtr(symbolReaderHandle); - ((OpenedReader)gch.Target).Dispose(); - gch.Free(); - } - catch - { - } - } - - /// <summary> - /// Returns method token and IL offset for given source line number. - /// </summary> - /// <param name="symbolReaderHandle">symbol reader handle returned by LoadSymbolsForModule</param> - /// <param name="filePath">source file name and path</param> - /// <param name="lineNumber">source line number</param> - /// <param name="methodToken">method token return</param> - /// <param name="ilOffset">IL offset return</param> - /// <returns> true if information is available</returns> - internal static bool ResolveSequencePoint(IntPtr symbolReaderHandle, string filePath, int lineNumber, out int methodToken, out int ilOffset) - { - Debug.Assert(symbolReaderHandle != IntPtr.Zero); - methodToken = 0; - ilOffset = 0; - - GCHandle gch = GCHandle.FromIntPtr(symbolReaderHandle); - MetadataReader reader = ((OpenedReader)gch.Target).Reader; - - try - { - string fileName = GetFileName(filePath); - foreach (MethodDebugInformationHandle methodDebugInformationHandle in reader.MethodDebugInformation) - { - MethodDebugInformation methodDebugInfo = reader.GetMethodDebugInformation(methodDebugInformationHandle); - SequencePointCollection sequencePoints = methodDebugInfo.GetSequencePoints(); - foreach (SequencePoint point in sequencePoints) - { - string sourceName = reader.GetString(reader.GetDocument(point.Document).Name); - if (point.StartLine == lineNumber && GetFileName(sourceName) == fileName) - { - methodToken = MetadataTokens.GetToken(methodDebugInformationHandle.ToDefinitionHandle()); - ilOffset = point.Offset; - return true; - } - } - } - } - catch - { - } - return false; - } - - /// <summary> - /// Returns source line number and source file name for given IL offset and method token. - /// </summary> - /// <param name="symbolReaderHandle">symbol reader handle returned by LoadSymbolsForModule</param> - /// <param name="methodToken">method token</param> - /// <param name="ilOffset">IL offset</param> - /// <param name="lineNumber">source line number return</param> - /// <param name="fileName">source file name return</param> - /// <returns> true if information is available</returns> - internal static bool GetLineByILOffset(IntPtr symbolReaderHandle, int methodToken, long ilOffset, out int lineNumber, out IntPtr fileName) - { - lineNumber = 0; - fileName = IntPtr.Zero; - - string sourceFileName = null; - - if (!GetSourceLineByILOffset(symbolReaderHandle, methodToken, ilOffset, out lineNumber, out sourceFileName)) - { - return false; - } - fileName = Marshal.StringToBSTR(sourceFileName); - sourceFileName = null; - return true; - } - - /// <summary> - /// Helper method to return source line number and source file name for given IL offset and method token. - /// </summary> - /// <param name="symbolReaderHandle">symbol reader handle returned by LoadSymbolsForModule</param> - /// <param name="methodToken">method token</param> - /// <param name="ilOffset">IL offset</param> - /// <param name="lineNumber">source line number return</param> - /// <param name="fileName">source file name return</param> - /// <returns> true if information is available</returns> - private static bool GetSourceLineByILOffset(IntPtr symbolReaderHandle, int methodToken, long ilOffset, out int lineNumber, out string fileName) - { - Debug.Assert(symbolReaderHandle != IntPtr.Zero); - lineNumber = 0; - fileName = null; - - GCHandle gch = GCHandle.FromIntPtr(symbolReaderHandle); - MetadataReader reader = ((OpenedReader)gch.Target).Reader; - - try - { - Handle handle = MetadataTokens.Handle(methodToken); - if (handle.Kind != HandleKind.MethodDefinition) - return false; - - MethodDebugInformationHandle methodDebugHandle = ((MethodDefinitionHandle)handle).ToDebugInformationHandle(); - if (methodDebugHandle.IsNil) - return false; - - MethodDebugInformation methodDebugInfo = reader.GetMethodDebugInformation(methodDebugHandle); - SequencePointCollection sequencePoints = methodDebugInfo.GetSequencePoints(); - - SequencePoint nearestPoint = sequencePoints.GetEnumerator().Current; - foreach (SequencePoint point in sequencePoints) - { - if (point.Offset < ilOffset) - { - nearestPoint = point; - } - else - { - if (point.Offset == ilOffset) - nearestPoint = point; - - if (nearestPoint.StartLine == 0 || nearestPoint.StartLine == SequencePoint.HiddenLine) - return false; - - lineNumber = nearestPoint.StartLine; - fileName = reader.GetString(reader.GetDocument(nearestPoint.Document).Name); - return true; - } - } - } - catch - { - } - return false; - } - - /// <summary> - /// Returns local variable name for given local index and IL offset. - /// </summary> - /// <param name="symbolReaderHandle">symbol reader handle returned by LoadSymbolsForModule</param> - /// <param name="methodToken">method token</param> - /// <param name="localIndex">local variable index</param> - /// <param name="localVarName">local variable name return</param> - /// <returns>true if name has been found</returns> - internal static bool GetLocalVariableName(IntPtr symbolReaderHandle, int methodToken, int localIndex, out IntPtr localVarName) - { - localVarName = IntPtr.Zero; - - string localVar = null; - if (!GetLocalVariableByIndex(symbolReaderHandle, methodToken, localIndex, out localVar)) - return false; - - localVarName = Marshal.StringToBSTR(localVar); - localVar = null; - return true; - } - - /// <summary> - /// Helper method to return local variable name for given local index and IL offset. - /// </summary> - /// <param name="symbolReaderHandle">symbol reader handle returned by LoadSymbolsForModule</param> - /// <param name="methodToken">method token</param> - /// <param name="localIndex">local variable index</param> - /// <param name="localVarName">local variable name return</param> - /// <returns>true if name has been found</returns> - internal static bool GetLocalVariableByIndex(IntPtr symbolReaderHandle, int methodToken, int localIndex, out string localVarName) - { - Debug.Assert(symbolReaderHandle != IntPtr.Zero); - localVarName = null; - - GCHandle gch = GCHandle.FromIntPtr(symbolReaderHandle); - MetadataReader reader = ((OpenedReader)gch.Target).Reader; - - try - { - Handle handle = MetadataTokens.Handle(methodToken); - if (handle.Kind != HandleKind.MethodDefinition) - return false; - - MethodDebugInformationHandle methodDebugHandle = ((MethodDefinitionHandle)handle).ToDebugInformationHandle(); - LocalScopeHandleCollection localScopes = reader.GetLocalScopes(methodDebugHandle); - foreach (LocalScopeHandle scopeHandle in localScopes) - { - LocalScope scope = reader.GetLocalScope(scopeHandle); - LocalVariableHandleCollection localVars = scope.GetLocalVariables(); - foreach (LocalVariableHandle varHandle in localVars) - { - LocalVariable localVar = reader.GetLocalVariable(varHandle); - if (localVar.Index == localIndex) - { - if (localVar.Attributes == LocalVariableAttributes.DebuggerHidden) - return false; - - localVarName = reader.GetString(localVar.Name); - return true; - } - } - } - } - catch - { - } - return false; - } - internal static bool GetLocalsInfoForMethod(string assemblyPath, int methodToken, out List<LocalVarInfo> locals) - { - locals = null; - - OpenedReader openedReader = GetReader(assemblyPath, isFileLayout: true, peStream: null, pdbStream: null); - if (openedReader == null) - return false; - - using (openedReader) - { - try - { - Handle handle = MetadataTokens.Handle(methodToken); - if (handle.Kind != HandleKind.MethodDefinition) - return false; - - locals = new List<LocalVarInfo>(); - - MethodDebugInformationHandle methodDebugHandle = - ((MethodDefinitionHandle)handle).ToDebugInformationHandle(); - LocalScopeHandleCollection localScopes = openedReader.Reader.GetLocalScopes(methodDebugHandle); - foreach (LocalScopeHandle scopeHandle in localScopes) - { - LocalScope scope = openedReader.Reader.GetLocalScope(scopeHandle); - LocalVariableHandleCollection localVars = scope.GetLocalVariables(); - foreach (LocalVariableHandle varHandle in localVars) - { - LocalVariable localVar = openedReader.Reader.GetLocalVariable(varHandle); - if (localVar.Attributes == LocalVariableAttributes.DebuggerHidden) - continue; - LocalVarInfo info = new LocalVarInfo(); - info.startOffset = scope.StartOffset; - info.endOffset = scope.EndOffset; - info.name = openedReader.Reader.GetString(localVar.Name); - locals.Add(info); - } - } - } - catch - { - return false; - } - } - return true; - - } - /// <summary> - /// Returns source name, line numbers and IL offsets for given method token. - /// </summary> - /// <param name="assemblyPath">file path of the assembly</param> - /// <param name="methodToken">method token</param> - /// <param name="debugInfo">structure with debug information return</param> - /// <returns>true if information is available</returns> - /// <remarks>used by the gdb JIT support (not SOS). Does not support in-memory PEs or PDBs</remarks> - internal static bool GetInfoForMethod(string assemblyPath, int methodToken, ref MethodDebugInfo debugInfo) - { - try - { - List<DebugInfo> points = null; - List<LocalVarInfo> locals = null; - - if (!GetDebugInfoForMethod(assemblyPath, methodToken, out points)) - { - return false; - } - - if (!GetLocalsInfoForMethod(assemblyPath, methodToken, out locals)) - { - return false; - } - var structSize = Marshal.SizeOf<DebugInfo>(); - - debugInfo.size = points.Count; - var ptr = debugInfo.points; - - foreach (var info in points) - { - Marshal.StructureToPtr(info, ptr, false); - ptr = (IntPtr)(ptr.ToInt64() + structSize); - } - - structSize = Marshal.SizeOf<LocalVarInfo>(); - - debugInfo.localsSize = locals.Count; - ptr = debugInfo.locals; - - foreach (var info in locals) - { - Marshal.StructureToPtr(info, ptr, false); - ptr = (IntPtr)(ptr.ToInt64() + structSize); - } - - return true; - } - catch - { - } - return false; - } - - /// <summary> - /// Helper method to return source name, line numbers and IL offsets for given method token. - /// </summary> - /// <param name="assemblyPath">file path of the assembly</param> - /// <param name="methodToken">method token</param> - /// <param name="points">list of debug information for each sequence point return</param> - /// <returns>true if information is available</returns> - /// <remarks>used by the gdb JIT support (not SOS). Does not support in-memory PEs or PDBs</remarks> - private static bool GetDebugInfoForMethod(string assemblyPath, int methodToken, out List<DebugInfo> points) - { - points = null; - - OpenedReader openedReader = GetReader(assemblyPath, isFileLayout: true, peStream: null, pdbStream: null); - if (openedReader == null) - return false; - - using (openedReader) - { - try - { - Handle handle = MetadataTokens.Handle(methodToken); - if (handle.Kind != HandleKind.MethodDefinition) - return false; - - points = new List<DebugInfo>(); - MethodDebugInformationHandle methodDebugHandle = ((MethodDefinitionHandle)handle).ToDebugInformationHandle(); - MethodDebugInformation methodDebugInfo = openedReader.Reader.GetMethodDebugInformation(methodDebugHandle); - SequencePointCollection sequencePoints = methodDebugInfo.GetSequencePoints(); - - foreach (SequencePoint point in sequencePoints) - { - - DebugInfo debugInfo = new DebugInfo(); - debugInfo.lineNumber = point.StartLine; - debugInfo.fileName = openedReader.Reader.GetString(openedReader.Reader.GetDocument(point.Document).Name); - debugInfo.ilOffset = point.Offset; - points.Add(debugInfo); - } - } - catch - { - return false; - } - } - return true; - } - - /// <summary> - /// Returns the portable PDB reader for the assembly path - /// </summary> - /// <param name="assemblyPath">file path of the assembly or null if the module is in-memory or dynamic</param> - /// <param name="isFileLayout">type of in-memory PE layout, if true, file based layout otherwise, loaded layout</param> - /// <param name="peStream">optional in-memory PE stream</param> - /// <param name="pdbStream">optional in-memory PDB stream</param> - /// <returns>reader/provider wrapper instance</returns> - /// <remarks> - /// Assumes that neither PE image nor PDB loaded into memory can be unloaded or moved around. - /// </remarks> - private static OpenedReader GetReader(string assemblyPath, bool isFileLayout, Stream peStream, Stream pdbStream) - { - return (pdbStream != null) ? TryOpenReaderForInMemoryPdb(pdbStream) : TryOpenReaderFromAssembly(assemblyPath, isFileLayout, peStream); - } - - private static OpenedReader TryOpenReaderForInMemoryPdb(Stream pdbStream) - { - Debug.Assert(pdbStream != null); - - byte[] buffer = new byte[sizeof(uint)]; - if (pdbStream.Read(buffer, 0, sizeof(uint)) != sizeof(uint)) - { - return null; - } - uint signature = BitConverter.ToUInt32(buffer, 0); - - // quick check to avoid throwing exceptions below in common cases: - const uint ManagedMetadataSignature = 0x424A5342; - if (signature != ManagedMetadataSignature) - { - // not a Portable PDB - return null; - } - - OpenedReader result = null; - MetadataReaderProvider provider = null; - try - { - pdbStream.Position = 0; - provider = MetadataReaderProvider.FromPortablePdbStream(pdbStream); - result = new OpenedReader(provider, provider.GetMetadataReader()); - } - catch (Exception e) when (e is BadImageFormatException || e is IOException) - { - return null; - } - finally - { - if (result == null) - { - provider?.Dispose(); - } - } - - return result; - } - - private static OpenedReader TryOpenReaderFromAssembly(string assemblyPath, bool isFileLayout, Stream peStream) - { - if (assemblyPath == null && peStream == null) - return null; - - PEStreamOptions options = isFileLayout ? PEStreamOptions.Default : PEStreamOptions.IsLoadedImage; - if (peStream == null) - { - peStream = TryOpenFile(assemblyPath); - if (peStream == null) - return null; - - options = PEStreamOptions.Default; - } - - try - { - using (var peReader = new PEReader(peStream, options)) - { - DebugDirectoryEntry codeViewEntry, embeddedPdbEntry; - ReadPortableDebugTableEntries(peReader, out codeViewEntry, out embeddedPdbEntry); - - // First try .pdb file specified in CodeView data (we prefer .pdb file on disk over embedded PDB - // since embedded PDB needs decompression which is less efficient than memory-mapping the file). - if (codeViewEntry.DataSize != 0) - { - var result = TryOpenReaderFromCodeView(peReader, codeViewEntry, assemblyPath); - if (result != null) - { - return result; - } - } - - // if it failed try Embedded Portable PDB (if available): - if (embeddedPdbEntry.DataSize != 0) - { - return TryOpenReaderFromEmbeddedPdb(peReader, embeddedPdbEntry); - } - } - } - catch (Exception e) when (e is BadImageFormatException || e is IOException) - { - // nop - } - - return null; - } - - private static void ReadPortableDebugTableEntries(PEReader peReader, out DebugDirectoryEntry codeViewEntry, out DebugDirectoryEntry embeddedPdbEntry) - { - // See spec: https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PE-COFF.md - - codeViewEntry = default(DebugDirectoryEntry); - embeddedPdbEntry = default(DebugDirectoryEntry); - - foreach (DebugDirectoryEntry entry in peReader.ReadDebugDirectory()) - { - if (entry.Type == DebugDirectoryEntryType.CodeView) - { - const ushort PortableCodeViewVersionMagic = 0x504d; - if (entry.MinorVersion != PortableCodeViewVersionMagic) - { - continue; - } - - codeViewEntry = entry; - } - else if (entry.Type == DebugDirectoryEntryType.EmbeddedPortablePdb) - { - embeddedPdbEntry = entry; - } - } - } - - private static OpenedReader TryOpenReaderFromCodeView(PEReader peReader, DebugDirectoryEntry codeViewEntry, string assemblyPath) - { - OpenedReader result = null; - MetadataReaderProvider provider = null; - try - { - var data = peReader.ReadCodeViewDebugDirectoryData(codeViewEntry); - - string pdbPath = data.Path; - if (assemblyPath != null) - { - try - { - pdbPath = Path.Combine(Path.GetDirectoryName(assemblyPath), GetFileName(pdbPath)); - } - catch - { - // invalid characters in CodeView path - return null; - } - } - - var pdbStream = TryOpenFile(pdbPath); - if (pdbStream == null) - { - return null; - } - - provider = MetadataReaderProvider.FromPortablePdbStream(pdbStream); - var reader = provider.GetMetadataReader(); - - // Validate that the PDB matches the assembly version - if (data.Age == 1 && new BlobContentId(reader.DebugMetadataHeader.Id) == new BlobContentId(data.Guid, codeViewEntry.Stamp)) - { - result = new OpenedReader(provider, reader); - } - } - catch (Exception e) when (e is BadImageFormatException || e is IOException) - { - return null; - } - finally - { - if (result == null) - { - provider?.Dispose(); - } - } - - return result; - } - - private static OpenedReader TryOpenReaderFromEmbeddedPdb(PEReader peReader, DebugDirectoryEntry embeddedPdbEntry) - { - OpenedReader result = null; - MetadataReaderProvider provider = null; - - try - { - // TODO: We might want to cache this provider globally (across stack traces), - // since decompressing embedded PDB takes some time. - provider = peReader.ReadEmbeddedPortablePdbDebugDirectoryData(embeddedPdbEntry); - result = new OpenedReader(provider, provider.GetMetadataReader()); - } - catch (Exception e) when (e is BadImageFormatException || e is IOException) - { - return null; - } - finally - { - if (result == null) - { - provider?.Dispose(); - } - } - - return result; - } - - private static Stream TryOpenFile(string path) - { - if (!File.Exists(path)) - { - return null; - } - try - { - return File.OpenRead(path); - } - catch - { - return null; - } - } - } -} diff --git a/src/coreclr/src/ToolBox/SOS/SOSAndICorDebug.md b/src/coreclr/src/ToolBox/SOS/SOSAndICorDebug.md deleted file mode 100644 index 7398f6324b4..00000000000 --- a/src/coreclr/src/ToolBox/SOS/SOSAndICorDebug.md +++ /dev/null @@ -1,81 +0,0 @@ -SOS & ICorDebug --------------------- - -## Introduction - -The purpose of this doc is to capture the work and learnings produced by the Dev 11 MQ item to integrate the public ICorDebug* interfaces (mscordbi.dll) into the SOS implementation. -Note: Use of the name “windbg” in this document is meant to actually indicate all members of the ntsd family of debuggers. - - -## MSCORDBI.DLL Distribution - -### What’s checked in - suboptimal - -For MQ, SOS activates mscordbi.dll via the “typical” mechanism of calling into mscoree.dll to get the ICLRDebugging interface, on which SOS calls OpenVirtualProcess. -Although this works, it is not ideal. -It assumes the CLR you want to debug has been formally installed onto the debugging machine (complete with mscoree.dll in your system32 / syswow64 directory). - -### Recommendation -Optimally, everything windbg needs in order to debug the CLR should be automagically downloaded to the user’s box, including DAC, mscordbi.dll, and even SOS itself, with no need at all for mscoree.dll or mscoreei.dll. - -### Download -Today, DAC is already automagically downloaded via the symbol server, with support for that hard-coded into the debugger: Windbg takes file information of the CLR.dll in the debuggee or dump that would normally be used to ask the symbol server for CLR.pdb, and instead asks for the DAC dll (using CLR.dll’s file info and the DAC dll’s file name). -This same hack could be applied to download the appropriate mscordbi.dll, and even SOS.dll. - - -This requires work from the following parties: - - -* **CLR & Windows debugging teams** agree on a design whereby windbg could automatically (or receive a command to) locate and download the appropriate mscordbi.dll and SOS.dll much as it does today to locate DAC. -Extensions like SOS should then be able to access mscordbi.dll via a windbg API, much like the windbg API that exposes the private DAC interface. - * The above is intentionally way-vague. - Extra work needs to be done to determine a good way for windbg & extensions to cooperate and access mscordbi functionality. -* **Windows debugging team** implements the above -* **CLR Servicing Team** must alter their process to ensure mscordbi.dll and SOS.dll are properly indexed on the symbol server upon each CLR release, just as with DAC. - - -### Activation -Once DAC, SOS, and mscordbi are all on the same machine, we need a way for SOS to get an ICorDebugProcess from mscordbi that does not require mscoree.dll on the box. -Since the necessary mscordbi is already available as per above, there is no need for SOS to go through mscoree or mscoreei as it is today. -Since mscoreei.dll eventually just calls OpenVirtualProcessImpl() from mscordbi.dll, one could imagine that SOS could simply call mscordbi’s OpenVirtualProcessImpl() directly. - -* CLR Team would need to export OpenVirtualProcessImpl, or the equivalent, from mscordbi.dll directly. -* CLR Team would modify SOS to use OpenVirtualProcessImpl rather than the current scheme that relies on mscoree.dll. -* Since CLR Team owns both ends of this interface (i.e., implementation and consumer of OpenVirtualProcessImpl), and since we already have a scheme to exchange version numbers in OpenVirtualProcessImpl, there should not be versioning problems. -* However, we might prefer to have windbg (instead of SOS) call OpenVirtualProcessImpl(). -Examples why: - * The “Download” section above talks about SOS “accessing” mscordbi. - This is vague, and could either mean that SOS calls into mscordbi’ s OpenVirtualProcessImpl() directly to get an ICorDebugProcess OR SOS uses a new, structured windbg API which would call (on SOS’s behalf) into mscordbi to provide an ICorDebugProcess. - The latter may be a more natural way for an extension to get access to the “right” ICorDebugProcess, particularly if multiple mscordbi.dll’s end up on the symbol path, or for in-proc SxS scenarios. - * Perhaps windbg could someday use mscordbi directly to aid in managed debugging, rather than the private DAC interface. -* If we do choose to have windbg (instead of SOS) call OpenVirtualProcessImpl, then we’d need to give some more thought to OpenVirtualProcessImpl and versioning, since we no longer control the consumer end of that relationship. - -## !ClrStack –i [-p] - -This checkin uses !clrstack as the prototype for activating and consuming the public dbgapi. -This section briefly describes how that works. -If “-i” is specified, then !clrstack defers to ClrStackFromPublicInterface() to do its work. -Today in SOS, the private DAC interface is initialized at the beginning of each command, and released at the end of each command. -ClrStackFromPublicInterface() follows suit by initializing the public interface at the top, via ICLRDebugging, thus setting g_pCorDebugProcess appropriately (see “What’s checked in – suboptimal” above). -Note: currently, g_pCorDebugProcess is not released but hey, prototype code. -ClrStackFromPublicInterface then uses g_pCorDebugProcess to grab the other public interfaces necessary to do a stackwalk. -If “-p” is specified, ClrStackFromPublicInterface will also use ICorDebug* to get parameter info. -Caveats -To pretty-print the function names, there are several metadata-consuming chunks of code in SOS. -I picked the one that required the least amount of private DAC gunk. -However, that code appears not to be well-exercised. -See comments in the code for more details. -The code in !clrstack –i to print the stack is only mildly tested. -There may well be cases where the stack doesn’t look so great or where managed frames appear unordered with respect to the internal (explicit EE) Frames. -See comments in the code for more details. - -## Mac - -Nope. -There are several silly reasons why the above is #ifdef’d out on the Mac. -The work necessary to enable this on the Mac may well be straightforward. -Some of the reasons the Mac compile failed for me: - -* Need psapi.h for GetModuleInformation and structures it uses. The equivalents need to be found (or implemented!) for the Mac. -* No IID defined for IDebugAdvanced3. (Possibly just a matter of manually rebuilding the right GUID lib for the Mac, if you can find it.) -* Couldn’t find some CLR-specific headers on the Mac build, like metahost.h. diff --git a/src/coreclr/src/ToolBox/SOS/SOS_README.md b/src/coreclr/src/ToolBox/SOS/SOS_README.md new file mode 100644 index 00000000000..298d766fe61 --- /dev/null +++ b/src/coreclr/src/ToolBox/SOS/SOS_README.md @@ -0,0 +1,5 @@ +SOS and other diagnostic tools now ship of band and work with any version of the .NET Core runtime. + +SOS has moved to the diagnostics repo here: https://github.com/dotnet/diagnostics.git. + +Instructions to install SOS: https://github.com/dotnet/diagnostics#installing-sos. diff --git a/src/coreclr/src/ToolBox/SOS/Strike/ApolloNative.rc b/src/coreclr/src/ToolBox/SOS/Strike/ApolloNative.rc deleted file mode 100644 index ba320af9116..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/ApolloNative.rc +++ /dev/null @@ -1,10 +0,0 @@ -// 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/coreclr/src/ToolBox/SOS/Strike/CMakeLists.txt b/src/coreclr/src/ToolBox/SOS/Strike/CMakeLists.txt deleted file mode 100644 index 1b4d74678c7..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/CMakeLists.txt +++ /dev/null @@ -1,230 +0,0 @@ -# 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) - add_definitions(-D_TARGET_WIN64_) - add_definitions(-DDBG_TARGET_64BIT) - add_definitions(-DDBG_TARGET_WIN64) - if(WIN32) - add_definitions(-DSOS_TARGET_ARM64) - endif(WIN32) - remove_definitions(-D_TARGET_ARM64_) - add_definitions(-D_TARGET_AMD64_) - add_definitions(-DDBG_TARGET_AMD64) -elseif(CLR_CMAKE_PLATFORM_ARCH_I386) - add_definitions(-DSOS_TARGET_X86) - add_definitions(-D_TARGET_X86_) - add_definitions(-DDBG_TARGET_32BIT) - if(WIN32) - add_definitions(-DSOS_TARGET_ARM) - endif(WIN32) -elseif(CLR_CMAKE_PLATFORM_ARCH_ARM) - add_definitions(-DSOS_TARGET_ARM) - add_definitions(-D_TARGET_WIN32_) - add_definitions(-D_TARGET_ARM_) - add_definitions(-DDBG_TARGET_32BIT) -elseif(CLR_CMAKE_PLATFORM_ARCH_ARM64) - add_definitions(-DSOS_TARGET_ARM64) - add_definitions(-D_TARGET_WIN64_) - add_definitions(-DDBG_TARGET_64BIT) - add_definitions(-DDBG_TARGET_WIN64) -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(-DPAL_STDCPP_COMPAT) - if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - add_compile_options(-Wno-null-arithmetic) - elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-Wno-conversion-null>) - add_compile_options(-Wno-pointer-arith) - endif() - 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 - ) - - set(SOS_LIBRARY - corguids - debugshim - dbgutil - # share the PAL in the dac module - mscordaccore - palrt - unixcoreruncommon - ) - - 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_LINUX) - list(APPEND - SOS_LIBRARY - createdump_lib - ) - add_definitions(-DCREATE_DUMP_SUPPORTED) -endif(CLR_CMAKE_PLATFORM_LINUX) - -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 - ) - if(WIN32) - list(APPEND - SOS_SOURCES_ARCH - disasmARM.cpp - ) - endif(WIN32) -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}) - -# Add the DAC PAL export mapping file -if(CLR_CMAKE_PLATFORM_LINUX) - list(APPEND SOS_SOURCES ${PAL_REDEFINES_FILE}) -endif(CLR_CMAKE_PLATFORM_LINUX) - -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_LINUX) - add_dependencies(sos pal_redefines_file) -endif(CLR_CMAKE_PLATFORM_LINUX) - -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/coreclr/src/ToolBox/SOS/Strike/EventCallbacks.cpp b/src/coreclr/src/ToolBox/SOS/Strike/EventCallbacks.cpp deleted file mode 100644 index 0066dfa1e87..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/EventCallbacks.cpp +++ /dev/null @@ -1,160 +0,0 @@ -// 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/coreclr/src/ToolBox/SOS/Strike/EventCallbacks.h b/src/coreclr/src/ToolBox/SOS/Strike/EventCallbacks.h deleted file mode 100644 index acd5e412d1f..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/EventCallbacks.h +++ /dev/null @@ -1,68 +0,0 @@ -// 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/coreclr/src/ToolBox/SOS/Strike/ExpressionNode.cpp b/src/coreclr/src/ToolBox/SOS/Strike/ExpressionNode.cpp deleted file mode 100644 index b61dcad4878..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/ExpressionNode.cpp +++ /dev/null @@ -1,2175 +0,0 @@ -// 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 calculating 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 const 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 const WCHAR* pExpression, __in_z const 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 const WCHAR* pParentExpression, ChildKind ck, __in_z const 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(¤tTypeDef)); - - 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]; - 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(¤tTypeDef)); - - 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]; - 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 -// interpreting 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 coming 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, ¤tCharsParsed, &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, ¤tCharsParsed, &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(¤tTypeDef)); - - 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]; - 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, ¶mDef))) - pMD->GetParamProps(paramDef, NULL, NULL, paramName, mdNameLen, ¶mNameLen, 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 const 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 const 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 const 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 const 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]; - const 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]; - - const WCHAR* pCurName = pTypeName + genericParamListStart+1; - for(int i = 0; i < countTypeParams; i++) - { - WCHAR typeParamName[mdNameLen]; - const 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/coreclr/src/ToolBox/SOS/Strike/ExpressionNode.h b/src/coreclr/src/ToolBox/SOS/Strike/ExpressionNode.h deleted file mode 100644 index 2f34e5615b4..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/ExpressionNode.h +++ /dev/null @@ -1,307 +0,0 @@ -// 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 const WCHAR* pExpression, ICorDebugValue* pValue, ICorDebugILFrame* pFrame); - - // Creates a new expression that has an error and no value - ExpressionNode(__in_z const WCHAR* pExpression, __in_z const WCHAR* pErrorMessage); - - // Creates a new child expression - ExpressionNode(__in_z const WCHAR* pParentExpression, ChildKind ck, __in_z const 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 const 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 const WCHAR* pTypeName, ICorDebugType** ppType); - - // Searches the assembly for any ICorDebugType that matches the given fully qualified name - static HRESULT FindTypeByName(ICorDebugAssembly* pAssembly, __in_z const 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 const 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/coreclr/src/ToolBox/SOS/Strike/Native.rc b/src/coreclr/src/ToolBox/SOS/Strike/Native.rc deleted file mode 100644 index 179ddfd24af..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/Native.rc +++ /dev/null @@ -1,10 +0,0 @@ -// 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/coreclr/src/ToolBox/SOS/Strike/UtilCode.h b/src/coreclr/src/ToolBox/SOS/Strike/UtilCode.h deleted file mode 100644 index a002edc89e9..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/UtilCode.h +++ /dev/null @@ -1,11 +0,0 @@ -// 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/coreclr/src/ToolBox/SOS/Strike/WatchCmd.cpp b/src/coreclr/src/ToolBox/SOS/Strike/WatchCmd.cpp deleted file mode 100644 index 443f1dd6ef6..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/WatchCmd.cpp +++ /dev/null @@ -1,331 +0,0 @@ -// 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"<")) return; - pEscapedOutput += 4; - cchOutput -= 4; - } - else if(pInput[i] == L'>') - { - if(0 != wcscat_s(pEscapedOutput, cchOutput, L">")) 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/coreclr/src/ToolBox/SOS/Strike/WatchCmd.h b/src/coreclr/src/ToolBox/SOS/Strike/WatchCmd.h deleted file mode 100644 index a34e391b791..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/WatchCmd.h +++ /dev/null @@ -1,110 +0,0 @@ -// 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/coreclr/src/ToolBox/SOS/Strike/apollososdocs.txt b/src/coreclr/src/ToolBox/SOS/Strike/apollososdocs.txt deleted file mode 100644 index d2ac481ef14..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/apollososdocs.txt +++ /dev/null @@ -1,2755 +0,0 @@ -------------------------------------------------------------------------------- -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 -DumpAsync IP2MD -DumpDelegate U -DumpStackObjects (dso) DumpStack -DumpHeap EEStack -DumpVC CLRStack -GCRoot GCInfo -ObjSize EHInfo -FinalizeQueue BPMD -PrintException (pe) COMState -TraverseHeap StopOnCatch -Watch 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: dumpasync. -!DumpAsync [-addr <Object Address>] - [-mt <MethodTable address>] - [-type <partial type name>] - [-tasks] - [-completed] - [-fields] - [-stacks] - [-roots] - -!DumpAsync traverses the garbage collected heap, looking for objects representing -async state machines as created when an async method's state is transferred to the -heap. This command recognizes async state machines defined as "async void", "async Task", -"async Task<T>", "async ValueTask", and "async ValueTask<T>". It also optionally supports -any other tasks. -\\ - -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: dumpdelegate. -!DumpDelegate <delegate address> - -!DumpDelegate finds and outputs the one or more method descriptors associated with a delegate object. - -For example: - - 0:000> !dumpdelegate - Target Method Name - 000001461bacb0d8 00007ffc5c894b80 ConsoleApp16.Program.InstanceMethod() - 000001461bacb098 00007ffc5c894b68 ConsoleApp16.Program.StaticMethod() -\\ - -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 - 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 - - 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/coreclr/src/ToolBox/SOS/Strike/data.h b/src/coreclr/src/ToolBox/SOS/Strike/data.h deleted file mode 100644 index 9989038653e..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/data.h +++ /dev/null @@ -1,51 +0,0 @@ -// 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/coreclr/src/ToolBox/SOS/Strike/datatarget.cpp b/src/coreclr/src/ToolBox/SOS/Strike/datatarget.cpp deleted file mode 100644 index fe90f0e8254..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/datatarget.cpp +++ /dev/null @@ -1,215 +0,0 @@ -// 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) || defined(SOS_TARGET_X86) - *size = 4; -#else - #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/coreclr/src/ToolBox/SOS/Strike/datatarget.h b/src/coreclr/src/ToolBox/SOS/Strike/datatarget.h deleted file mode 100644 index 0293bc668b2..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/datatarget.h +++ /dev/null @@ -1,90 +0,0 @@ -// 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/coreclr/src/ToolBox/SOS/Strike/disasm.cpp b/src/coreclr/src/ToolBox/SOS/Strike/disasm.cpp deleted file mode 100644 index ceb0fed30de..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/disasm.cpp +++ /dev/null @@ -1,1140 +0,0 @@ -// 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 ((INT_PTR)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) -{ - 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 -#if defined(_TARGET_AMD64_) || defined(_TARGET_X86_) - // 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_ || _TARGET_X86_ - 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); - } -#if defined(_TARGET_AMD64_) || defined(_TARGET_X86_) - 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_ || _TARGET_X86_ - 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/coreclr/src/ToolBox/SOS/Strike/disasm.h b/src/coreclr/src/ToolBox/SOS/Strike/disasm.h deleted file mode 100644 index 2a2d9f7779f..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/disasm.h +++ /dev/null @@ -1,450 +0,0 @@ -// 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 -{ - 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/coreclr/src/ToolBox/SOS/Strike/disasmARM.cpp b/src/coreclr/src/ToolBox/SOS/Strike/disasmARM.cpp deleted file mode 100644 index e9a7a690a12..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/disasmARM.cpp +++ /dev/null @@ -1,627 +0,0 @@ -// 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_ -#define WIN64EXCEPTIONS -#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 definitely 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/coreclr/src/ToolBox/SOS/Strike/disasmARM64.cpp b/src/coreclr/src/ToolBox/SOS/Strike/disasmARM64.cpp deleted file mode 100644 index 6a19fc9377e..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/disasmARM64.cpp +++ /dev/null @@ -1,392 +0,0 @@ -// 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/coreclr/src/ToolBox/SOS/Strike/disasmX86.cpp b/src/coreclr/src/ToolBox/SOS/Strike/disasmX86.cpp deleted file mode 100644 index 0934a824ea7..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/disasmX86.cpp +++ /dev/null @@ -1,1707 +0,0 @@ -// 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, ®namelen); - 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 ®) -{ - int size = 0; - - reg.reg = FindReg(ptr, ®.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 address 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 address 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/coreclr/src/ToolBox/SOS/Strike/dllsext.cpp b/src/coreclr/src/ToolBox/SOS/Strike/dllsext.cpp deleted file mode 100644 index 757a04c91fc..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/dllsext.cpp +++ /dev/null @@ -1,278 +0,0 @@ -// 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/coreclr/src/ToolBox/SOS/Strike/eeheap.cpp b/src/coreclr/src/ToolBox/SOS/Strike/eeheap.cpp deleted file mode 100644 index c28c9641f5b..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/eeheap.cpp +++ /dev/null @@ -1,1948 +0,0 @@ -// 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; -} - -static MethodTableInfo* GetMethodTableInfo(DWORD_PTR dwAddrMethTable) -{ - // 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 NULL; - - - info->BaseSize = dmtd.BaseSize; - info->ComponentSize = dmtd.ComponentSize; - info->bContainsPointers = dmtd.bContainsPointers; - - // The following request doesn't work on older runtimes. For those, the - // objects would just look like non-collectible, which is acceptable. - DacpMethodTableCollectibleData dmtcd; - if (SUCCEEDED(dmtcd.Request(g_sos, dwAddrMethTable))) - { - info->bCollectible = dmtcd.bCollectible; - info->LoaderAllocatorObjectHandle = TO_TADDR(dmtcd.LoaderAllocatorObjectHandle); - } - } - - return info; -} - -BOOL GetSizeEfficient(DWORD_PTR dwAddrCurrObj, - DWORD_PTR dwAddrMethTable, BOOL bLarge, size_t& s, BOOL& bContainsPointers) -{ - MethodTableInfo* info = GetMethodTableInfo(dwAddrMethTable); - if (info == NULL) - { - return FALSE; - } - - 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; -} - -BOOL GetCollectibleDataEfficient(DWORD_PTR dwAddrMethTable, BOOL& bCollectible, TADDR& loaderAllocatorObjectHandle) -{ - MethodTableInfo* info = GetMethodTableInfo(dwAddrMethTable); - if (info == NULL) - { - return FALSE; - } - - bCollectible = info->bCollectible; - loaderAllocatorObjectHandle = info->LoaderAllocatorObjectHandle; - - 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 "u) bytes", total, total); - if (wasted) - ExtOut(" total, 0x%" POINTERSIZE_TYPE "x (%" POINTERSIZE_TYPE "u) 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/coreclr/src/ToolBox/SOS/Strike/exts.cpp b/src/coreclr/src/ToolBox/SOS/Strike/exts.cpp deleted file mode 100644 index 566ba35f194..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/exts.cpp +++ /dev/null @@ -1,434 +0,0 @@ -// 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(); - } -#elif defined(SOS_TARGET_X86) - if (targetArchitecture == IMAGE_FILE_MACHINE_I386) - { - targetMachine = X86Machine::GetInstance(); - } -#elif defined(SOS_TARGET_ARM) - if (targetArchitecture == IMAGE_FILE_MACHINE_ARMNT) - { - targetMachine = ARMMachine::GetInstance(); - } -#elif defined(SOS_TARGET_ARM64) - if (targetArchitecture == IMAGE_FILE_MACHINE_ARM64) - { - targetMachine = ARM64Machine::GetInstance(); - } -#else -#error "Undefined target architecture" -#endif - - if (targetMachine == NULL) - { - g_targetMachine = NULL; - ExtErr("SOS does not support the current target architecture 0x%llx.\n", targetArchitecture); - 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/coreclr/src/ToolBox/SOS/Strike/exts.h b/src/coreclr/src/ToolBox/SOS/Strike/exts.h deleted file mode 100644 index c66ba7ff640..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/exts.h +++ /dev/null @@ -1,521 +0,0 @@ -// 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 - -#ifndef DLLEXPORT -#ifdef _MSC_VER -#define DLLEXPORT __declspec(dllexport) -#else -#define DLLEXPORT __attribute__ ((visibility ("default"))) -#endif // _MSC_VER -#endif // DLLEXPORT - -#define DECLARE_API(extension) \ -CPPMOD DLLEXPORT 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/coreclr/src/ToolBox/SOS/Strike/gchist.cpp b/src/coreclr/src/ToolBox/SOS/Strike/gchist.cpp deleted file mode 100644 index ee9d5b4033f..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/gchist.cpp +++ /dev/null @@ -1,636 +0,0 @@ -// 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/coreclr/src/ToolBox/SOS/Strike/gcroot.cpp b/src/coreclr/src/ToolBox/SOS/Strike/gcroot.cpp deleted file mode 100644 index 1417748f284..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/gcroot.cpp +++ /dev/null @@ -1,2536 +0,0 @@ -// 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" - - -#undef _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 - -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 constraints 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 constraints 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 || node->MTInfo->Collectible) - { - 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 && !node->MTInfo->Collectible) - 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; - - // The following request doesn't work on older runtimes. For those, the - // objects would just look like non-collectible, which is acceptable. - DacpMethodTableCollectibleData dmtcd; - if (SUCCEEDED(dmtcd.Request(g_sos, mt))) - { - curr->Collectible = dmtcd.bCollectible ? true : false; - curr->LoaderAllocatorObjectHandle = TO_TADDR(dmtcd.LoaderAllocatorObjectHandle); - } - - // 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_GC_PAGE_SIZE/card_word_width)) -#else -#define card_size ((size_t)(DT_GC_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 initialize 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("&")); - replace(wname, W("\""), W(""")); - replace(wname, W("'"), W("'")); - replace(wname, W("<"), W("<")); - replace(wname, W(">"), W(">")); - 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::PrintLoaderAllocator(size_t memberValue) -{ - if (m_format == FORMAT_XML) - { - fprintf(m_file, - " <loaderallocator address=\"0x%p\"/>\n", - (PBYTE)memberValue); - } - else if (m_format == FORMAT_CLRPROFILER) - { - fprintf(m_file, - " 0x%p", - (PBYTE)memberValue); - } -} - -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 initialized - - if (!info->bContainsPointers && !info->bCollectible) - return; - - if (info->bContainsPointers) - { - // 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))) - { - if (itr.IsLoaderAllocator()) - { - PrintLoaderAllocator(*itr); - } - else - { - 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/coreclr/src/ToolBox/SOS/Strike/inc/dbgeng.h b/src/coreclr/src/ToolBox/SOS/Strike/inc/dbgeng.h deleted file mode 100644 index cf04574c12a..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/inc/dbgeng.h +++ /dev/null @@ -1,16126 +0,0 @@ -// 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 -// Android defines various fields on struct which are named __reserved[x]; for example, in wchar.h, -// so we must prefix __reserved with __clr_ -#define __clr_reserved -#else -#define __clr_reserved __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 __clr_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 __clr_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 __clr_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 __clr_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 __clr_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 __clr_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/coreclr/src/ToolBox/SOS/Strike/inc/dbghelp.h b/src/coreclr/src/ToolBox/SOS/Strike/inc/dbghelp.h deleted file mode 100644 index 8075929bf06..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/inc/dbghelp.h +++ /dev/null @@ -1,4540 +0,0 @@ -// 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/coreclr/src/ToolBox/SOS/Strike/inc/wdbgexts.h b/src/coreclr/src/ToolBox/SOS/Strike/inc/wdbgexts.h deleted file mode 100644 index 5e0e8c3adf6..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/inc/wdbgexts.h +++ /dev/null @@ -1,2807 +0,0 @@ -// 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/coreclr/src/ToolBox/SOS/Strike/metadata.cpp b/src/coreclr/src/ToolBox/SOS/Strike/metadata.cpp deleted file mode 100644 index 8502aaa45b0..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/metadata.cpp +++ /dev/null @@ -1,1040 +0,0 @@ -// 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" - -/**********************************************************************\ -* 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/coreclr/src/ToolBox/SOS/Strike/ntinfo.h b/src/coreclr/src/ToolBox/SOS/Strike/ntinfo.h deleted file mode 100644 index f3e6e91ff00..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/ntinfo.h +++ /dev/null @@ -1,193 +0,0 @@ -// 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/coreclr/src/ToolBox/SOS/Strike/platformspecific.h b/src/coreclr/src/ToolBox/SOS/Strike/platformspecific.h deleted file mode 100644 index ee416dbcc2a..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/platformspecific.h +++ /dev/null @@ -1,200 +0,0 @@ -// 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 - -// TODO-ARM64-NYI Support for SOS on target with 64K pages -// -// This is probably as simple as redefining DT_OS_PAGE_SIZE to be a function -// which returns the page size of the connected target -#define DT_OS_PAGE_SIZE 4096 -#define DT_GC_PAGE_SIZE 0x1000 - -#endif // !__PLATFORM_SPECIFIC_INCLUDED diff --git a/src/coreclr/src/ToolBox/SOS/Strike/sildasm.cpp b/src/coreclr/src/ToolBox/SOS/Strike/sildasm.cpp deleted file mode 100644 index 097376d553f..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/sildasm.cpp +++ /dev/null @@ -1,1091 +0,0 @@ -// 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; -static char* asString(CQuickBytes *out); - -// 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 (i < numSizes && lowerBounds[i] == 0) - 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/coreclr/src/ToolBox/SOS/Strike/sos.cpp b/src/coreclr/src/ToolBox/SOS/Strike/sos.cpp deleted file mode 100644 index 4aacc2a2e77..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/sos.cpp +++ /dev/null @@ -1,939 +0,0 @@ -// 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" - - -#undef _ASSERTE - -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; - - // The following request doesn't work on older runtimes. For those, the - // objects would just look like non-collectible, which is acceptable. - DacpMethodTableCollectibleData mtcd; - if (SUCCEEDED(mtcd.Request(g_sos, GetMT()))) - { - info->bCollectible = mtcd.bCollectible; - info->LoaderAllocatorObjectHandle = TO_TADDR(mtcd.LoaderAllocatorObjectHandle); - } - } - - 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), mLoaderAllocatorObjectHandle(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), mLoaderAllocatorObjectHandle(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 (mCurr == mLoaderAllocatorObjectHandle) - { - // The mLoaderAllocatorObjectHandle is always the last reference returned - mDone = true; - return *this; - } - - 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; - } - - if (mDone && mLoaderAllocatorObjectHandle != NULL) - { - // The iteration over all regular object references is done, but there is one more - // reference for collectible types - the LoaderAllocator for GC - mCurr = mLoaderAllocatorObjectHandle; - mDone = false; - } - - 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; - BOOL bCollectible = FALSE; - TADDR loaderAllocatorObjectHandle; - - if (!GetSizeEfficient(mObject, mt, FALSE, mObjSize, bContainsPointers)) - Throw<DataRead>("Failed to get size of object."); - - if (!GetCollectibleDataEfficient(mt, bCollectible, loaderAllocatorObjectHandle)) - Throw<DataRead>("Failed to get collectible info of object."); - - if (!bContainsPointers && !bCollectible) - { - mDone = true; - return; - } - - if (bContainsPointers) - { - 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; - } - else - { - mDone = true; - } - - if (bCollectible) - { - mLoaderAllocatorObjectHandle = loaderAllocatorObjectHandle; - if (mDone) - { - // There are no object references, but there is still a reference for - // collectible types - the LoaderAllocator for GC - mCurr = mLoaderAllocatorObjectHandle; - mDone = false; - } - } - } - - - 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."); - } - } - - GCHeap::~GCHeap() - { - delete [] mHeaps; - } - - 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/coreclr/src/ToolBox/SOS/Strike/sos.def b/src/coreclr/src/ToolBox/SOS/Strike/sos.def deleted file mode 100644 index 62560c09feb..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/sos.def +++ /dev/null @@ -1,233 +0,0 @@ -; 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 - DumpAsync - dumpasync=DumpAsync - DumpClass - dumpclass=DumpClass - DumpDelegate - dumpdelegate=DumpDelegate - DumpDomain - dumpdomain=DumpDomain - DumpGCLog - dumpgclog=DumpGCLog - dlog=DumpGCLog - 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/coreclr/src/ToolBox/SOS/Strike/sos.h b/src/coreclr/src/ToolBox/SOS/Strike/sos.h deleted file mode 100644 index 4d154ef76f6..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/sos.h +++ /dev/null @@ -1,801 +0,0 @@ -// 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; - } - - bool IsLoaderAllocator() const - { - return mLoaderAllocatorObjectHandle == mCurr; - } - - 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; - - TADDR mLoaderAllocatorObjectHandle; - - 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 ¶m); - * 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(); - - ~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/coreclr/src/ToolBox/SOS/Strike/sos_md.h b/src/coreclr/src/ToolBox/SOS/Strike/sos_md.h deleted file mode 100644 index a7cd30c3bce..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/sos_md.h +++ /dev/null @@ -1,924 +0,0 @@ -// 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 order 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; - -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/coreclr/src/ToolBox/SOS/Strike/sos_stacktrace.h b/src/coreclr/src/ToolBox/SOS/Strike/sos_stacktrace.h deleted file mode 100644 index ed2b81b54f8..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/sos_stacktrace.h +++ /dev/null @@ -1,174 +0,0 @@ -// 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 - -DLLEXPORT 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. -DLLEXPORT HRESULT CALLBACK _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. -DLLEXPORT HRESULT CALLBACK _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 -// -DLLEXPORT HRESULT CALLBACK _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. -DLLEXPORT HRESULT CALLBACK _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/coreclr/src/ToolBox/SOS/Strike/sos_unixexports.src b/src/coreclr/src/ToolBox/SOS/Strike/sos_unixexports.src deleted file mode 100644 index 84af3dab6c6..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/sos_unixexports.src +++ /dev/null @@ -1,59 +0,0 @@ -; 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 -CreateDump -DumpArray -DumpAssembly -DumpAsync -DumpClass -DumpDelegate -DumpDomain -DumpGCData -DumpHeap -DumpIL -DumpLog -DumpMD -DumpModule -DumpMT -DumpObj -DumpRuntimeTypes -DumpSig -DumpSigElem -DumpStack -DumpStackObjects -DumpVC -EEHeap -GCWhere -EEStack -EHInfo -FinalizeQueue -FindAppDomain -GCInfo -GCRoot -Help -HistClear -HistInit -HistObj -HistObjFind -HistRoot -HistStats -IP2MD -Name2EE -PrintException -StopOnCatch -SyncBlk -Threads -ThreadState -Token2EE -u -VerifyHeap -VerifyStackTrace - -_EFN_GetManagedExcepStack -_EFN_GetManagedExcepStackW -_EFN_GetManagedObjectFieldInfo -_EFN_GetManagedObjectName -_EFN_StackTrace diff --git a/src/coreclr/src/ToolBox/SOS/Strike/sosdocs.txt b/src/coreclr/src/ToolBox/SOS/Strike/sosdocs.txt deleted file mode 100644 index f03d0731f4b..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/sosdocs.txt +++ /dev/null @@ -1,2600 +0,0 @@ -------------------------------------------------------------------------------- -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 -DumpAsync IP2MD -DumpDelegate U -DumpStackObjects (dso) DumpStack -DumpHeap EEStack -DumpVC CLRStack -GCRoot GCInfo -ObjSize EHInfo -FinalizeQueue BPMD -PrintException (pe) COMState -TraverseHeap - -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: dumpasync. -!DumpAsync [-addr <Object Address>] - [-mt <MethodTable address>] - [-type <partial type name>] - [-tasks] - [-completed] - [-fields] - [-stacks] - [-roots] - -!DumpAsync traverses the garbage collected heap, looking for objects representing -async state machines as created when an async method's state is transferred to the -heap. This command recognizes async state machines defined as "async void", "async Task", -"async Task<T>", "async ValueTask", and "async ValueTask<T>". It also optionally supports -any other tasks. -\\ - -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: dumpdelegate. -!DumpDelegate <delegate address> - -!DumpDelegate finds and outputs the one or more method descriptors associated with a delegate object. - -For example: - - 0:000> !dumpdelegate - Target Method Name - 000001461bacb0d8 00007ffc5c894b80 ConsoleApp16.Program.InstanceMethod() - 000001461bacb098 00007ffc5c894b68 ConsoleApp16.Program.StaticMethod() -\\ - -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 - 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 - - 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/coreclr/src/ToolBox/SOS/Strike/sosdocsunix.txt b/src/coreclr/src/ToolBox/SOS/Strike/sosdocsunix.txt deleted file mode 100644 index d4333cfff2f..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/sosdocsunix.txt +++ /dev/null @@ -1,1853 +0,0 @@ -------------------------------------------------------------------------------- -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 -DumpAsync (dumpasync) IP2MD (ip2md) -DumpDelegate (dumpdelegate) u (clru) -DumpStackObjects (dso) DumpStack (dumpstack) -DumpHeap (dumpheap) EEStack (eestack) -DumpVC CLRStack (clrstack) -FinalizeQueue (finalizequeue) GCInfo -GCRoot (gcroot) EHInfo -PrintException (pe) bpmd (bpmd) - -Examining CLR data structures Diagnostic Utilities ------------------------------ ----------------------------- -DumpDomain VerifyHeap -EEHeap (eeheap) FindAppDomain -Name2EE (name2ee) DumpLog (dumplog) -SyncBlk (syncblk) -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) CreateDump (createdump) -HistObj (histobj) Help (soshelp) -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: dumpasync. -!DumpAsync [-addr <Object Address>] - [-mt <MethodTable address>] - [-type <partial type name>] - [-tasks] - [-completed] - [-fields] - [-stacks] - [-roots] - -!DumpAsync traverses the garbage collected heap, looking for objects representing -async state machines as created when an async method's state is transferred to the -heap. This command recognizes async state machines defined as "async void", "async Task", -"async Task<T>", "async ValueTask", and "async ValueTask<T>". It also optionally supports -any other tasks. -\\ - -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: dumpdelegate. -!DumpDelegate <delegate address> - -!DumpDelegate finds and outputs the one or more method descriptors associated with a delegate object. - -For example: - - 0:000> !dumpdelegate - Target Method Name - 000001461bacb0d8 00007ffc5c894b80 ConsoleApp16.Program.InstanceMethod() - 000001461bacb098 00007ffc5c894b68 ConsoleApp16.Program.StaticMethod() -\\ - -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: finalizequeue -FinalizeQueue [-detail] | [-allReady] [-short] - -Displays all objects registered for finalization. -The "-detail" option displays extra information about any SyncBlocks that need -to be cleaned up. This data structure is cached and cleaned up by the -finalizer thread when it runs. - -The "-allReady" option displays all objects that are ready for finalization, -regardless of whether they are already marked by the garbage collection -as such, or will be marked by the next garbage collection. The objects that -are in the "ready for finalization" list are finalizable objects that are -no longer rooted. This option can be very expensive, because it verifies -whether all the objects in the finalizable queues are still rooted. - -The "-short" option limits the output to the address of each object. If it is -used in conjunction with -allReady, it enumerates all objects that have -a finalizer that are no longer rooted. If it is used independently, it lists -all objects in the finalizable and "ready for finalization" queues. -\\ - -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: createdump. -createdump [options] [dumpFileName] --n - create minidump. --h - create minidump with heap (default). --t - create triage minidump. --f - create full core dump (everything). --d - enable diagnostic messages. - -Creates a platform (ELF core on Linux, etc.) minidump. The pid can be placed in the dump -file name with %d. The default is '/tmp/coredump.%d'. -\\ - -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 - -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 - - 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 <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) 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: 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> bt - 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 "clru 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: - - (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/coreclr/src/ToolBox/SOS/Strike/stressLogDump.cpp b/src/coreclr/src/ToolBox/SOS/Strike/stressLogDump.cpp deleted file mode 100644 index 756cc73f7b4..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/stressLogDump.cpp +++ /dev/null @@ -1,549 +0,0 @@ -// 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_s(buff, buffLen, _TRUNCATE, 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 bookkeeping 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, (unsigned)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/coreclr/src/ToolBox/SOS/Strike/strike.cpp b/src/coreclr/src/ToolBox/SOS/Strike/strike.cpp deleted file mode 100644 index ed9b4cf71ac..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/strike.cpp +++ /dev/null @@ -1,15640 +0,0 @@ -// 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 instances 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 <algorithm> - -#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 - -#include <set> -#include <vector> -#include <map> - -BOOL CallStatus; -BOOL ControlC = FALSE; - -IMetaDataDispenserEx *pDisp = NULL; -WCHAR g_mdName[mdNameLen]; - -#ifndef FEATURE_PAL -HMODULE g_hInstance = NULL; -#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 "" -#define SOSThreads "clrthreads" -#else -#define SOSPrefix "!" -#define SOSThreads "!threads" -#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 - -#if defined(_TARGET_WIN64_) -#define DEBUG_STACK_CONTEXT AMD64_CONTEXT -#elif defined(_TARGET_ARM_) // _TARGET_WIN64_ -#define DEBUG_STACK_CONTEXT ARM_CONTEXT -#elif defined(_TARGET_X86_) // _TARGET_ARM_ -#define DEBUG_STACK_CONTEXT X86_CONTEXT -#endif // _TARGET_X86_ - -#ifdef DEBUG_STACK_CONTEXT -// 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]; -DEBUG_STACK_CONTEXT g_FrameContexts[MAX_STACK_FRAMES]; - -static HRESULT -GetContextStackTrace(ULONG osThreadId, PULONG pnumFrames) -{ - PDEBUG_CONTROL4 debugControl4; - HRESULT hr; - - // Do we have advanced capability? - if ((hr = g_ExtControl->QueryInterface(__uuidof(IDebugControl4), (void **)&debugControl4)) == S_OK) - { - ULONG oldId, id; - g_ExtSystem->GetCurrentThreadId(&oldId); - - if ((hr = g_ExtSystem->GetThreadIdBySystemId(osThreadId, &id)) != S_OK) { - return hr; - } - g_ExtSystem->SetCurrentThreadId(id); - - // GetContextStackTrace fills g_FrameContexts 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_FrameContexts, - MAX_STACK_FRAMES*g_targetMachine->GetContextSize(), - g_targetMachine->GetContextSize(), - pnumFrames); - - g_ExtSystem->SetCurrentThreadId(oldId); - debugControl4->Release(); - } - return hr; -} - -#endif // DEBUG_STACK_CONTEXT - -/**********************************************************************\ -* 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); -} - -#if defined(FEATURE_PAL) && defined(_TARGET_AMD64_) -static BOOL UnwindStackFrames(ULONG32 osThreadId); -#endif - -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 unwind = FALSE; - BOOL dml = FALSE; - CMDOption option[] = { - // name, vptr, type, hasValue - {"-EE", &DSFlag.fEEonly, COBOOL, FALSE}, - {"-n", &DSFlag.fSuppressSrcInfo, COBOOL, FALSE}, - {"-unwind", &unwind, 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 sysId = 0, id = 0; - g_ExtSystem->GetCurrentThreadSystemId(&sysId); - ExtOut("OS Thread Id: 0x%x ", sysId); - g_ExtSystem->GetCurrentThreadId(&id); - ExtOut("(%d)\n", id); - -#if defined(FEATURE_PAL) && defined(_TARGET_AMD64_) - if (unwind) - { - UnwindStackFrames(sysId); - } - else -#endif - { - 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(TO_CDADDR(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(TO_CDADDR(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 (TO_CDADDR(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 (TO_CDADDR(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(TO_CDADDR(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"); - - 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.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; - } - - DacpMethodTableCollectibleData vMethTableCollectible; - vMethTableCollectible.Request(g_sos, TO_CDADDR(dwStartAddr)); - - 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")); - - if (vMethTableCollectible.LoaderAllocatorObjectHandle != NULL) - { - TADDR loaderAllocator; - if (SUCCEEDED(MOVE(loaderAllocator, vMethTableCollectible.LoaderAllocatorObjectHandle))) - { - table.WriteRow("LoaderAllocator:", ObjectPtr(loaderAllocator)); - } - } - - 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(TO_CDADDR(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 (TO_CDADDR(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 (TO_CDADDR(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 (TO_CDADDR(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 (TO_CDADDR(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 (TO_CDADDR(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.ElementTypeHandle, 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; -} - -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to dump the contents of a delegate from a * -* given address. * -* * -\**********************************************************************/ - -DECLARE_API(DumpDelegate) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - try - { - BOOL dml = FALSE; - DWORD_PTR dwAddr = 0; - - CMDOption option[] = - { // name, vptr, type, hasValue - {"/d", &dml, COBOOL, FALSE} - }; - CMDValue arg[] = - { // vptr, type - {&dwAddr, COHEX} - }; - size_t nArg; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - if (nArg != 1) - { - ExtOut("Usage: !DumpDelegate <delegate object address>\n"); - return Status; - } - - EnableDMLHolder dmlHolder(dml); - CLRDATA_ADDRESS delegateAddr = TO_CDADDR(dwAddr); - - if (!sos::IsObject(delegateAddr)) - { - ExtOut("Invalid object.\n"); - } - else - { - sos::Object delegateObj = TO_TADDR(delegateAddr); - if (!IsDerivedFrom(TO_CDADDR(delegateObj.GetMT()), W("System.Delegate"))) - { - ExtOut("Object of type '%S' is not a delegate.", delegateObj.GetTypeName()); - } - else - { - ExtOut("Target Method Name\n"); - - std::vector<CLRDATA_ADDRESS> delegatesRemaining; - delegatesRemaining.push_back(delegateAddr); - while (delegatesRemaining.size() > 0) - { - delegateAddr = delegatesRemaining.back(); - delegatesRemaining.pop_back(); - delegateObj = TO_TADDR(delegateAddr); - - int offset; - if ((offset = GetObjFieldOffset(delegateObj.GetAddress(), delegateObj.GetMT(), W("_target"))) != 0) - { - CLRDATA_ADDRESS target; - MOVE(target, delegateObj.GetAddress() + offset); - - if ((offset = GetObjFieldOffset(delegateObj.GetAddress(), delegateObj.GetMT(), W("_invocationList"))) != 0) - { - CLRDATA_ADDRESS invocationList; - MOVE(invocationList, delegateObj.GetAddress() + offset); - - if ((offset = GetObjFieldOffset(delegateObj.GetAddress(), delegateObj.GetMT(), W("_invocationCount"))) != 0) - { - int invocationCount; - MOVE(invocationCount, delegateObj.GetAddress() + offset); - - if (invocationList == NULL) - { - CLRDATA_ADDRESS md; - DMLOut("%s ", DMLObject(target)); - if (TryGetMethodDescriptorForDelegate(delegateAddr, &md)) - { - DMLOut("%s ", DMLMethodDesc(md)); - NameForMD_s((DWORD_PTR)md, g_mdName, mdNameLen); - ExtOut("%S\n", g_mdName); - } - else - { - ExtOut("(unknown)\n"); - } - } - else if (sos::IsObject(invocationList, false)) - { - DacpObjectData objData; - if (objData.Request(g_sos, invocationList) == S_OK && - objData.ObjectType == OBJ_ARRAY && - invocationCount <= (int)objData.dwNumComponents) - { - for (int i = 0; i < invocationCount; i++) - { - CLRDATA_ADDRESS elementPtr; - MOVE(elementPtr, TO_CDADDR(objData.ArrayDataPtr + (i * objData.dwComponentSize))); - if (elementPtr != NULL && sos::IsObject(elementPtr, false)) - { - delegatesRemaining.push_back(elementPtr); - } - } - } - } - } - } - } - } - } - } - - return S_OK; - } - catch (const sos::Exception &e) - { - ExtOut("%s\n", e.what()); - return E_FAIL; - } -} - -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 - // TRUE if this element represents the last frame of the foreign - // exception stack trace. - BOOL fIsLastFrameFromForeignStackTrace; - -}; - -#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(CLRDATA_ADDRESS taObj, CLRDATA_ADDRESS 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 ((DWORD)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(CLRDATA_ADDRESS taObj, BOOL bLineNumbers = FALSE) -{ - HRESULT Status = S_OK; - - DacpObjectData objData; - if ((Status=objData.Request(g_sos, 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, 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, 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(TO_CDADDR(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(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)); - if (adsData.sharedDomain != NULL) - { - 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(TO_CDADDR(objAddr), TO_CDADDR(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 async state machines on GC heap, * -* displaying details about each async operation found. * -* (May not work if GC is in progress.) * -* * -\**********************************************************************/ - -void ResolveContinuation(CLRDATA_ADDRESS* contAddr) -{ - // Ideally this continuation is itself an async method box. - sos::Object contObj = TO_TADDR(*contAddr); - if (GetObjFieldOffset(contObj.GetAddress(), contObj.GetMT(), W("StateMachine")) == 0) - { - // It was something else. - - // If it's a standard task continuation, get its task field. - int offset; - if ((offset = GetObjFieldOffset(contObj.GetAddress(), contObj.GetMT(), W("m_task"))) != 0) - { - MOVE(*contAddr, contObj.GetAddress() + offset); - if (sos::IsObject(*contAddr, false)) - { - contObj = TO_TADDR(*contAddr); - } - } - else - { - // If it's storing an action wrapper, try to follow to that action's target. - if ((offset = GetObjFieldOffset(contObj.GetAddress(), contObj.GetMT(), W("m_action"))) != 0) - { - MOVE(*contAddr, contObj.GetAddress() + offset); - if (sos::IsObject(*contAddr, false)) - { - contObj = TO_TADDR(*contAddr); - } - } - - // If we now have an Action, try to follow through to the delegate's target. - if ((offset = GetObjFieldOffset(contObj.GetAddress(), contObj.GetMT(), W("_target"))) != 0) - { - MOVE(*contAddr, contObj.GetAddress() + offset); - if (sos::IsObject(*contAddr, false)) - { - contObj = TO_TADDR(*contAddr); - - // In some cases, the delegate's target might be a ContinuationWrapper, in which case we want to unwrap that as well. - if (_wcsncmp(contObj.GetTypeName(), W("System.Runtime.CompilerServices.AsyncMethodBuilderCore+ContinuationWrapper"), 74) == 0 && - (offset = GetObjFieldOffset(contObj.GetAddress(), contObj.GetMT(), W("_continuation"))) != 0) - { - MOVE(*contAddr, contObj.GetAddress() + offset); - if (sos::IsObject(*contAddr, false)) - { - contObj = TO_TADDR(*contAddr); - if ((offset = GetObjFieldOffset(contObj.GetAddress(), contObj.GetMT(), W("_target"))) != 0) - { - MOVE(*contAddr, contObj.GetAddress() + offset); - if (sos::IsObject(*contAddr, false)) - { - contObj = TO_TADDR(*contAddr); - } - } - } - } - } - } - } - - // Use whatever object we ended with. - *contAddr = contObj.GetAddress(); - } -} - -bool TryGetContinuation(CLRDATA_ADDRESS addr, CLRDATA_ADDRESS mt, CLRDATA_ADDRESS* contAddr) -{ - // Get the continuation field from the task. - int offset = GetObjFieldOffset(addr, mt, W("m_continuationObject")); - if (offset != 0) - { - DWORD_PTR contObjPtr; - MOVE(contObjPtr, addr + offset); - if (sos::IsObject(contObjPtr, false)) - { - *contAddr = TO_CDADDR(contObjPtr); - ResolveContinuation(contAddr); - return true; - } - } - - return false; -} - -struct AsyncRecord -{ - CLRDATA_ADDRESS Address; - CLRDATA_ADDRESS MT; - DWORD Size; - CLRDATA_ADDRESS StateMachineAddr; - CLRDATA_ADDRESS StateMachineMT; - BOOL FilteredByOptions; - BOOL IsStateMachine; - BOOL IsValueType; - BOOL IsTopLevel; - int TaskStateFlags; - int StateValue; - std::vector<CLRDATA_ADDRESS> Continuations; -}; - -bool AsyncRecordIsCompleted(AsyncRecord& ar) -{ - const int TASK_STATE_COMPLETED_MASK = 0x1600000; - return (ar.TaskStateFlags & TASK_STATE_COMPLETED_MASK) != 0; -} - -const char* GetAsyncRecordStatusDescription(AsyncRecord& ar) -{ - const int TASK_STATE_RAN_TO_COMPLETION = 0x1000000; - const int TASK_STATE_FAULTED = 0x200000; - const int TASK_STATE_CANCELED = 0x400000; - - if ((ar.TaskStateFlags & TASK_STATE_RAN_TO_COMPLETION) != 0) return "Success"; - if ((ar.TaskStateFlags & TASK_STATE_FAULTED) != 0) return "Failed"; - if ((ar.TaskStateFlags & TASK_STATE_CANCELED) != 0) return "Canceled"; - return "Pending"; -} - -void ExtOutTaskDelegateMethod(sos::Object& obj) -{ - DacpFieldDescData actionField; - int offset = GetObjFieldOffset(obj.GetAddress(), obj.GetMT(), W("m_action"), TRUE, &actionField); - if (offset != 0) - { - CLRDATA_ADDRESS actionAddr; - MOVE(actionAddr, obj.GetAddress() + offset); - CLRDATA_ADDRESS actionMD; - if (actionAddr != NULL && TryGetMethodDescriptorForDelegate(actionAddr, &actionMD)) - { - NameForMD_s((DWORD_PTR)actionMD, g_mdName, mdNameLen); - ExtOut("(%S) ", g_mdName); - } - } -} - -void ExtOutTaskStateFlagsDescription(int stateFlags) -{ - if (stateFlags == 0) return; - - ExtOut("State Flags: "); - - // TaskCreationOptions.* - if ((stateFlags & 0x01) != 0) ExtOut("PreferFairness "); - if ((stateFlags & 0x02) != 0) ExtOut("LongRunning "); - if ((stateFlags & 0x04) != 0) ExtOut("AttachedToParent "); - if ((stateFlags & 0x08) != 0) ExtOut("DenyChildAttach "); - if ((stateFlags & 0x10) != 0) ExtOut("HideScheduler "); - if ((stateFlags & 0x40) != 0) ExtOut("RunContinuationsAsynchronously "); - - // InternalTaskOptions.* - if ((stateFlags & 0x0200) != 0) ExtOut("ContinuationTask "); - if ((stateFlags & 0x0400) != 0) ExtOut("PromiseTask "); - if ((stateFlags & 0x1000) != 0) ExtOut("LazyCancellation "); - if ((stateFlags & 0x2000) != 0) ExtOut("QueuedByRuntime "); - if ((stateFlags & 0x4000) != 0) ExtOut("DoNotDispose "); - - // TASK_STATE_* - if ((stateFlags & 0x10000) != 0) ExtOut("STARTED "); - if ((stateFlags & 0x20000) != 0) ExtOut("DELEGATE_INVOKED "); - if ((stateFlags & 0x40000) != 0) ExtOut("DISPOSED "); - if ((stateFlags & 0x80000) != 0) ExtOut("EXCEPTIONOBSERVEDBYPARENT "); - if ((stateFlags & 0x100000) != 0) ExtOut("CANCELLATIONACKNOWLEDGED "); - if ((stateFlags & 0x200000) != 0) ExtOut("FAULTED "); - if ((stateFlags & 0x400000) != 0) ExtOut("CANCELED "); - if ((stateFlags & 0x800000) != 0) ExtOut("WAITING_ON_CHILDREN "); - if ((stateFlags & 0x1000000) != 0) ExtOut("RAN_TO_COMPLETION "); - if ((stateFlags & 0x2000000) != 0) ExtOut("WAITINGFORACTIVATION "); - if ((stateFlags & 0x4000000) != 0) ExtOut("COMPLETION_RESERVED "); - if ((stateFlags & 0x8000000) != 0) ExtOut("THREAD_WAS_ABORTED "); - if ((stateFlags & 0x10000000) != 0) ExtOut("WAIT_COMPLETION_NOTIFICATION "); - if ((stateFlags & 0x20000000) != 0) ExtOut("EXECUTIONCONTEXT_IS_NULL "); - if ((stateFlags & 0x40000000) != 0) ExtOut("TASKSCHEDULED_WAS_FIRED "); - - ExtOut("\n"); -} - -void ExtOutStateMachineFields(AsyncRecord& ar) -{ - DacpMethodTableData mtabledata; - DacpMethodTableFieldData vMethodTableFields; - if (mtabledata.Request(g_sos, ar.StateMachineMT) == S_OK && - vMethodTableFields.Request(g_sos, ar.StateMachineMT) == S_OK && - vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0) - { - DisplayFields(ar.StateMachineMT, &mtabledata, &vMethodTableFields, (DWORD_PTR)ar.StateMachineAddr, TRUE, ar.IsValueType); - } -} - -void FindStateMachineTypes(DWORD_PTR* corelibModule, mdTypeDef* stateMachineBox, mdTypeDef* debugStateMachineBox) -{ - int numModule; - ArrayHolder<DWORD_PTR> moduleList = ModuleFromName(const_cast<LPSTR>("System.Private.CoreLib.dll"), &numModule); - if (moduleList != NULL && numModule == 1) - { - *corelibModule = moduleList[0]; - GetInfoFromName(*corelibModule, "System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1", stateMachineBox); - GetInfoFromName(*corelibModule, "System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+DebugFinalizableAsyncStateMachineBox`1", debugStateMachineBox); - } - else - { - *corelibModule = 0; - *stateMachineBox = 0; - *debugStateMachineBox = 0; - } -} - -DECLARE_API(DumpAsync) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - if (!g_snapshot.Build()) - { - ExtOut("Unable to build snapshot of the garbage collector state\n"); - return E_FAIL; - } - - try - { - // Process command-line arguments. - size_t nArg = 0; - TADDR mt = NULL, addr = NULL; - ArrayHolder<char> ansiType = NULL; - ArrayHolder<WCHAR> type = NULL; - BOOL dml = FALSE, includeCompleted = FALSE, includeStacks = FALSE, includeRoots = FALSE, includeAllTasks = FALSE, dumpFields = FALSE; - CMDOption option[] = - { // name, vptr, type, hasValue - { "-addr", &addr, COHEX, TRUE }, // dump only the async object at the specified address - { "-mt", &mt, COHEX, TRUE }, // dump only async objects with a given MethodTable - { "-type", &ansiType, COSTRING, TRUE }, // dump only async objects that contain the specified type substring - { "-tasks", &includeAllTasks, COBOOL, FALSE }, // include all tasks that can be found on the heap, not just async methods - { "-completed", &includeCompleted, COBOOL, FALSE }, // include async objects that are in a completed state - { "-fields", &dumpFields, COBOOL, FALSE }, // show relevant fields of found async objects - { "-stacks", &includeStacks, COBOOL, FALSE }, // gather and output continuation/stack information - { "-roots", &includeRoots, COBOOL, FALSE }, // gather and output GC root information -#ifndef FEATURE_PAL - { "/d", &dml, COBOOL, FALSE }, // Debugger Markup Language -#endif - }; - if (!GetCMDOption(args, option, _countof(option), NULL, 0, &nArg) || nArg != 0) - { - sos::Throw<sos::Exception>( - "Usage: DumpAsync [-addr ObjectAddr] [-mt MethodTableAddr] [-type TypeName] [-tasks] [-completed] [-fields] [-stacks] [-roots]\n" - "[-addr ObjectAddr] => Only display the async object at the specified address.\n" - "[-mt MethodTableAddr] => Only display top-level async objects with the specified method table address.\n" - "[-type TypeName] => Only display top-level async objects whose type name includes the specified substring.\n" - "[-tasks] => Include Task and Task-derived objects, in addition to any state machine objects found.\n" - "[-completed] => Include async objects that represent completed operations but that are still on the heap.\n" - "[-fields] => Show the fields of state machines.\n" - "[-stacks] => Gather, output, and consolidate based on continuation chains / async stacks for discovered async objects.\n" - "[-roots] => Perform a gcroot on each rendered async object.\n" - ); - } - if (ansiType != NULL) - { - size_t ansiTypeLen = strlen(ansiType) + 1; - type = new WCHAR[ansiTypeLen]; - MultiByteToWideChar(CP_ACP, 0, ansiType, -1, type, (int)ansiTypeLen); - } - - EnableDMLHolder dmlHolder(dml); - BOOL hasTypeFilter = mt != NULL || ansiType != NULL || addr != NULL; - - // Display a message if the heap isn't verified. - sos::GCHeap gcheap; - if (!gcheap.AreGCStructuresValid()) - { - DisplayInvalidStructuresMessage(); - } - - // Find the state machine types - DWORD_PTR corelibModule; - mdTypeDef stateMachineBoxMd, debugStateMachineBoxMd; - FindStateMachineTypes(&corelibModule, &stateMachineBoxMd, &debugStateMachineBoxMd); - - // Walk each heap object looking for async state machine objects. As we're targeting .NET Core 2.1+, all such objects - // will be Task or Task-derived types. - std::map<CLRDATA_ADDRESS, AsyncRecord> asyncRecords; - for (sos::ObjectIterator itr = gcheap.WalkHeap(); !IsInterrupt() && itr != NULL; ++itr) - { - // Skip objects too small to be state machines or tasks, avoiding some compiler-generated caching data structures. - if (itr->GetSize() <= 24) - { - continue; - } - - // Match only async objects. - if (includeAllTasks) - { - // If the user has selected to include all tasks and not just async state machine boxes, we simply need to validate - // that this is Task or Task-derived, and if it's not, skip it. - if (!IsDerivedFrom(itr->GetMT(), W("System.Threading.Tasks.Task"))) - { - continue; - } - } - else - { - // Otherwise, we only care about AsyncStateMachineBox`1 as well as the DebugFinalizableAsyncStateMachineBox`1 - // that's used when certain ETW events are set. - DacpMethodTableData mtdata; - if (mtdata.Request(g_sos, TO_TADDR(itr->GetMT())) != S_OK || - mtdata.Module != corelibModule || - (mtdata.cl != stateMachineBoxMd && mtdata.cl != debugStateMachineBoxMd)) - { - continue; - } - } - - // Create an AsyncRecord to store the state for this instance. We're likely going to keep the object at this point, - // though we may still discard/skip it with a few checks later; to do that, though, we'll need some of the info - // gathered here, so we construct the record to store the data. - AsyncRecord ar; - ar.Address = itr->GetAddress(); - ar.MT = itr->GetMT(); - ar.Size = (DWORD)itr->GetSize(); - ar.StateMachineAddr = itr->GetAddress(); - ar.StateMachineMT = itr->GetMT(); - ar.IsValueType = false; - ar.IsTopLevel = true; - ar.IsStateMachine = false; - ar.TaskStateFlags = 0; - ar.StateValue = 0; - ar.FilteredByOptions = // we process all objects to support forming proper chains, but then only display ones that match the user's request - (mt == NULL || mt == itr->GetMT()) && // Match only MTs the user requested. - (type == NULL || _wcsstr(itr->GetTypeName(), type) != NULL) && // Match only type name substrings the user requested. - (addr == NULL || addr == itr->GetAddress()); // Match only the object at the specified address. - - // Get the state flags for the task. This is used to determine whether async objects are completed (and thus should - // be culled by default). It avoids our needing to depend on interpreting the compiler's "<>1__state" field, and also lets - // us display state information for non-async state machine objects. - DacpFieldDescData stateFlagsField; - int offset = GetObjFieldOffset(ar.Address, ar.MT, W("m_stateFlags"), TRUE, &stateFlagsField); - if (offset != 0) - { - MOVE(ar.TaskStateFlags, ar.Address + offset); - } - - // Get the async state machine object's StateMachine field. - DacpFieldDescData stateMachineField; - int stateMachineFieldOffset = GetObjFieldOffset(TO_CDADDR(itr->GetAddress()), itr->GetMT(), W("StateMachine"), TRUE, &stateMachineField); - if (stateMachineFieldOffset != 0) - { - ar.IsStateMachine = true; - ar.IsValueType = stateMachineField.Type == ELEMENT_TYPE_VALUETYPE; - - // Get the address and method table of the state machine. While it'll generally be a struct, it is valid for it to be a - // class (the C# compiler generates a class in debug builds to better support Edit-And-Continue), so we accommodate both. - DacpFieldDescData stateField; - int stateFieldOffset = -1; - if (ar.IsValueType) - { - ar.StateMachineAddr = itr->GetAddress() + stateMachineFieldOffset; - ar.StateMachineMT = stateMachineField.MTOfType; - stateFieldOffset = GetValueFieldOffset(ar.StateMachineMT, W("<>1__state"), &stateField); - } - else - { - MOVE(ar.StateMachineAddr, itr->GetAddress() + stateMachineFieldOffset); - DacpObjectData objData; - if (objData.Request(g_sos, ar.StateMachineAddr) == S_OK) - { - ar.StateMachineMT = objData.MethodTable; // update from Canon to actual type - stateFieldOffset = GetObjFieldOffset(ar.StateMachineAddr, ar.StateMachineMT, W("<>1__state"), TRUE, &stateField); - } - } - - if (stateFieldOffset >= 0 && (ar.IsValueType || stateFieldOffset != 0)) - { - MOVE(ar.StateValue, ar.StateMachineAddr + stateFieldOffset); - } - } - - // If we only want to include incomplete async objects, skip this one if it's completed. - if (!includeCompleted && AsyncRecordIsCompleted(ar)) - { - continue; - } - - // If the user has asked to include "async stacks" information, resolve any continuation - // that might be registered with it. This could be a single continuation, or it could - // be a list of continuations in the case of the same task being awaited multiple times. - CLRDATA_ADDRESS nextAddr; - if (includeStacks && TryGetContinuation(itr->GetAddress(), itr->GetMT(), &nextAddr)) - { - sos::Object contObj = TO_TADDR(nextAddr); - if (_wcsncmp(contObj.GetTypeName(), W("System.Collections.Generic.List`1"), 33) == 0) - { - // The continuation is a List<object>. Iterate through its internal object[] - // looking for non-null objects, and adding each one as a continuation. - int itemsOffset = GetObjFieldOffset(contObj.GetAddress(), contObj.GetMT(), W("_items")); - if (itemsOffset != 0) - { - DWORD_PTR listItemsPtr; - MOVE(listItemsPtr, contObj.GetAddress() + itemsOffset); - if (sos::IsObject(listItemsPtr, false)) - { - DacpObjectData objData; - if (objData.Request(g_sos, TO_CDADDR(listItemsPtr)) == S_OK && objData.ObjectType == OBJ_ARRAY) - { - for (SIZE_T i = 0; i < objData.dwNumComponents; i++) - { - CLRDATA_ADDRESS elementPtr; - MOVE(elementPtr, TO_CDADDR(objData.ArrayDataPtr + (i * objData.dwComponentSize))); - if (elementPtr != NULL && sos::IsObject(elementPtr, false)) - { - ResolveContinuation(&elementPtr); - ar.Continuations.push_back(elementPtr); - } - } - } - } - } - } - else - { - ar.Continuations.push_back(contObj.GetAddress()); - } - } - - // We've gathered all of the needed information for this heap object. Add it to our list of async records. - asyncRecords.insert(std::pair<CLRDATA_ADDRESS, AsyncRecord>(ar.Address, ar)); - } - - // As with DumpHeap, output a summary table about all of the objects we found. In contrast, though, his is based on the filtered - // list of async records we gathered rather than everything on the heap. - if (addr == NULL) // no point in stats if we're only targeting a single object - { - HeapStat stats; - for (std::map<CLRDATA_ADDRESS, AsyncRecord>::iterator arIt = asyncRecords.begin(); arIt != asyncRecords.end(); ++arIt) - { - if (!hasTypeFilter || arIt->second.FilteredByOptions) - { - stats.Add((DWORD_PTR)arIt->second.MT, (DWORD)arIt->second.Size); - } - } - stats.Sort(); - stats.Print(); - } - - // If the user has asked for "async stacks" and if there's not MT/type name filter, look through all of our async records - // to find the "top-level" nodes that start rather than that are a part of a continuation chain. When we then iterate through - // async records, we only print ones out that are still classified as top-level. We don't do this if there's a type filter - // because in that case we consider those and only those objects to be top-level. - if (includeStacks && !hasTypeFilter) - { - size_t uniqueChains = asyncRecords.size(); - for (std::map<CLRDATA_ADDRESS, AsyncRecord>::iterator arIt = asyncRecords.begin(); arIt != asyncRecords.end(); ++arIt) - { - for (std::vector<CLRDATA_ADDRESS>::iterator contIt = arIt->second.Continuations.begin(); contIt != arIt->second.Continuations.end(); ++contIt) - { - std::map<CLRDATA_ADDRESS, AsyncRecord>::iterator found = asyncRecords.find(*contIt); - if (found != asyncRecords.end()) - { - if (found->second.IsTopLevel) - { - found->second.IsTopLevel = false; - uniqueChains--; - } - } - } - } - - ExtOut("In %d chains.\n", uniqueChains); - } - - // Print out header for the main line of each result. - ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %8s ", "Address", "MT", "Size"); - if (includeCompleted) ExtOut("%8s ", "Status"); - ExtOut("%10s %s\n", "State", "Description"); - - // Output each top-level async record. - int counter = 0; - for (std::map<CLRDATA_ADDRESS, AsyncRecord>::iterator arIt = asyncRecords.begin(); arIt != asyncRecords.end(); ++arIt) - { - if (!arIt->second.IsTopLevel || (hasTypeFilter && !arIt->second.FilteredByOptions)) - { - continue; - } - - // Output the state machine's details as a single line. - sos::Object obj = TO_TADDR(arIt->second.Address); - if (arIt->second.IsStateMachine) - { - // This has a StateMachine. Output its details. - sos::MethodTable mt = TO_TADDR(arIt->second.StateMachineMT); - DMLOut("%s %s %8d ", DMLAsync(obj.GetAddress()), DMLDumpHeapMT(obj.GetMT()), obj.GetSize()); - if (includeCompleted) ExtOut("%8s ", GetAsyncRecordStatusDescription(arIt->second)); - ExtOut("%10d %S\n", arIt->second.StateValue, mt.GetName()); - if (dumpFields) ExtOutStateMachineFields(arIt->second); - } - else - { - // This does not have a StateMachine. Output the details of the Task itself. - DMLOut("%s %s %8d ", DMLAsync(obj.GetAddress()), DMLDumpHeapMT(obj.GetMT()), obj.GetSize()); - if (includeCompleted) ExtOut("%8s ", GetAsyncRecordStatusDescription(arIt->second)); - ExtOut("[%08x] %S ", arIt->second.TaskStateFlags, obj.GetTypeName()); - ExtOutTaskDelegateMethod(obj); - ExtOut("\n"); - if (dumpFields) ExtOutTaskStateFlagsDescription(arIt->second.TaskStateFlags); - } - - // If we gathered any continuations for this record, output the chains now. - if (includeStacks && arIt->second.Continuations.size() > 0) - { - ExtOut(includeAllTasks ? "Continuation chains:\n" : "Async \"stack\":\n"); - std::vector<std::pair<int, CLRDATA_ADDRESS>> continuationChainToExplore; - continuationChainToExplore.push_back(std::pair<int, CLRDATA_ADDRESS>(1, obj.GetAddress())); - - // Do a depth-first traversal of continuations, outputting each continuation found and then - // looking in our gathered objects list for its continuations. - std::set<CLRDATA_ADDRESS> seen; - while (continuationChainToExplore.size() > 0) - { - // Pop the next continuation from the stack. - std::pair<int, CLRDATA_ADDRESS> cur = continuationChainToExplore.back(); - continuationChainToExplore.pop_back(); - - // Get the async record for this continuation. It should be one we already know about. - std::map<CLRDATA_ADDRESS, AsyncRecord>::iterator curAsyncRecord = asyncRecords.find(cur.second); - if (curAsyncRecord == asyncRecords.end()) - { - continue; - } - - // Make sure to avoid cycles in the rare case where async records may refer to each other. - if (seen.find(cur.second) != seen.end()) - { - continue; - } - seen.insert(cur.second); - - // Iterate through all continuations from this object. - for (std::vector<CLRDATA_ADDRESS>::iterator contIt = curAsyncRecord->second.Continuations.begin(); contIt != curAsyncRecord->second.Continuations.end(); ++contIt) - { - sos::Object cont = TO_TADDR(*contIt); - - // Print out the depth of the continuation with dots, then its address. - for (int i = 0; i < cur.first; i++) ExtOut("."); - DMLOut("%s ", DMLObject(cont.GetAddress())); - - // Print out the name of the method for this task's delegate if it has one (state machines won't, but others tasks may). - ExtOutTaskDelegateMethod(cont); - - // Find the async record for this continuation, and output its name. If it's a state machine, - // also output its current state value so that a user can see at a glance its status. - std::map<CLRDATA_ADDRESS, AsyncRecord>::iterator contAsyncRecord = asyncRecords.find(cont.GetAddress()); - if (contAsyncRecord != asyncRecords.end()) - { - sos::MethodTable contMT = TO_TADDR(contAsyncRecord->second.StateMachineMT); - if (contAsyncRecord->second.IsStateMachine) ExtOut("(%d) ", contAsyncRecord->second.StateValue); - ExtOut("%S\n", contMT.GetName()); - if (contAsyncRecord->second.IsStateMachine && dumpFields) ExtOutStateMachineFields(contAsyncRecord->second); - } - else - { - ExtOut("%S\n", cont.GetTypeName()); - } - - // Add this continuation to the stack to explore. - continuationChainToExplore.push_back(std::pair<int, CLRDATA_ADDRESS>(cur.first + 1, *contIt)); - } - } - } - - // Finally, output gcroots, as they can serve as alternative/more detailed "async stacks", and also help to highlight - // state machines that aren't being kept alive. However, they're more expensive to compute, so they're opt-in. - if (includeRoots) - { - ExtOut("GC roots:\n"); - IncrementIndent(); - GCRootImpl gcroot; - int numRoots = gcroot.PrintRootsForObject(obj.GetAddress(), FALSE, FALSE); - DecrementIndent(); - if (numRoots == 0 && !AsyncRecordIsCompleted(arIt->second)) - { - ExtOut("Incomplete state machine or task with 0 roots.\n"); - } - } - - // If we're rendering more than one line per entry, output a separator to help distinguish the entries. - if (dumpFields || includeStacks || includeRoots) - { - ExtOut("--------------------------------------------------------------------------------\n"); - } - } - - return S_OK; - } - catch (const sos::Exception &e) - { - ExtOut("%s\n", e.what()); - return E_FAIL; - } -} - -/**********************************************************************\ -* 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 data structures", // fgm_grow_table - "Didn't have enough memory to commit the internal GC data structures", // 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 across 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 -} - -#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); -#ifdef FEATURE_COMINTEROP - ExtOut("CCW %d\n", CCWCount); - ExtOut("RCW %d\n", RCWCount); - ExtOut("ComClassFactory %d\n", CFCount); -#endif - 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; -} - -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); - - if (adsData.sharedDomain != NULL) - { - 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; - - 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; - } - - if (tlsArrayAddr == NULL) - { - continue; - } - - TADDR moduleTlsDataAddr = 0; - if (!SafeReadMemory (tlsArrayAddr + sizeof (void*) * (dwCLRTLSDataIndex & 0xFFFF), &moduleTlsDataAddr, sizeof (void**), NULL)) - { - PrintLn("Failed to get Tls expansion slots for thread ", ThreadID(SysId)); - continue; - } - - CLRTLSDataAddr = moduleTlsDataAddr + ((dwCLRTLSDataIndex & 0x7FFF0000) >> 16) + OFFSETOF__TLS__tls_EETlsData; - - 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_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 - -HRESULT SwitchToExceptionThread() -{ - HRESULT Status; - - DacpThreadStoreData ThreadStore; - if ((Status = ThreadStore.Request(g_sos)) != S_OK) - { - Print("Failed to request ThreadStore\n"); - return Status; - } - - 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; - } - - TADDR taLTOH; - if (Thread.lastThrownObjectHandle != NULL) - { - if (SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle), &taLTOH, sizeof(taLTOH), NULL)) - { - if (taLTOH != NULL) - { - ULONG id; - if (g_ExtSystem->GetThreadIdBySystemId(Thread.osThreadId, &id) == S_OK) - { - if (g_ExtSystem->SetCurrentThreadId(id) == S_OK) - { - PrintLn("Found managed exception on thread ", ThreadID(Thread.osThreadId)); - break; - } - } - } - } - } - - CurThread = Thread.nextThread; - } - - return Status; -} - -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 bSwitchToManagedExceptionThread = FALSE; - BOOL dml = FALSE; - - CMDOption option[] = - { // name, vptr, type, hasValue - {"-special", &bPrintSpecialThreads, COBOOL, FALSE}, - {"-live", &bPrintLiveThreadsOnly, COBOOL, FALSE}, - {"-managedexception", &bSwitchToManagedExceptionThread, COBOOL, FALSE}, -#ifndef FEATURE_PAL - {"/d", &dml, COBOOL, FALSE}, -#endif - }; - if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL)) - { - return Status; - } - - if (bSwitchToManagedExceptionThread) - { - return SwitchToExceptionThread(); - } - - // 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; - } - - BOOL UpdateKnownCodeAddress(TADDR mod, CLRDATA_ADDRESS bpLocation) - { - PendingBreakpoint *pCur = m_breakpoints; - BOOL bpSet = FALSE; - - while(pCur) - { - PendingBreakpoint *pNext = pCur->pNext; - if (pCur->ModuleMatches(mod)) - { - IssueDebuggerBPCommand(bpLocation); - bpSet = TRUE; - break; - } - - pCur = pNext; - } - - return bpSet; - } - - 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 deferred 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 IXCLRDataExceptionNotification5 -{ - 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) - || IsEqualIID(iid, IID_IXCLRDataExceptionNotification5)) - { - *ppvObject = static_cast<IXCLRDataExceptionNotification5*>(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) - { - m_dbgStatus = DEBUG_STATUS_GO_HANDLED; - return S_OK; - } - - STDMETHODIMP OnCodeGenerated2(IXCLRDataMethodInstance* method, CLRDATA_ADDRESS nativeCodeLocation) - { - // Some method has been generated, make a breakpoint. - 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); - - DacpGetModuleAddress dgma; - if (SUCCEEDED(dgma.Request(pMod))) - { - g_bpoints.UpdateKnownCodeAddress(TO_TADDR(dgma.ModulePtr), nativeCodeLocation); - } - 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(); - 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, doWorkItemDump = FALSE, dml = FALSE; - - CMDOption option[] = - { // name, vptr, type, hasValue - {"-ti", &doHCDump, COBOOL, FALSE}, - {"-wi", &doWorkItemDump, COBOOL, FALSE}, -#ifndef FEATURE_PAL - {"/d", &dml, COBOOL, FALSE}, -#endif - }; - - if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL)) - { - return Status; - } - - EnableDMLHolder dmlHolder(dml); - - 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 (doWorkItemDump && g_snapshot.Build()) - { - // Display a message if the heap isn't verified. - sos::GCHeap gcheap; - if (!gcheap.AreGCStructuresValid()) - { - DisplayInvalidStructuresMessage(); - } - - // Walk every heap item looking for the global queue and local queues. - ExtOut("\nQueued work items:\n%" POINTERSIZE "s %" POINTERSIZE "s %s\n", "Queue", "Address", "Work Item"); - HeapStat stats; - for (sos::ObjectIterator itr = gcheap.WalkHeap(); !IsInterrupt() && itr != NULL; ++itr) - { - if (_wcscmp(itr->GetTypeName(), W("System.Threading.ThreadPoolWorkQueue")) == 0) - { - // We found a global queue (there should be only one, given one AppDomain). - // Get its workItems ConcurrentQueue<IThreadPoolWorkItem>. - int offset = GetObjFieldOffset(itr->GetAddress(), itr->GetMT(), W("workItems")); - if (offset > 0) - { - DWORD_PTR workItemsConcurrentQueuePtr; - MOVE(workItemsConcurrentQueuePtr, itr->GetAddress() + offset); - if (sos::IsObject(workItemsConcurrentQueuePtr, false)) - { - // We got the ConcurrentQueue. Get its head segment. - sos::Object workItemsConcurrentQueue = TO_TADDR(workItemsConcurrentQueuePtr); - offset = GetObjFieldOffset(workItemsConcurrentQueue.GetAddress(), workItemsConcurrentQueue.GetMT(), W("_head")); - if (offset > 0) - { - // Now, walk from segment to segment, each of which contains an array of work items. - DWORD_PTR segmentPtr; - MOVE(segmentPtr, workItemsConcurrentQueue.GetAddress() + offset); - while (sos::IsObject(segmentPtr, false)) - { - sos::Object segment = TO_TADDR(segmentPtr); - - // Get the work items array. It's an array of Slot structs, which starts with the T. - offset = GetObjFieldOffset(segment.GetAddress(), segment.GetMT(), W("_slots")); - if (offset <= 0) - { - break; - } - - DWORD_PTR slotsPtr; - MOVE(slotsPtr, segment.GetAddress() + offset); - if (!sos::IsObject(slotsPtr, false)) - { - break; - } - - // Walk every element in the array, outputting details on non-null work items. - DacpObjectData slotsArray; - if (slotsArray.Request(g_sos, TO_CDADDR(slotsPtr)) == S_OK && slotsArray.ObjectType == OBJ_ARRAY) - { - for (int i = 0; i < slotsArray.dwNumComponents; i++) - { - CLRDATA_ADDRESS workItemPtr; - MOVE(workItemPtr, TO_CDADDR(slotsArray.ArrayDataPtr + (i * slotsArray.dwComponentSize))); // the item object reference is at the beginning of the Slot - if (workItemPtr != NULL && sos::IsObject(workItemPtr, false)) - { - sos::Object workItem = TO_TADDR(workItemPtr); - stats.Add((DWORD_PTR)workItem.GetMT(), (DWORD)workItem.GetSize()); - DMLOut("%" POINTERSIZE "s %s %S", "[Global]", DMLObject(workItem.GetAddress()), workItem.GetTypeName()); - if ((offset = GetObjFieldOffset(workItem.GetAddress(), workItem.GetMT(), W("_callback"))) > 0 || - (offset = GetObjFieldOffset(workItem.GetAddress(), workItem.GetMT(), W("m_action"))) > 0) - { - CLRDATA_ADDRESS delegatePtr; - MOVE(delegatePtr, workItem.GetAddress() + offset); - CLRDATA_ADDRESS md; - if (TryGetMethodDescriptorForDelegate(delegatePtr, &md)) - { - NameForMD_s((DWORD_PTR)md, g_mdName, mdNameLen); - ExtOut(" => %S", g_mdName); - } - } - ExtOut("\n"); - } - } - } - - // Move to the next segment. - DacpFieldDescData segmentField; - offset = GetObjFieldOffset(segment.GetAddress(), segment.GetMT(), W("_nextSegment"), TRUE, &segmentField); - if (offset <= 0) - { - break; - } - - MOVE(segmentPtr, segment.GetAddress() + offset); - if (segmentPtr == NULL) - { - break; - } - } - } - } - } - } - else if (_wcscmp(itr->GetTypeName(), W("System.Threading.ThreadPoolWorkQueue+WorkStealingQueue")) == 0) - { - // We found a local queue. Get its work items array. - int offset = GetObjFieldOffset(itr->GetAddress(), itr->GetMT(), W("m_array")); - if (offset > 0) - { - // Walk every element in the array, outputting details on non-null work items. - DWORD_PTR workItemArrayPtr; - MOVE(workItemArrayPtr, itr->GetAddress() + offset); - DacpObjectData workItemArray; - if (workItemArray.Request(g_sos, TO_CDADDR(workItemArrayPtr)) == S_OK && workItemArray.ObjectType == OBJ_ARRAY) - { - for (int i = 0; i < workItemArray.dwNumComponents; i++) - { - CLRDATA_ADDRESS workItemPtr; - MOVE(workItemPtr, TO_CDADDR(workItemArray.ArrayDataPtr + (i * workItemArray.dwComponentSize))); - if (workItemPtr != NULL && sos::IsObject(workItemPtr, false)) - { - sos::Object workItem = TO_TADDR(workItemPtr); - stats.Add((DWORD_PTR)workItem.GetMT(), (DWORD)workItem.GetSize()); - DMLOut("%s %s %S", DMLObject(itr->GetAddress()), DMLObject(workItem.GetAddress()), workItem.GetTypeName()); - if ((offset = GetObjFieldOffset(workItem.GetAddress(), workItem.GetMT(), W("_callback"))) > 0 || - (offset = GetObjFieldOffset(workItem.GetAddress(), workItem.GetMT(), W("m_action"))) > 0) - { - CLRDATA_ADDRESS delegatePtr; - MOVE(delegatePtr, workItem.GetAddress() + offset); - CLRDATA_ADDRESS md; - if (TryGetMethodDescriptorForDelegate(delegatePtr, &md)) - { - NameForMD_s((DWORD_PTR)md, g_mdName, mdNameLen); - ExtOut(" => %S", g_mdName); - } - } - ExtOut("\n"); - } - } - } - } - } - } - - // Output a summary. - stats.Sort(); - stats.Print(); - ExtOut("\n"); - } - - 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(SOSPrefix "gcroot %p, and if you find a root on a\n", p_Object); - ExtOut("stack, check the AppDomain of that stack with " SOSThreads ".\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; -} - -#ifndef FEATURE_PAL -DECLARE_API (DumpGCLog) -{ - INIT_API_NODAC(); - MINIDUMP_NOT_SUPPORTED(); - - if (GetEEFlavor() == UNKNOWNEE) - { - ExtOut("CLR not loaded\n"); - return Status; - } - - const char* fileName = "GCLog.txt"; - int iLogSize = 1024*1024; - BYTE* bGCLog = NULL; - int iRealLogSize = iLogSize - 1; - DWORD dwWritten = 0; - - 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; - } - - 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); - } - - while (iRealLogSize >= 0) - { - if (bGCLog[iRealLogSize] != '*') - { - break; - } - - iRealLogSize--; - } - - WriteFile (hGCLog, bGCLog, iRealLogSize + 1, &dwWritten, NULL); - - Status = S_OK; - -exit: - - if (bGCLog != NULL) - { - delete [] bGCLog; - } - - 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; -} - -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 fragmentation", - "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 < DAC_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 < DAC_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 < DAC_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 < DAC_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 < DAC_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 const 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 '" SOSPrefix "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: - GCHandleStatsForDomains() - : m_singleDomainMode(FALSE), m_numDomains(0), m_pStatistics(NULL), m_pDomainPointers(NULL), m_sharedDomainIndex(-1), m_systemDomainIndex(-1) - { - } - - ~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; - - LONG numSpecialDomains = (adsData.sharedDomain != NULL) ? 2 : 1; - m_numDomains = adsData.DomainCount + numSpecialDomains; - ArrayHolder<CLRDATA_ADDRESS> pArray = new NOTHROW CLRDATA_ADDRESS[m_numDomains]; - if (pArray == NULL) - return FALSE; - - int i = 0; - if (adsData.sharedDomain != NULL) - { - pArray[i++] = adsData.sharedDomain; - } - - pArray[i] = adsData.systemDomain; - - m_sharedDomainIndex = i - 1; // The m_sharedDomainIndex is set to -1 if there is no shared domain - m_systemDomainIndex = i; - - if (g_sos->GetAppDomainList(adsData.DomainCount, pArray+numSpecialDomains, NULL) != S_OK) - return FALSE; - - m_pDomainPointers = pArray.Detach(); - m_pStatistics = new NOTHROW GCHandleStatistics[m_numDomains]; - 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]; - } - - int GetSharedDomainIndex() - { - return m_sharedDomainIndex; - } - - int GetSystemDomainIndex() - { - return m_systemDomainIndex; - } - -private: - BOOL m_singleDomainMode; - int m_numDomains; - GCHandleStatistics *m_pStatistics; - CLRDATA_ADDRESS *m_pDomainPointers; - int m_sharedDomainIndex; - int m_systemDomainIndex; -}; - -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 == mHandleStat.GetSharedDomainIndex()) - Print(" (Shared Domain)\n"); - else if (i == mHandleStat.GetSystemDomainIndex()) - 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; -} - -// 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 && IsDerivedFrom(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("<"), 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(">"), 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(¤tTypeDef)); - - 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]; - 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, ¶mDef))) - pMD->GetParamProps(paramDef, NULL, NULL, paramName, mdNameLen, ¶mNameLen, 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(¤tTypeDef)); - - 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_) || defined(_ARM64_) - ExtOut("%-16s %-16s %s\n", "Child SP", "IP", "Call Site"); - #elif defined(_X86_) || defined(_ARM_) - 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 DEBUG_STACK_CONTEXT - PDEBUG_STACK_FRAME currentNativeFrame = NULL; - ULONG numNativeFrames = 0; - if (bFull) - { - hr = GetContextStackTrace(osID, &numNativeFrames); - if (FAILED(hr)) - { - ExtOut("Failed to get native stack frames: %lx\n", hr); - return; - } - currentNativeFrame = &g_Frames[0]; - } -#endif // DEBUG_STACK_CONTEXT - - 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 DEBUG_STACK_CONTEXT - while ((numNativeFrames > 0) && (currentNativeFrame->StackOffset <= sp)) - { - if (currentNativeFrame->StackOffset != sp) - { - PrintNativeStackFrame(out, currentNativeFrame, bSuppressLines); - } - currentNativeFrame++; - numNativeFrames--; - } -#endif // DEBUG_STACK_CONTEXT - - // 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 DEBUG_STACK_CONTEXT - while (numNativeFrames > 0) - { - PrintNativeStackFrame(out, currentNativeFrame, bSuppressLines); - currentNativeFrame++; - numNativeFrames--; - } -#endif // DEBUG_STACK_CONTEXT - } - - 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); - } - - static void PrintAllThreads(BOOL bParams, BOOL bLocals, BOOL bSuppressLines, BOOL bGC, BOOL bNative, BOOL bDisplayRegVals) - { - HRESULT Status; - - DacpThreadStoreData ThreadStore; - if ((Status = ThreadStore.Request(g_sos)) != S_OK) - { - ExtErr("Failed to request ThreadStore\n"); - return; - } - - DacpThreadData Thread; - CLRDATA_ADDRESS CurThread = ThreadStore.firstThread; - while (CurThread != 0) - { - if (IsInterrupt()) - break; - - if ((Status = Thread.Request(g_sos, CurThread)) != S_OK) - { - ExtErr("Failed to request thread at %p\n", CurThread); - return; - } - ExtOut("OS Thread Id: 0x%x\n", Thread.osThreadId); - PrintThread(Thread.osThreadId, bParams, bLocals, bSuppressLines, bGC, bNative, bDisplayRegVals); - CurThread = Thread.nextThread; - } - } - -private: - static HRESULT CreateStackWalk(ULONG osID, IXCLRDataStackWalk **ppStackwalk) - { - HRESULT hr = S_OK; - ToRelease<IXCLRDataTask> pTask; - - if ((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 " SOSThreads " 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; - BOOL bAllThreads = 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}, - {"-all", &bAllThreads, 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); - } - - if (bAllThreads) { - ClrStackImpl::PrintAllThreads(bParams, bLocals, bSuppressLines, bGC, bFull, bDisplayRegVals); - } - else { - 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), §ion, 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(ThreadId, &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_FrameContexts[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_FrameContexts[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(cdaObj, 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(¤tFlags))) - { - 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; -} - -#ifdef FEATURE_PAL - -#ifdef CREATE_DUMP_SUPPORTED -#include <dumpcommon.h> -#include "datatarget.h" -extern bool CreateDumpForSOS(const char* programPath, const char* dumpPathTemplate, pid_t pid, MINIDUMP_TYPE minidumpType, ICLRDataTarget* dataTarget); -extern bool g_diagnostics; -#endif // CREATE_DUMP_SUPPORTED - -DECLARE_API(CreateDump) -{ - INIT_API(); -#ifdef CREATE_DUMP_SUPPORTED - StringHolder sFileName; - BOOL normal = FALSE; - BOOL withHeap = FALSE; - BOOL triage = FALSE; - BOOL full = FALSE; - BOOL diag = FALSE; - - size_t nArg = 0; - CMDOption option[] = - { // name, vptr, type, hasValue - {"-n", &normal, COBOOL, FALSE}, - {"-h", &withHeap, COBOOL, FALSE}, - {"-t", &triage, COBOOL, FALSE}, - {"-f", &full, COBOOL, FALSE}, - {"-d", &diag, COBOOL, FALSE}, - }; - CMDValue arg[] = - { // vptr, type - {&sFileName.data, COSTRING} - }; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return E_FAIL; - } - MINIDUMP_TYPE minidumpType = MiniDumpWithPrivateReadWriteMemory; - ULONG pid = 0; - g_ExtSystem->GetCurrentProcessId(&pid); - - if (full) - { - minidumpType = MiniDumpWithFullMemory; - } - else if (withHeap) - { - minidumpType = MiniDumpWithPrivateReadWriteMemory; - } - else if (triage) - { - minidumpType = MiniDumpFilterTriage; - } - else if (normal) - { - minidumpType = MiniDumpNormal; - } - g_diagnostics = diag; - - const char* programPath = g_ExtServices->GetCoreClrDirectory(); - const char* dumpPathTemplate = "/tmp/coredump.%d"; - ToRelease<ICLRDataTarget> dataTarget = new DataTarget(); - dataTarget->AddRef(); - - if (sFileName.data != nullptr) - { - dumpPathTemplate = sFileName.data; - } - if (!CreateDumpForSOS(programPath, dumpPathTemplate, pid, minidumpType, dataTarget)) - { - Status = E_FAIL; - } -#else // CREATE_DUMP_SUPPORTED - ExtErr("CreateDump not supported on this platform\n"); -#endif // CREATE_DUMP_SUPPORTED - return Status; -} - -#endif // FEATURE_PAL - -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; -} - -#if defined(FEATURE_PAL) && defined(_TARGET_AMD64_) - -static BOOL -ReadMemoryAdapter(PVOID address, PVOID buffer, SIZE_T size) -{ - ULONG fetched; - HRESULT hr = g_ExtData->ReadVirtual(TO_CDADDR(address), buffer, size, &fetched); - return SUCCEEDED(hr); -} - -static BOOL -GetStackFrame(CONTEXT* context, ULONG numNativeFrames) -{ - KNONVOLATILE_CONTEXT_POINTERS contextPointers; - memset(&contextPointers, 0, sizeof(contextPointers)); - - ULONG64 baseAddress; - HRESULT hr = g_ExtSymbols->GetModuleByOffset(context->Rip, 0, NULL, &baseAddress); - if (FAILED(hr)) - { - PDEBUG_STACK_FRAME frame = &g_Frames[0]; - for (unsigned int i = 0; i < numNativeFrames; i++, frame++) { - if (frame->InstructionOffset == context->Rip) - { - if ((i + 1) >= numNativeFrames) { - return FALSE; - } - memcpy(context, &(g_FrameContexts[i + 1]), sizeof(*context)); - return TRUE; - } - } - return FALSE; - } - if (!PAL_VirtualUnwindOutOfProc(context, &contextPointers, baseAddress, ReadMemoryAdapter)) - { - return FALSE; - } - return TRUE; -} - -static BOOL -UnwindStackFrames(ULONG32 osThreadId) -{ - ULONG numNativeFrames = 0; - HRESULT hr = GetContextStackTrace(osThreadId, &numNativeFrames); - if (FAILED(hr)) - { - return FALSE; - } - CONTEXT context; - memset(&context, 0, sizeof(context)); - context.ContextFlags = CONTEXT_FULL; - - hr = g_ExtSystem->GetThreadContextById(osThreadId, CONTEXT_FULL, sizeof(context), (PBYTE)&context); - if (FAILED(hr)) - { - return FALSE; - } - TableOutput out(3, POINTERSIZE_HEX, AlignRight); - out.WriteRow("RSP", "RIP", "Call Site"); - - DEBUG_STACK_FRAME nativeFrame; - memset(&nativeFrame, 0, sizeof(nativeFrame)); - - do - { - if (context.Rip == 0) - { - break; - } - nativeFrame.InstructionOffset = context.Rip; - nativeFrame.ReturnOffset = context.Rip; - nativeFrame.FrameOffset = context.Rbp; - nativeFrame.StackOffset = context.Rsp; - ClrStackImpl::PrintNativeStackFrame(out, &nativeFrame, FALSE); - - } while (GetStackFrame(&context, numNativeFrames)); - - return TRUE; -} - -#endif // FEATURE_PAL && _TARGET_AMD64_ diff --git a/src/coreclr/src/ToolBox/SOS/Strike/strike.h b/src/coreclr/src/ToolBox/SOS/Strike/strike.h deleted file mode 100644 index 3078d8b3c50..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/strike.h +++ /dev/null @@ -1,138 +0,0 @@ -// 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 _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 _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 - -#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/coreclr/src/ToolBox/SOS/Strike/util.cpp b/src/coreclr/src/ToolBox/SOS/Strike/util.cpp deleted file mode 100644 index 880c5656a46..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/util.cpp +++ /dev/null @@ -1,7042 +0,0 @@ -// 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> -#include <wctype.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; -const UINT kcMaxTieredVersions = 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 - { - *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) -{ - 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 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; - } - - ExtOut(" %x:", vThread.osThreadId); - DisplayDataMember(pFD, dwTmp, FALSE); - } - - // Go to next thread - CurThread = vThread.nextThread; - } - 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 every time. * -* * -\**********************************************************************/ -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; - } - - 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.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) - { - numStaticFields ++; - 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); - } - } - } - - } - else if (vFieldDesc.bIsStatic) - { - numStaticFields ++; - - 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*/, DacpFieldDescData* pDacpFieldDescData/*=NULL*/) -{ - -#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, pDacpFieldDescData); - 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) - { - if (pDacpFieldDescData != NULL) - { - *pDacpFieldDescData = vFieldDesc; - } - return offset; - } - numInstanceFields ++; - } - - dwAddr = vFieldDesc.NextField; - } - - // Field name not found... - return 0; - -#undef EXITPOINT -} - - -// Return value: -1 = error -// -2 = not found -// >= 0 = offset to field from cdaValue -int GetValueFieldOffset(CLRDATA_ADDRESS cdaMT, __in_z LPCWSTR wszFieldName, DacpFieldDescData* pDacpFieldDescData) -{ -#define EXITPOINT(EXPR) do { if(!(EXPR)) { return -1; } } while (0) - - const int NOT_FOUND = -2; - DacpMethodTableData dmtd; - DacpMethodTableFieldData vMethodTableFields; - DacpFieldDescData vFieldDesc; - DacpModuleData module; - static DWORD numInstanceFields = 0; // Static due to recursion visiting parents - numInstanceFields = 0; - - EXITPOINT(vMethodTableFields.Request(g_sos, cdaMT) == S_OK); - - EXITPOINT(dmtd.Request(g_sos, cdaMT) == S_OK); - EXITPOINT(module.Request(g_sos, dmtd.Module) == S_OK); - if (dmtd.ParentMethodTable) - { - DWORD retVal = GetValueFieldOffset(dmtd.ParentMethodTable, wszFieldName, pDacpFieldDescData); - if (retVal != (DWORD)NOT_FOUND) - { - // Return in case of error or success. Fall through for field-not-found. - return retVal; - } - } - - 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) - { - NameForToken_s(TokenFromRid(vFieldDesc.mb, mdtFieldDef), pImport, g_mdName, mdNameLen, false); - if (_wcscmp(wszFieldName, g_mdName) == 0) - { - if (pDacpFieldDescData != NULL) - { - *pDacpFieldDescData = vFieldDesc; - } - return vFieldDesc.dwOffset; - } - numInstanceFields++; - } - - dwAddr = vFieldDesc.NextField; - } - - // Field name not found... - return NOT_FOUND; - -#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 = 0; - 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; -} - -BOOL IsDerivedFrom(CLRDATA_ADDRESS mtObj, __in_z LPCWSTR baseString) -{ - 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; -} - -BOOL TryGetMethodDescriptorForDelegate(CLRDATA_ADDRESS delegateAddr, CLRDATA_ADDRESS* pMD) -{ - if (!sos::IsObject(delegateAddr, false)) - { - return FALSE; - } - - sos::Object delegateObj = TO_TADDR(delegateAddr); - - for (int i = 0; i < 2; i++) - { - int offset; - if ((offset = GetObjFieldOffset(delegateObj.GetAddress(), delegateObj.GetMT(), i == 0 ? W("_methodPtrAux") : W("_methodPtr"))) != 0) - { - CLRDATA_ADDRESS methodPtr; - MOVE(methodPtr, delegateObj.GetAddress() + offset); - if (methodPtr != NULL) - { - if (g_sos->GetMethodDescPtrFromIP(methodPtr, pMD) == S_OK) - { - return TRUE; - } - - DacpCodeHeaderData codeHeaderData; - if (codeHeaderData.Request(g_sos, methodPtr) == S_OK) - { - *pMD = codeHeaderData.MethodDescPtr; - return TRUE; - } - } - } - } - - 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, ®))) - 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(®s, &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; - int numSpecialDomains = (adsData.sharedDomain != NULL) ? 2 : 1; - if (!ClrSafeInt<int>::addition(adsData.DomainCount, numSpecialDomains, 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; - if (adsData.sharedDomain != NULL) - { - pArray[1] = adsData.sharedDomain; - } - if (g_sos->GetAppDomainList(adsData.DomainCount, pArray.GetPtr()+numSpecialDomains, 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+numSpecialDomains; 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, mdTypeDef* retMdTypeDef) -{ - DWORD_PTR ignoredModuleInfoRet = NULL; - if (retMdTypeDef) - *retMdTypeDef = 0; - - 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 && !retMdTypeDef) - ExtOut("-----------------------\n"); - - mdTypeDef token; - if (pMeth->GetTokenAndScope(&token, NULL) == S_OK) - { - GetInfoFromModule(ModulePtr, token, retMdTypeDef ? &ignoredModuleInfoRet : NULL); - 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))) - { - if (retMdTypeDef) - *retMdTypeDef = cl; - - GetInfoFromModule(ModulePtr, cl, retMdTypeDef ? &ignoredModuleInfoRet : NULL); - 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))) - { - if (retMdTypeDef) - *retMdTypeDef = cl; - - mdMethodDef token; - ULONG cTokens; - HCORENUM henum = NULL; - - // is Member? - henum = NULL; - if (SUCCEEDED (pImport->EnumMembersWithName (&henum, cl, pwzMethod, - &token, 1, &cTokens)) - && cTokens == 1) - { - if (!retMdTypeDef) ExtOut("Member (mdToken token) of\n"); - GetInfoFromModule(ModulePtr, cl, retMdTypeDef ? &ignoredModuleInfoRet : NULL); - return; - } - - // is Field? - henum = NULL; - if (SUCCEEDED (pImport->EnumFieldsWithName (&henum, cl, pwzMethod, - &token, 1, &cTokens)) - && cTokens == 1) - { - if (!retMdTypeDef) ExtOut("Field (mdToken token) of\n"); - GetInfoFromModule(ModulePtr, cl, retMdTypeDef ? &ignoredModuleInfoRet : NULL); - 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 DumpTieredNativeCodeAddressInfo(struct DacpTieredVersionData * pTieredVersionData, const UINT cTieredVersionData) -{ - ExtOut("Code Version History:\n"); - - for(int i = cTieredVersionData - 1; i >= 0; --i) - { - const char *descriptor = NULL; - switch(pTieredVersionData[i].OptimizationTier) - { - case DacpTieredVersionData::OptimizationTier_Unknown: - default: - _ASSERTE(!"Update SOS to understand the new tier"); - descriptor = "Unknown Tier"; - break; - case DacpTieredVersionData::OptimizationTier_MinOptJitted: - descriptor = "MinOptJitted"; - break; - case DacpTieredVersionData::OptimizationTier_Optimized: - descriptor = "Optimized"; - break; - case DacpTieredVersionData::OptimizationTier_QuickJitted: - descriptor = "QuickJitted"; - break; - case DacpTieredVersionData::OptimizationTier_OptimizedTier1: - descriptor = "OptimizedTier1"; - break; - case DacpTieredVersionData::OptimizationTier_ReadyToRun: - descriptor = "ReadyToRun"; - break; - } - - DMLOut(" CodeAddr: %s (%s)\n", DMLIP(pTieredVersionData[i].NativeCodeAddr), descriptor); - ExtOut(" NativeCodeVersion: %p\n", SOS_PTR(pTieredVersionData[i].NativeCodeVersionNodePtr)); - } -} - -void DumpRejitData(CLRDATA_ADDRESS pMethodDesc, DacpReJitData * pReJitData) -{ - ExtOut(" ReJITID %p: ", SOS_PTR(pReJitData->rejitID)); - - struct DacpTieredVersionData codeAddrs[kcMaxTieredVersions]; - int cCodeAddrs; - - ReleaseHolder<ISOSDacInterface5> sos5; - if (SUCCEEDED(g_sos->QueryInterface(__uuidof(ISOSDacInterface5), &sos5)) && - SUCCEEDED(sos5->GetTieredVersions(pMethodDesc, - (int)pReJitData->rejitID, - codeAddrs, - kcMaxTieredVersions, - &cCodeAddrs))) - { - DumpTieredNativeCodeAddressInfo(codeAddrs, cCodeAddrs); - } - - 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->MethodDescPtr, &pMethodDescData->rejitDataCurrent); - - // Dump reverted rejit infos - for (ULONG i=0; i < cRevertedRejitData; i++) - { - DumpRejitData(pMethodDescData->MethodDescPtr, &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->MethodDescPtr, &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("Current CodeAddr: %s\n", DMLIP(pMethodDescData->NativeCodeAddr)); - - struct DacpTieredVersionData codeAddrs[kcMaxTieredVersions]; - int cCodeAddrs; - - ReleaseHolder<ISOSDacInterface5> sos5; - if (SUCCEEDED(g_sos->QueryInterface(__uuidof(ISOSDacInterface5), &sos5)) && - SUCCEEDED(sos5->GetTieredVersions(pMethodDescData->MethodDescPtr, - (int)pMethodDescData->rejitDataCurrent.rejitID, - codeAddrs, - kcMaxTieredVersions, - &cCodeAddrs))) - { - DumpTieredNativeCodeAddressInfo(codeAddrs, cCodeAddrs); - } - - 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; - LONG NumExtraDomains = (adsData.sharedDomain != NULL) ? 2 : 1; - if (!ClrSafeInt<LONG>::addition(adsData.DomainCount, NumExtraDomains, DomainAllocCount) || - !ClrSafeInt<size_t>::multiply(DomainAllocCount, sizeof(PVOID), AllocSize) || - (domainList = new DWORD_PTR[DomainAllocCount]) == NULL) - { - return; - } - - domainList[numDomain++] = (DWORD_PTR) adsData.systemDomain; - if (adsData.sharedDomain != NULL) - { - 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 absence 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; -#else - GUID skuId = CLR_ID_CORECLR; -#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 - "<exec cmd=\"!DumpAsync -addr %s -tasks -completed -fields -stacks -roots\">%s</exec>", // DML_Async -}; - -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, - NATIVE_SYMBOL_READER_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 SOS was unable to locate a suitable version of DiaSymReader. The dll searched for was '%S'\n", NATIVE_SYMBOL_READER_DLL); - 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(TO_TADDR(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/coreclr/src/ToolBox/SOS/Strike/util.h b/src/coreclr/src/ToolBox/SOS/Strike/util.h deleted file mode 100644 index 55c822e1504..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/util.h +++ /dev/null @@ -1,3222 +0,0 @@ -// 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 - -#ifndef TARGET_POINTER_SIZE -#define TARGET_POINTER_SIZE POINTERSIZE_BYTES -#endif // TARGET_POINTER_SIZE - -#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 - -// The native symbol reader dll name -#if defined(_AMD64_) -#define NATIVE_SYMBOL_READER_DLL W("Microsoft.DiaSymReader.Native.amd64.dll") -#elif defined(_X86_) -#define NATIVE_SYMBOL_READER_DLL W("Microsoft.DiaSymReader.Native.x86.dll") -#elif defined(_ARM_) -#define NATIVE_SYMBOL_READER_DLL W("Microsoft.DiaSymReader.Native.arm.dll") -#elif defined(_ARM64_) -// Use diasymreader until the package has an arm64 version - issue #7360 -//#define NATIVE_SYMBOL_READER_DLL W("Microsoft.DiaSymReader.Native.arm64.dll") -#define NATIVE_SYMBOL_READER_DLL W("diasymreader.dll") -#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, - DML_Async, - }; - - /**********************************************************************\ - * 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() -#define DMLAsync(addr) Output::BuildHexValue(addr, Output::DML_Async).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, DacpFieldDescData* pDacpFieldDescData=NULL); -int GetValueFieldOffset(CLRDATA_ADDRESS cdaMT, __in_z LPCWSTR wszFieldName, DacpFieldDescData* pDacpFieldDescData); - -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; - -#include <arrayholder.h> - -// 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; - BOOL bCollectible; - DWORD_PTR* GCInfoBuffer; // Start of memory of GC info - CGCDesc* GCInfo; // Just past GC info (which is how it is stored) - bool ArrayOfVC; - TADDR LoaderAllocatorObjectHandle; -}; - -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.bCollectible = false; - info.GCInfo = NULL; - info.ArrayOfVC = false; - info.GCInfoBuffer = NULL; - info.LoaderAllocatorObjectHandle = 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_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); -BOOL IsDerivedFrom(CLRDATA_ADDRESS mtObj, __in_z LPCWSTR baseString); -BOOL TryGetMethodDescriptorForDelegate(CLRDATA_ADDRESS delegateAddr, CLRDATA_ADDRESS* pMD); - -/* 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, mdTypeDef* retMdTypeDef=NULL); -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); - -BOOL GetCollectibleDataEfficient(DWORD_PTR dwAddrMethTable, BOOL& bCollectible, TADDR& loaderAllocatorObjectHandle); - -// 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); - -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 -#else -#define itoa_s_ptr _i64toa_s -#define itow_s_ptr _i64tow_s -#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 PVOID (*LoadSymbolsForModuleDelegate)(const char*, BOOL, ULONG64, int, ULONG64, int, ReadMemoryDelegate); -typedef void (*DisposeDelegate)(PVOID); -typedef BOOL (*ResolveSequencePointDelegate)(PVOID, const char*, unsigned int, unsigned int*, unsigned int*); -typedef BOOL (*GetLocalVariableName)(PVOID, int, int, BSTR*); -typedef BOOL (*GetLineByILOffsetDelegate)(PVOID, mdMethodDef, ULONG64, ULONG *, BSTR*); - -class SymbolReader -{ -private: -#ifndef FEATURE_PAL - ISymUnmanagedReader* m_pSymReader; -#endif - PVOID 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 1 -typedef DECLSPEC_ALIGN(8) 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; - DWORD Padding; - 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]; - - DWORD Padding2[2]; - -} 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 PrintLoaderAllocator(size_t memberValue); - 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; - - TADDR LoaderAllocatorObjectHandle; - bool ArrayOfVC; - bool ContainsPointers; - bool Collectible; - 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), Collectible(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/coreclr/src/ToolBox/SOS/Strike/vm.cpp b/src/coreclr/src/ToolBox/SOS/Strike/vm.cpp deleted file mode 100644 index 7eb6e93cdeb..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/vm.cpp +++ /dev/null @@ -1,712 +0,0 @@ -// 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: - ---*/ - -#ifdef FEATURE_PAL -#error This file is Win32 only. -#endif // #ifdef 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; - PCSTR 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 PCSTR 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 PCSTR 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 - ) -{ - PCSTR 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 - ) -{ - PCSTR 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 ) diff --git a/src/coreclr/src/ToolBox/SOS/Strike/xplat/dbgeng.h b/src/coreclr/src/ToolBox/SOS/Strike/xplat/dbgeng.h deleted file mode 100644 index 5dcf9572bba..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/xplat/dbgeng.h +++ /dev/null @@ -1,485 +0,0 @@ -// 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 = _vsnprintf_s(str, sizeof(str), _TRUNCATE, 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/coreclr/src/ToolBox/SOS/Strike/xplat/dbghelp.h b/src/coreclr/src/ToolBox/SOS/Strike/xplat/dbghelp.h deleted file mode 100644 index 7086b335fd8..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/xplat/dbghelp.h +++ /dev/null @@ -1,17 +0,0 @@ -// 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/coreclr/src/ToolBox/SOS/Strike/xplat/wdbgexts.h b/src/coreclr/src/ToolBox/SOS/Strike/xplat/wdbgexts.h deleted file mode 100644 index d5a1a31c0a9..00000000000 --- a/src/coreclr/src/ToolBox/SOS/Strike/xplat/wdbgexts.h +++ /dev/null @@ -1,28 +0,0 @@ -// 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_ diff --git a/src/coreclr/src/ToolBox/SOS/lldbplugin/CMakeLists.txt b/src/coreclr/src/ToolBox/SOS/lldbplugin/CMakeLists.txt deleted file mode 100644 index 6a279edc8a1..00000000000 --- a/src/coreclr/src/ToolBox/SOS/lldbplugin/CMakeLists.txt +++ /dev/null @@ -1,150 +0,0 @@ -project(sosplugin) - -set(CMAKE_INCLUDE_CURRENT_DIR ON) - -# Set the RPATH of sos so that it can find dependencies without needing to set LD_LIBRARY_PATH -# For more information: http://www.cmake.org/Wiki/CMake_RPATH_handling. -if (CORECLR_SET_RPATH) - set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) - if(CLR_CMAKE_PLATFORM_DARWIN) - set(CMAKE_INSTALL_RPATH "@loader_path") - else() - set(CMAKE_INSTALL_RPATH "\$ORIGIN") - endif(CLR_CMAKE_PLATFORM_DARWIN) -endif (CORECLR_SET_RPATH) - -add_definitions(-DPAL_STDCPP_COMPAT) - -if (CLR_CMAKE_PLATFORM_LINUX OR CLR_CMAKE_PLATFORM_FREEBSD) - set(PLATFORM_SUPPORTS_LLDB_PLUGIN true) -endif() - -set(ENABLE_LLDBPLUGIN ${PLATFORM_SUPPORTS_LLDB_PLUGIN} CACHE BOOL "Enable building the SOS plugin for LLDB.") -set(REQUIRE_LLDBPLUGIN ${PLATFORM_SUPPORTS_LLDB_PLUGIN} CACHE BOOL "Require building the SOS plugin for LLDB.") - -if(SKIP_LLDBPLUGIN) - SET(REQUIRE_LLDBPLUGIN false) -endif() - -if(CLR_CMAKE_PLATFORM_ARCH_AMD64) - add_definitions(-D_TARGET_AMD64_) - add_definitions(-DDBG_TARGET_64BIT) - add_definitions(-DDBG_TARGET_AMD64) - add_definitions(-DDBG_TARGET_WIN64) - add_definitions(-DBIT64) -elseif(CLR_CMAKE_PLATFORM_ARCH_I386) - add_definitions(-D_TARGET_X86_) - add_definitions(-DDBG_TARGET_32BIT) - add_definitions(-DDBG_TARGET_X86) -elseif(CLR_CMAKE_PLATFORM_ARCH_ARM) - add_definitions(-D_TARGET_ARM_) - add_definitions(-DDBG_TARGET_32BIT) - add_definitions(-DDBG_TARGET_ARM) -elseif(CLR_CMAKE_PLATFORM_ARCH_ARM64) - add_definitions(-D_TARGET_ARM64_) - add_definitions(-DDBG_TARGET_64BIT) - add_definitions(-DDBG_TARGET_ARM64) - add_definitions(-DDBG_TARGET_WIN64) - add_definitions(-DBIT64) - SET(REQUIRE_LLDBPLUGIN false) -endif() - -if(NOT $ENV{LLVM_HOME} STREQUAL "") - set(LLDB_INCLUDE_DIR "$ENV{LLVM_HOME}/include") - set(LLDB_LIB_DIR "$ENV{LLVM_HOME}/lib") -else() - if(NOT $ENV{LLDB_INCLUDE_DIR} STREQUAL "") - set(LLDB_INCLUDE_DIR "$ENV{LLDB_INCLUDE_DIR}") - endif() - if(NOT $ENV{LLDB_LIB_DIR} STREQUAL "") - set(LLDB_LIB_DIR "$ENV{LLDB_LIB_DIR}") - endif() -endif() - -if(NOT ENABLE_LLDBPLUGIN) - return() -endif() - -if(NOT $ENV{LLDB_LIB} STREQUAL "") - set(LLDB_LIB "$ENV{LLDB_LIB}") -else() - # Check for LLDB library - if(CLR_CMAKE_PLATFORM_DARWIN) - find_library(LLDB_LIB NAMES LLDB lldb lldb-7 lldb-6.0 lldb-5.0 lldb-4.0 lldb-3.9 lldb-3.8 lldb-3.7 lldb-3.6 lldb-3.5 PATHS "${LLDB_LIB_DIR}" PATH_SUFFIXES llvm NO_DEFAULT_PATH) - find_library(LLDB_LIB NAMES LLDB lldb lldb-7 lldb-6.0 lldb-5.0 lldb-4.0 lldb-3.9 lldb-3.8 lldb-3.7 lldb-3.6 lldb-3.5 PATH_SUFFIXES llvm) - if(LLDB_LIB STREQUAL LLDB_LIB-NOTFOUND) - if(REQUIRE_LLDBPLUGIN) - set(MESSAGE_MODE FATAL_ERROR) - else() - set(MESSAGE_MODE WARNING) - endif() - message(${MESSAGE_MODE} "Cannot find lldb library. Try installing Xcode. You may need to set LLVM_HOME, LLDB_LIB_DIR or LLDB_LIB if the build still can't find it.") - return() - endif() - endif() -endif() - -message(STATUS "LLDB_LIB: ${LLDB_LIB}") - -if(NOT $ENV{LLDB_H} STREQUAL "") - set(LLDB_H "$ENV{LLDB_H}") -else() - # Check for LLDB headers - # Multiple versions of LLDB can install side-by-side, so we need to check for lldb in various locations. - # If the file in a directory is found the result is stored in the variable and the search will not be repeated unless the variable is cleared. - find_path(LLDB_H "lldb/API/LLDB.h" PATHS "${LLDB_INCLUDE_DIR}" NO_DEFAULT_PATH) - find_path(LLDB_H "lldb/API/LLDB.h") - find_path(LLDB_H "lldb/API/LLDB.h" PATHS "/usr/lib/llvm-7/include") - find_path(LLDB_H "lldb/API/LLDB.h" PATHS "/usr/lib/llvm-6.0/include") - find_path(LLDB_H "lldb/API/LLDB.h" PATHS "/usr/lib/llvm-5.0/include") - find_path(LLDB_H "lldb/API/LLDB.h" PATHS "/usr/lib/llvm-4.0/include") - find_path(LLDB_H "lldb/API/LLDB.h" PATHS "/usr/lib/llvm-3.9/include") - find_path(LLDB_H "lldb/API/LLDB.h" PATHS "/usr/lib/llvm-3.8/include") - find_path(LLDB_H "lldb/API/LLDB.h" PATHS "/usr/lib/llvm-3.7/include") - find_path(LLDB_H "lldb/API/LLDB.h" PATHS "/usr/lib/llvm-3.6/include") - find_path(LLDB_H "lldb/API/LLDB.h" PATHS "/usr/lib/llvm-3.5/include") - #FreeBSD - find_path(LLDB_H "lldb/API/LLDB.h" PATHS "/usr/local/llvm70/include") - find_path(LLDB_H "lldb/API/LLDB.h" PATHS "/usr/local/llvm60/include") - find_path(LLDB_H "lldb/API/LLDB.h" PATHS "/usr/local/llvm50/include") - find_path(LLDB_H "lldb/API/LLDB.h" PATHS "/usr/local/llvm40/include") - find_path(LLDB_H "lldb/API/LLDB.h" PATHS "/usr/local/llvm39/include") - find_path(LLDB_H "lldb/API/LLDB.h" PATHS "/usr/local/llvm38/include") - - if(LLDB_H STREQUAL LLDB_H-NOTFOUND) - if(REQUIRE_LLDBPLUGIN) - set(MESSAGE_MODE FATAL_ERROR) - else() - set(MESSAGE_MODE WARNING) - endif() - message(${MESSAGE_MODE} "Cannot find LLDB.h Try installing lldb-3.9-dev (or the appropriate package for your platform). You may need to set LLVM_HOME or LLDB_INCLUDE_DIR if the build still can't find it.") - return() - endif() -endif() - -message(STATUS "LLDB_H: ${LLDB_H}") - -include_directories(inc) -include_directories("${LLDB_H}") -include_directories(${CLR_DIR}/src/debug/inc) -include_directories(${CLR_DIR}/src/inc) -include_directories(${CLR_DIR}/src/pal/inc) -include_directories(${CLR_DIR}/src/pal/inc/rt) - -set(SOURCES - sosplugin.cpp - soscommand.cpp - setclrpathcommand.cpp - setsostidcommand.cpp - services.cpp -) - -_add_library(sosplugin SHARED ${SOURCES}) -add_dependencies(sosplugin sos) - -if (CLR_CMAKE_PLATFORM_DARWIN) - target_link_libraries(sosplugin ${LLDB_LIB}) -endif() - -# add the install targets -install_clr(sosplugin) diff --git a/src/coreclr/src/ToolBox/SOS/lldbplugin/inc/lldbservices.h b/src/coreclr/src/ToolBox/SOS/lldbplugin/inc/lldbservices.h deleted file mode 100644 index 7bb9b1aca39..00000000000 --- a/src/coreclr/src/ToolBox/SOS/lldbplugin/inc/lldbservices.h +++ /dev/null @@ -1,550 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -//---------------------------------------------------------------------------- -// -// LLDB debugger services for sos -// -//---------------------------------------------------------------------------- - -#ifndef __LLDBSERVICES_H__ -#define __LLDBSERVICES_H__ - -#include <stdarg.h> -#include <palrt.h> -#include <unknwn.h> - -#ifdef __cplusplus -extern "C" { -#endif - -// Output mask bits. -// Normal output. -#define DEBUG_OUTPUT_NORMAL 0x00000001 -// Error output. -#define DEBUG_OUTPUT_ERROR 0x00000002 -// Warnings. -#define DEBUG_OUTPUT_WARNING 0x00000004 -// Additional output. -#define DEBUG_OUTPUT_VERBOSE 0x00000008 -// Prompt output. -#define DEBUG_OUTPUT_PROMPT 0x00000010 -// Register dump before prompt. -#define DEBUG_OUTPUT_PROMPT_REGISTERS 0x00000020 -// Warnings specific to extension operation. -#define DEBUG_OUTPUT_EXTENSION_WARNING 0x00000040 -// Debuggee debug output, such as from OutputDebugString. -#define DEBUG_OUTPUT_DEBUGGEE 0x00000080 -// Debuggee-generated prompt, such as from DbgPrompt. -#define DEBUG_OUTPUT_DEBUGGEE_PROMPT 0x00000100 -// Symbol messages, such as for !sym noisy. -#define DEBUG_OUTPUT_SYMBOLS 0x00000200 - -// Execute and ExecuteCommandFile flags. -// These flags only apply to the command -// text itself; output from the executed -// command is controlled by the output -// control parameter. -// Default execution. Command is logged -// but not output. -#define DEBUG_EXECUTE_DEFAULT 0x00000000 -// Echo commands during execution. In -// ExecuteCommandFile also echoes the prompt -// for each line of the file. -#define DEBUG_EXECUTE_ECHO 0x00000001 -// Do not log or output commands during execution. -// Overridden by DEBUG_EXECUTE_ECHO. -#define DEBUG_EXECUTE_NOT_LOGGED 0x00000002 -// If this flag is not set an empty string -// to Execute will repeat the last Execute -// string. -#define DEBUG_EXECUTE_NO_REPEAT 0x00000004 - -// Classes of debuggee. Each class -// has different qualifiers for specific -// kinds of debuggees. -#define DEBUG_CLASS_UNINITIALIZED 0 -#define DEBUG_CLASS_KERNEL 1 -#define DEBUG_CLASS_USER_WINDOWS 2 -#define DEBUG_CLASS_IMAGE_FILE 3 - -// Generic dump types. These can be used -// with either user or kernel sessions. -// Session-type-specific aliases are also -// provided. -#define DEBUG_DUMP_SMALL 1024 -#define DEBUG_DUMP_DEFAULT 1025 -#define DEBUG_DUMP_FULL 1026 -#define DEBUG_DUMP_IMAGE_FILE 1027 -#define DEBUG_DUMP_TRACE_LOG 1028 -#define DEBUG_DUMP_WINDOWS_CE 1029 - -#define IMAGE_FILE_MACHINE_I386 0x014c // Intel 386. -#define IMAGE_FILE_MACHINE_ARMNT 0x01c4 // ARM Thumb-2 Little-Endian -#define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8) -#define IMAGE_FILE_MACHINE_ARM64 0xAA64 // ARM64 Little-Endian - -// Execution status codes used for waiting, -// for returning current status and for -// event method return values. -#define DEBUG_STATUS_NO_CHANGE 0 -#define DEBUG_STATUS_GO 1 -#define DEBUG_STATUS_GO_HANDLED 2 -#define DEBUG_STATUS_GO_NOT_HANDLED 3 -#define DEBUG_STATUS_STEP_OVER 4 -#define DEBUG_STATUS_STEP_INTO 5 -#define DEBUG_STATUS_BREAK 6 -#define DEBUG_STATUS_NO_DEBUGGEE 7 -#define DEBUG_STATUS_STEP_BRANCH 8 -#define DEBUG_STATUS_IGNORE_EVENT 9 -#define DEBUG_STATUS_RESTART_REQUESTED 10 -#define DEBUG_STATUS_REVERSE_GO 11 -#define DEBUG_STATUS_REVERSE_STEP_BRANCH 12 -#define DEBUG_STATUS_REVERSE_STEP_OVER 13 -#define DEBUG_STATUS_REVERSE_STEP_INTO 14 -#define DEBUG_STATUS_OUT_OF_SYNC 15 -#define DEBUG_STATUS_WAIT_INPUT 16 -#define DEBUG_STATUS_TIMEOUT 17 - -#define DEBUG_STATUS_MASK 0x1f - -#define DEBUG_EVENT_EXCEPTION 0x00000002 - -typedef struct _DEBUG_LAST_EVENT_INFO_EXCEPTION -{ - EXCEPTION_RECORD64 ExceptionRecord; - ULONG FirstChance; -} DEBUG_LAST_EVENT_INFO_EXCEPTION, *PDEBUG_LAST_EVENT_INFO_EXCEPTION; - -// -// Information about a module. -// - -// Flags. -#define DEBUG_MODULE_LOADED 0x00000000 -#define DEBUG_MODULE_UNLOADED 0x00000001 -#define DEBUG_MODULE_USER_MODE 0x00000002 -#define DEBUG_MODULE_EXPLICIT 0x00000008 -#define DEBUG_MODULE_SECONDARY 0x00000010 -#define DEBUG_MODULE_SYNTHETIC 0x00000020 -#define DEBUG_MODULE_SYM_BAD_CHECKSUM 0x00010000 - -// Symbol types. -#define DEBUG_SYMTYPE_NONE 0 -#define DEBUG_SYMTYPE_COFF 1 -#define DEBUG_SYMTYPE_CODEVIEW 2 -#define DEBUG_SYMTYPE_PDB 3 -#define DEBUG_SYMTYPE_EXPORT 4 -#define DEBUG_SYMTYPE_DEFERRED 5 -#define DEBUG_SYMTYPE_SYM 6 -#define DEBUG_SYMTYPE_DIA 7 - -typedef struct _DEBUG_MODULE_PARAMETERS -{ - ULONG64 Base; - ULONG Size; - ULONG TimeDateStamp; - ULONG Checksum; - ULONG Flags; - ULONG SymbolType; - ULONG ImageNameSize; - ULONG ModuleNameSize; - ULONG LoadedImageNameSize; - ULONG SymbolFileNameSize; - ULONG MappedImageNameSize; - ULONG64 Reserved[2]; -} DEBUG_MODULE_PARAMETERS, *PDEBUG_MODULE_PARAMETERS; - -// FindSourceFile flags. -#define DEBUG_FIND_SOURCE_DEFAULT 0x00000000 -// Returns fully-qualified paths only. If this -// is not set the path returned may be relative. -#define DEBUG_FIND_SOURCE_FULL_PATH 0x00000001 -// Scans all the path elements for a match and -// returns the one that has the most similarity -// between the given file and the matching element. -#define DEBUG_FIND_SOURCE_BEST_MATCH 0x00000002 -// Do not search source server paths. -#define DEBUG_FIND_SOURCE_NO_SRCSRV 0x00000004 -// Restrict FindSourceFileAndToken to token lookup only. -#define DEBUG_FIND_SOURCE_TOKEN_LOOKUP 0x00000008 - -// A special value marking an offset that should not -// be treated as a valid offset. This is only used -// in special situations where it is unlikely that -// this value would be a valid offset. -#define DEBUG_INVALID_OFFSET ((ULONG64)-1) - -// General unspecified ID constant. -#define DEBUG_ANY_ID 0xffffffff - -typedef struct _DEBUG_STACK_FRAME -{ - ULONG64 InstructionOffset; - ULONG64 ReturnOffset; - ULONG64 FrameOffset; - ULONG64 StackOffset; - ULONG64 FuncTableEntry; - ULONG64 Params[4]; - ULONG64 Reserved[6]; - BOOL Virtual; - ULONG FrameNumber; -} DEBUG_STACK_FRAME, *PDEBUG_STACK_FRAME; - -#define DBG_FRAME_DEFAULT 0 // the same as INLINE_FRAME_CONTEXT_INIT in dbghelp.h -#define DBG_FRAME_IGNORE_INLINE 0xFFFFFFFF // the same as INLINE_FRAME_CONTEXT_IGNORE in dbghelp.h - -typedef struct _DEBUG_STACK_FRAME_EX -{ - // First DEBUG_STACK_FRAME structure - ULONG64 InstructionOffset; - ULONG64 ReturnOffset; - ULONG64 FrameOffset; - ULONG64 StackOffset; - ULONG64 FuncTableEntry; - ULONG64 Params[4]; - ULONG64 Reserved[6]; - BOOL Virtual; - ULONG FrameNumber; - - // Extended DEBUG_STACK_FRAME fields. - ULONG InlineFrameContext; - ULONG Reserved1; // For alignment purpose. -} DEBUG_STACK_FRAME_EX, *PDEBUG_STACK_FRAME_EX; - -// The types of inline frame context. -#define STACK_FRAME_TYPE_INIT 0x00 -#define STACK_FRAME_TYPE_STACK 0x01 -#define STACK_FRAME_TYPE_INLINE 0x02 -#define STACK_FRAME_TYPE_RA 0x80 // Whether the instruction pointer is the current IP or a RA from callee frame. -#define STACK_FRAME_TYPE_IGNORE 0xFF - -// -// options that are set/returned by SymSetOptions() & SymGetOptions() -// these are used as a mask -// -#define SYMOPT_LOAD_LINES 0x00000010 - -interface ILLDBServices; -typedef HRESULT (*PFN_EXCEPTION_CALLBACK)(ILLDBServices *services); - -//---------------------------------------------------------------------------- -// ILLDBServices -//---------------------------------------------------------------------------- - -MIDL_INTERFACE("2E6C569A-9E14-4DA4-9DFC-CDB73A532566") -ILLDBServices : public IUnknown -{ -public: - - //---------------------------------------------------------------------------- - // ILLDBServices - //---------------------------------------------------------------------------- - - // Returns the coreclr module directory found by lldb plugin - // in the target process. - virtual PCSTR GetCoreClrDirectory() = 0; - - // Evaluates a lldb expression into a value. - virtual DWORD_PTR GetExpression( - /* [in] */ PCSTR exp) = 0; - - // Unwind one native stack frame given a thread and register context - virtual HRESULT VirtualUnwind( - /* [in] */ DWORD threadID, - /* [in] */ ULONG32 contextSize, - /* [in, out, size_is(contextSize)] */ PBYTE context) = 0; - - // Set an exception throw callback - virtual HRESULT SetExceptionCallback( - /* [in] */ PFN_EXCEPTION_CALLBACK callback) = 0; - - // Clear the exception throw callback - virtual HRESULT ClearExceptionCallback() = 0; - - //------------------------------------------------ - // IDebugControl2 - //------------------------------------------------ - - // Checks for a user interrupt, such a Ctrl-C - // or stop button. - // This method is reentrant. - virtual HRESULT GetInterrupt( - void) = 0; - - virtual HRESULT OutputVaList( - ULONG mask, - PCSTR format, - va_list args) = 0; - - // Returns information about the debuggee such - // as user vs. kernel, dump vs. live, etc. - virtual HRESULT GetDebuggeeType( - PULONG debugClass, - PULONG qualifier) = 0; - - // Returns the page size for the currently executing - // processor context. The page size may vary between - // processor types. - virtual HRESULT GetPageSize( - PULONG size) = 0; - - // Returns the type of processor used in the - // current processor context. - virtual HRESULT GetExecutingProcessorType( - PULONG type) = 0; - - // Executes the given command string. - // If the string has multiple commands - // Execute will not return until all - // of them have been executed. If this - // requires waiting for the debuggee to - // execute an internal wait will be done - // so Execute can take an arbitrary amount - // of time. - virtual HRESULT Execute( - ULONG outputControl, - PCSTR command, - ULONG flags) = 0; - - // Retrieves information about the last event that occurred. - // EventType is one of the event callback mask bits. - // ExtraInformation contains additional event-specific - // information. Not all events have additional information. - virtual HRESULT GetLastEventInformation( - PULONG type, - PULONG processId, - PULONG threadId, - PVOID extraInformation, - ULONG extraInformationSize, - PULONG extraInformationUsed, - PSTR description, - ULONG descriptionSize, - PULONG descriptionUsed) = 0; - - virtual HRESULT Disassemble( - ULONG64 offset, - ULONG flags, - PSTR buffer, - ULONG bufferSize, - PULONG disassemblySize, - PULONG64 endOffset) = 0; - - //---------------------------------------------------------------------------- - // IDebugControl4 - //---------------------------------------------------------------------------- - - // Stack tracing with a full initial context - // and full context return for each frame. - // The FrameContextsSize parameter is the total - // byte size of FrameContexts. FrameContextsEntrySize - // gives the byte size of each entry in - // FrameContexts. - virtual HRESULT GetContextStackTrace( - PVOID startContext, - ULONG startContextSize, - PDEBUG_STACK_FRAME frames, - ULONG framesSize, - PVOID frameContexts, - ULONG frameContextsSize, - ULONG frameContextsEntrySize, - PULONG framesFilled) = 0; - - //------------------------------------------------ - // IDebugDataSpaces - //------------------------------------------------ - - virtual HRESULT ReadVirtual( - ULONG64 offset, - PVOID buffer, - ULONG bufferSize, - PULONG bytesRead) = 0; - - virtual HRESULT WriteVirtual( - ULONG64 offset, - PVOID buffer, - ULONG bufferSize, - PULONG bytesWritten) = 0; - - //------------------------------------------------ - // IDebugSymbols - //------------------------------------------------ - - // Controls the symbol options used during - // symbol operations. - // Uses the same flags as dbghelps SymSetOptions. - virtual HRESULT GetSymbolOptions( - PULONG options) = 0; - - virtual HRESULT GetNameByOffset( - ULONG64 offset, - PSTR nameBuffer, - ULONG nameBufferSize, - PULONG nameSize, - PULONG64 displacement) = 0; - - // Enumerates the engines list of modules - // loaded for the current process. This may - // or may not match the system module list - // for the process. Reload can be used to - // synchronize the engines list with the system - // if necessary. - // Some sessions also track recently unloaded - // code modules for help in analyzing failures - // where an attempt is made to call unloaded code. - // These modules are indexed after the loaded - // modules. - virtual HRESULT GetNumberModules( - PULONG loaded, - PULONG unloaded) = 0; - - virtual HRESULT GetModuleByIndex( - ULONG index, - PULONG64 base) = 0; - - // The module name may not be unique. - // This method returns the first match. - virtual HRESULT GetModuleByModuleName( - PCSTR name, - ULONG startIndex, - PULONG index, - PULONG64 base) = 0; - - // Offset can be any offset within - // the module extent. Extents may - // not be unique when including unloaded - // drivers. This method returns the - // first match. - virtual HRESULT GetModuleByOffset( - ULONG64 offset, - ULONG startIndex, - PULONG index, - PULONG64 base) = 0; - - // If Index is DEBUG_ANY_ID the base address - // is used to look up the module instead. - virtual HRESULT GetModuleNames( - ULONG index, - ULONG64 base, - PSTR imageNameBuffer, - ULONG imageNameBufferSize, - PULONG imageNameSize, - PSTR moduleNameBuffer, - ULONG moduleNameBufferSize, - PULONG moduleNameSize, - PSTR loadedImageNameBuffer, - ULONG loadedImageNameBufferSize, - PULONG loadedImageNameSize) = 0; - - HRESULT virtual GetLineByOffset( - ULONG64 offset, - PULONG line, - PSTR fileBuffer, - ULONG fileBufferSize, - PULONG fileSize, - PULONG64 displacement) = 0; - - HRESULT virtual GetSourceFileLineOffsets( - PCSTR file, - PULONG64 buffer, - ULONG bufferLines, - PULONG fileLines) = 0; - - // Uses the given file path and the source path - // information to try and locate an existing file. - // The given file path is merged with elements - // of the source path and checked for existence. - // If a match is found the element used is returned. - // A starting element can be specified to restrict - // the search to a subset of the path elements; - // this can be useful when checking for multiple - // matches along the source path. - // The returned element can be 1, indicating - // the file was found directly and not on the path. - HRESULT virtual FindSourceFile( - ULONG startElement, - PCSTR file, - ULONG flags, - PULONG foundElement, - PSTR buffer, - ULONG bufferSize, - PULONG foundSize) = 0; - - //------------------------------------------------ - // IDebugSystemObjects - //------------------------------------------------ - - virtual HRESULT GetCurrentProcessId( - PULONG id) = 0; - - // Controls implicit thread used by the - // debug engine. The debuggers current - // thread is just a piece of data held - // by the debugger for calls which use - // thread-specific information. In those - // calls the debuggers current thread is used. - // The debuggers current thread is not related - // to any system thread attribute. - // IDs for threads are small integer IDs - // maintained by the engine. They are not - // related to system thread IDs. - virtual HRESULT GetCurrentThreadId( - PULONG id) = 0; - - virtual HRESULT SetCurrentThreadId( - ULONG id) = 0; - - // Returns the system unique ID for the current thread. - // Not currently supported when kernel debugging. - virtual HRESULT GetCurrentThreadSystemId( - PULONG sysId) = 0; - - // Looks up a debugger thread ID for the given - // system thread ID. - // Currently when kernel debugging this will fail - // if the thread is not executing on a processor. - virtual HRESULT GetThreadIdBySystemId( - ULONG sysId, - PULONG id) = 0; - - // This is a special sos/lldb function used to implement the ICLRDataTarget interface and - // not actually part of dbgeng's IDebugSystemObjects interface. - virtual HRESULT GetThreadContextById( - /* [in] */ ULONG32 threadID, - /* [in] */ ULONG32 contextFlags, - /* [in] */ ULONG32 contextSize, - /* [out, size_is(contextSize)] */ PBYTE context) = 0; - - //------------------------------------------------ - // IDebugRegister - //------------------------------------------------ - - // This is the combination of dbgeng's GetIndexByName and GetValue and not - // actually part of the dbgeng's IDebugRegister interface. - virtual HRESULT GetValueByName( - PCSTR name, - PDWORD_PTR value) = 0; - - // Abstracted pieces of processor information. - // The mapping of these values to architectural - // registers is architecture-specific and their - // interpretation and existence may vary. They - // are intended to be directly compatible with - // calls which take this information, such as - // stack walking. - virtual HRESULT GetInstructionOffset( - PULONG64 offset) = 0; - - virtual HRESULT GetStackOffset( - PULONG64 offset) = 0; - - virtual HRESULT GetFrameOffset( - PULONG64 offset) = 0; -}; - -#ifdef __cplusplus -}; -#endif - -#endif // #ifndef __LLDBSERVICES_H__ diff --git a/src/coreclr/src/ToolBox/SOS/lldbplugin/mstypes.h b/src/coreclr/src/ToolBox/SOS/lldbplugin/mstypes.h deleted file mode 100644 index 9b6f261be37..00000000000 --- a/src/coreclr/src/ToolBox/SOS/lldbplugin/mstypes.h +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// Contains some definitions duplicated from pal.h, palrt.h, rpc.h, -// etc. because they have various conflicits with the linux standard -// runtime h files like wchar_t, memcpy, etc. - -#include <pal_mstypes.h> - -#define MAX_PATH 260 - -// Platform-specific library naming -// -#ifdef __APPLE__ -#define MAKEDLLNAME_W(name) u"lib" name u".dylib" -#define MAKEDLLNAME_A(name) "lib" name ".dylib" -#elif defined(_AIX) -#define MAKEDLLNAME_W(name) L"lib" name L".a" -#define MAKEDLLNAME_A(name) "lib" name ".a" -#elif defined(__hppa__) || defined(_IA64_) -#define MAKEDLLNAME_W(name) L"lib" name L".sl" -#define MAKEDLLNAME_A(name) "lib" name ".sl" -#else -#define MAKEDLLNAME_W(name) u"lib" name u".so" -#define MAKEDLLNAME_A(name) "lib" name ".so" -#endif diff --git a/src/coreclr/src/ToolBox/SOS/lldbplugin/services.cpp b/src/coreclr/src/ToolBox/SOS/lldbplugin/services.cpp deleted file mode 100644 index aeca0208425..00000000000 --- a/src/coreclr/src/ToolBox/SOS/lldbplugin/services.cpp +++ /dev/null @@ -1,1746 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#include <cstdarg> -#include <cstdlib> -#include "sosplugin.h" -#include <string.h> -#include <string> - -#define CONVERT_FROM_SIGN_EXTENDED(offset) ((ULONG_PTR)(offset)) - -ULONG g_currentThreadIndex = (ULONG)-1; -ULONG g_currentThreadSystemId = (ULONG)-1; -char *g_coreclrDirectory; - -LLDBServices::LLDBServices(lldb::SBDebugger &debugger, lldb::SBCommandReturnObject &returnObject, lldb::SBProcess *process, lldb::SBThread *thread) : - m_ref(1), - m_debugger(debugger), - m_returnObject(returnObject), - m_currentProcess(process), - m_currentThread(thread) -{ - returnObject.SetStatus(lldb::eReturnStatusSuccessFinishResult); -} - -LLDBServices::~LLDBServices() -{ -} - -//---------------------------------------------------------------------------- -// IUnknown -//---------------------------------------------------------------------------- - -HRESULT -LLDBServices::QueryInterface( - REFIID InterfaceId, - PVOID* Interface - ) -{ - if (InterfaceId == __uuidof(IUnknown) || - InterfaceId == __uuidof(ILLDBServices)) - { - *Interface = (ILLDBServices*)this; - AddRef(); - return S_OK; - } - else - { - *Interface = NULL; - return E_NOINTERFACE; - } -} - -ULONG -LLDBServices::AddRef() -{ - LONG ref = InterlockedIncrement(&m_ref); - return ref; -} - -ULONG -LLDBServices::Release() -{ - LONG ref = InterlockedDecrement(&m_ref); - if (ref == 0) - { - delete this; - } - return ref; -} - -//---------------------------------------------------------------------------- -// ILLDBServices -//---------------------------------------------------------------------------- - -PCSTR -LLDBServices::GetCoreClrDirectory() -{ - return g_coreclrDirectory; -} - -DWORD_PTR -LLDBServices::GetExpression( - PCSTR exp) -{ - if (exp == nullptr) - { - return 0; - } - - lldb::SBFrame frame = GetCurrentFrame(); - if (!frame.IsValid()) - { - return 0; - } - - DWORD_PTR result = 0; - lldb::SBError error; - std::string str; - - // To be compatible with windbg/dbgeng, we need to emulate the default - // hex radix (because sos prints addresses and other hex values without - // the 0x) by first prepending 0x and if that fails use the actual - // undecorated expression. - str.append("0x"); - str.append(exp); - - result = GetExpression(frame, error, str.c_str()); - if (error.Fail()) - { - result = GetExpression(frame, error, exp); - } - - return result; -} - -// Internal function -DWORD_PTR -LLDBServices::GetExpression( - /* const */ lldb::SBFrame& frame, - lldb::SBError& error, - PCSTR exp) -{ - DWORD_PTR result = 0; - - lldb::SBValue value = frame.EvaluateExpression(exp, lldb::eNoDynamicValues); - if (value.IsValid()) - { - result = value.GetValueAsUnsigned(error); - } - - return result; -} - -// -// lldb doesn't have a way or API to unwind an arbitrary context (IP, SP) -// and return the next frame so we have to stick with the native frames -// lldb has found and find the closest frame to the incoming context SP. -// -HRESULT -LLDBServices::VirtualUnwind( - DWORD threadID, - ULONG32 contextSize, - PBYTE context) -{ - lldb::SBProcess process; - lldb::SBThread thread; - - if (context == NULL || contextSize < sizeof(DT_CONTEXT)) - { - return E_INVALIDARG; - } - - process = GetCurrentProcess(); - if (!process.IsValid()) - { - return E_FAIL; - } - - thread = process.GetThreadByID(threadID); - if (!thread.IsValid()) - { - return E_FAIL; - } - - DT_CONTEXT *dtcontext = (DT_CONTEXT*)context; - lldb::SBFrame frameFound; - -#ifdef DBG_TARGET_AMD64 - DWORD64 spToFind = dtcontext->Rsp; -#elif DBG_TARGET_X86 - DWORD spToFind = dtcontext->Esp; -#elif DBG_TARGET_ARM - DWORD spToFind = dtcontext->Sp; -#elif DBG_TARGET_ARM64 - DWORD64 spToFind = dtcontext->Sp; -#else -#error "spToFind undefined for this platform" -#endif - - int numFrames = thread.GetNumFrames(); - for (int i = 0; i < numFrames; i++) - { - lldb::SBFrame frame = thread.GetFrameAtIndex(i); - if (!frame.IsValid()) - { - break; - } - lldb::addr_t sp = frame.GetSP(); - - if ((i + 1) < numFrames) - { - lldb::SBFrame frameNext = thread.GetFrameAtIndex(i + 1); - if (frameNext.IsValid()) - { - lldb::addr_t spNext = frameNext.GetSP(); - - // An exact match of the current frame's SP would be nice - // but sometimes the incoming context is between lldb frames - if (spToFind >= sp && spToFind < spNext) - { - frameFound = frameNext; - break; - } - } - } - } - - if (!frameFound.IsValid()) - { - return E_FAIL; - } - - GetContextFromFrame(frameFound, dtcontext); - - return S_OK; -} - -bool -ExceptionBreakpointCallback( - void *baton, - lldb::SBProcess &process, - lldb::SBThread &thread, - lldb::SBBreakpointLocation &location) -{ - lldb::SBDebugger debugger = process.GetTarget().GetDebugger(); - - // Send the normal and error output to stdout/stderr since we - // don't have a return object from the command interpreter. - lldb::SBCommandReturnObject result; - result.SetImmediateOutputFile(stdout); - result.SetImmediateErrorFile(stderr); - - // Save the process and thread to be used by the current process/thread - // helper functions. - LLDBServices* client = new LLDBServices(debugger, result, &process, &thread); - return ((PFN_EXCEPTION_CALLBACK)baton)(client) == S_OK; -} - -lldb::SBBreakpoint g_exceptionbp; - -HRESULT -LLDBServices::SetExceptionCallback( - PFN_EXCEPTION_CALLBACK callback) -{ - if (!g_exceptionbp.IsValid()) - { - lldb::SBTarget target = m_debugger.GetSelectedTarget(); - if (!target.IsValid()) - { - return E_FAIL; - } - lldb::SBBreakpoint exceptionbp = target.BreakpointCreateForException(lldb::LanguageType::eLanguageTypeC_plus_plus, false, true); - if (!exceptionbp.IsValid()) - { - return E_FAIL; - } -#ifdef FLAGS_ANONYMOUS_ENUM - exceptionbp.AddName("DoNotDeleteOrDisable"); -#endif - exceptionbp.SetCallback(ExceptionBreakpointCallback, (void *)callback); - g_exceptionbp = exceptionbp; - } - return S_OK; -} - -HRESULT -LLDBServices::ClearExceptionCallback() -{ - if (g_exceptionbp.IsValid()) - { - lldb::SBTarget target = m_debugger.GetSelectedTarget(); - if (!target.IsValid()) - { - return E_FAIL; - } - target.BreakpointDelete(g_exceptionbp.GetID()); - g_exceptionbp = lldb::SBBreakpoint(); - } - return S_OK; -} - -//---------------------------------------------------------------------------- -// IDebugControl2 -//---------------------------------------------------------------------------- - -// Checks for a user interrupt, such a Ctrl-C -// or stop button. -// This method is reentrant. -HRESULT -LLDBServices::GetInterrupt() -{ - return E_FAIL; -} - -// Sends output through clients -// output callbacks if the mask is allowed -// by the current output control mask and -// according to the output distribution -// settings. -HRESULT -LLDBServices::Output( - ULONG mask, - PCSTR format, - ...) -{ - va_list args; - va_start (args, format); - HRESULT result = OutputVaList(mask, format, args); - va_end (args); - return result; -} - -HRESULT -LLDBServices::OutputVaList( - ULONG mask, - PCSTR format, - va_list args) -{ - HRESULT result = S_OK; - char str[1024]; - - va_list args_copy; - va_copy (args_copy, args); - - // Try and format our string into a fixed buffer first and see if it fits - size_t length = ::vsnprintf(str, sizeof(str), format, args); - if (length < sizeof(str)) - { - OutputString(mask, str); - } - else - { - // Our stack buffer wasn't big enough to contain the entire formatted - // string, so lets let vasprintf create the string for us! - char *str_ptr = nullptr; - length = ::vasprintf(&str_ptr, format, args_copy); - if (str_ptr) - { - OutputString(mask, str_ptr); - ::free (str_ptr); - } - else - { - result = E_FAIL; - } - } - - va_end (args_copy); - - return result; -} - -// The following methods allow direct control -// over the distribution of the given output -// for situations where something other than -// the default is desired. These methods require -// extra work in the engine so they should -// only be used when necessary. -HRESULT -LLDBServices::ControlledOutput( - ULONG outputControl, - ULONG mask, - PCSTR format, - ...) -{ - va_list args; - va_start (args, format); - HRESULT result = ControlledOutputVaList(outputControl, mask, format, args); - va_end (args); - return result; -} - -HRESULT -LLDBServices::ControlledOutputVaList( - ULONG outputControl, - ULONG mask, - PCSTR format, - va_list args) -{ - return OutputVaList(mask, format, args); -} - -// Returns information about the debuggee such -// as user vs. kernel, dump vs. live, etc. -HRESULT -LLDBServices::GetDebuggeeType( - PULONG debugClass, - PULONG qualifier) -{ - *debugClass = DEBUG_CLASS_USER_WINDOWS; - *qualifier = 0; - return S_OK; -} - -// Returns the page size for the currently executing -// processor context. The page size may vary between -// processor types. -HRESULT -LLDBServices::GetPageSize( - PULONG size) -{ - *size = 4096; - return S_OK; -} - -HRESULT -LLDBServices::GetExecutingProcessorType( - PULONG type) -{ -#ifdef DBG_TARGET_AMD64 - *type = IMAGE_FILE_MACHINE_AMD64; -#elif DBG_TARGET_ARM - *type = IMAGE_FILE_MACHINE_ARMNT; -#elif DBG_TARGET_ARM64 - *type = IMAGE_FILE_MACHINE_ARM64; -#elif DBG_TARGET_X86 - *type = IMAGE_FILE_MACHINE_I386; -#else -#error "Unsupported target" -#endif - return S_OK; -} - -HRESULT -LLDBServices::Execute( - ULONG outputControl, - PCSTR command, - ULONG flags) -{ - lldb::SBCommandInterpreter interpreter = m_debugger.GetCommandInterpreter(); - - lldb::SBCommandReturnObject result; - lldb::ReturnStatus status = interpreter.HandleCommand(command, result); - - return status <= lldb::eReturnStatusSuccessContinuingResult ? S_OK : E_FAIL; -} - -// PAL raise exception function and exception record pointer variable name -// See coreclr\src\pal\src\exception\seh-unwind.cpp for the details. This -// function depends on RtlpRaisException not being inlined or optimized. -#define FUNCTION_NAME "RtlpRaiseException" -#define VARIABLE_NAME "ExceptionRecord" - -HRESULT -LLDBServices::GetLastEventInformation( - PULONG type, - PULONG processId, - PULONG threadId, - PVOID extraInformation, - ULONG extraInformationSize, - PULONG extraInformationUsed, - PSTR description, - ULONG descriptionSize, - PULONG descriptionUsed) -{ - if (extraInformationSize < sizeof(DEBUG_LAST_EVENT_INFO_EXCEPTION) || - type == NULL || processId == NULL || threadId == NULL || extraInformationUsed == NULL) - { - return E_INVALIDARG; - } - - *type = DEBUG_EVENT_EXCEPTION; - *processId = 0; - *threadId = 0; - *extraInformationUsed = sizeof(DEBUG_LAST_EVENT_INFO_EXCEPTION); - - DEBUG_LAST_EVENT_INFO_EXCEPTION *pdle = (DEBUG_LAST_EVENT_INFO_EXCEPTION *)extraInformation; - pdle->FirstChance = 1; - - lldb::SBProcess process = GetCurrentProcess(); - if (!process.IsValid()) - { - return E_FAIL; - } - lldb::SBThread thread = GetCurrentThread(); - if (!thread.IsValid()) - { - return E_FAIL; - } - - *processId = process.GetProcessID(); - *threadId = thread.GetThreadID(); - - // Enumerate each stack frame at the special "throw" - // breakpoint and find the raise exception function - // with the exception record parameter. - int numFrames = thread.GetNumFrames(); - for (int i = 0; i < numFrames; i++) - { - lldb::SBFrame frame = thread.GetFrameAtIndex(i); - if (!frame.IsValid()) - { - break; - } - - const char *functionName = frame.GetFunctionName(); - if (functionName == NULL || strncmp(functionName, FUNCTION_NAME, sizeof(FUNCTION_NAME) - 1) != 0) - { - continue; - } - - lldb::SBValue exValue = frame.FindVariable(VARIABLE_NAME); - if (!exValue.IsValid()) - { - break; - } - - lldb::SBError error; - ULONG64 pExceptionRecord = exValue.GetValueAsUnsigned(error); - if (error.Fail()) - { - break; - } - - process.ReadMemory(pExceptionRecord, &pdle->ExceptionRecord, sizeof(pdle->ExceptionRecord), error); - if (error.Fail()) - { - break; - } - - return S_OK; - } - - return E_FAIL; -} - -HRESULT -LLDBServices::Disassemble( - ULONG64 offset, - ULONG flags, - PSTR buffer, - ULONG bufferSize, - PULONG disassemblySize, - PULONG64 endOffset) -{ - lldb::SBInstruction instruction; - lldb::SBInstructionList list; - lldb::SBTarget target; - lldb::SBAddress address; - lldb::SBError error; - lldb::SBData data; - std::string str; - HRESULT hr = S_OK; - ULONG size = 0; - uint8_t byte; - int cch; - - // lldb doesn't expect sign-extended address - offset = CONVERT_FROM_SIGN_EXTENDED(offset); - - if (buffer == NULL) - { - hr = E_INVALIDARG; - goto exit; - } - *buffer = 0; - - target = m_debugger.GetSelectedTarget(); - if (!target.IsValid()) - { - hr = E_INVALIDARG; - goto exit; - } - address = target.ResolveLoadAddress(offset); - if (!address.IsValid()) - { - hr = E_INVALIDARG; - goto exit; - } - list = target.ReadInstructions(address, 1, "intel"); - if (!list.IsValid()) - { - hr = E_FAIL; - goto exit; - } - instruction = list.GetInstructionAtIndex(0); - if (!instruction.IsValid()) - { - hr = E_FAIL; - goto exit; - } - cch = snprintf(buffer, bufferSize, "%016llx ", (unsigned long long)offset); - buffer += cch; - bufferSize -= cch; - - size = instruction.GetByteSize(); - data = instruction.GetData(target); - for (ULONG i = 0; i < size && bufferSize > 0; i++) - { - byte = data.GetUnsignedInt8(error, i); - if (error.Fail()) - { - hr = E_FAIL; - goto exit; - } - cch = snprintf(buffer, bufferSize, "%02x", byte); - buffer += cch; - bufferSize -= cch; - } - // Pad the data bytes to 16 chars - cch = size * 2; - while (bufferSize > 0) - { - *buffer++ = ' '; - bufferSize--; - if (++cch >= 21) - break; - } - - cch = snprintf(buffer, bufferSize, "%s", instruction.GetMnemonic(target)); - buffer += cch; - bufferSize -= cch; - - // Pad the mnemonic to 8 chars - while (bufferSize > 0) - { - *buffer++ = ' '; - bufferSize--; - if (++cch >= 8) - break; - } - snprintf(buffer, bufferSize, "%s\n", instruction.GetOperands(target)); - -exit: - if (disassemblySize != NULL) - { - *disassemblySize = size; - } - if (endOffset != NULL) - { - *endOffset = offset + size; - } - return hr; -} - -// Internal output string function -void -LLDBServices::OutputString( - ULONG mask, - PCSTR str) -{ - if (mask == DEBUG_OUTPUT_ERROR) - { - m_returnObject.SetStatus(lldb::eReturnStatusFailed); - } - // Can not use AppendMessage or AppendWarning because they add a newline. SetError - // can not be used for DEBUG_OUTPUT_ERROR mask because it caches the error strings - // seperately from the normal output so error/normal texts are not intermixed - // correctly. - m_returnObject.Printf("%s", str); -} - -//---------------------------------------------------------------------------- -// IDebugControl4 -//---------------------------------------------------------------------------- - -HRESULT -LLDBServices::GetContextStackTrace( - PVOID startContext, - ULONG startContextSize, - PDEBUG_STACK_FRAME frames, - ULONG framesSize, - PVOID frameContexts, - ULONG frameContextsSize, - ULONG frameContextsEntrySize, - PULONG framesFilled) -{ - DT_CONTEXT *currentContext = (DT_CONTEXT*)frameContexts; - PDEBUG_STACK_FRAME currentFrame = frames; - lldb::SBThread thread; - lldb::SBFrame frame; - ULONG cFrames = 0; - HRESULT hr = S_OK; - - // Doesn't support a starting context - if (startContext != NULL || frames == NULL || frameContexts == NULL || frameContextsEntrySize != sizeof(DT_CONTEXT)) - { - hr = E_INVALIDARG; - goto exit; - } - - thread = GetCurrentThread(); - if (!thread.IsValid()) - { - hr = E_FAIL; - goto exit; - } - - frame = thread.GetFrameAtIndex(0); - for (uint32_t i = 0; i < thread.GetNumFrames(); i++) - { - if (!frame.IsValid() || (cFrames > framesSize) || ((char *)currentContext > ((char *)frameContexts + frameContextsSize))) - { - break; - } - lldb::SBFrame framePrevious; - lldb::SBFrame frameNext; - - currentFrame->InstructionOffset = frame.GetPC(); - currentFrame->StackOffset = frame.GetSP(); - - currentFrame->FuncTableEntry = 0; - currentFrame->Params[0] = 0; - currentFrame->Params[1] = 0; - currentFrame->Params[2] = 0; - currentFrame->Params[3] = 0; - currentFrame->Virtual = i == 0 ? TRUE : FALSE; - currentFrame->FrameNumber = frame.GetFrameID(); - - frameNext = thread.GetFrameAtIndex(i + 1); - if (frameNext.IsValid()) - { - currentFrame->ReturnOffset = frameNext.GetPC(); - } - - if (framePrevious.IsValid()) - { - currentFrame->FrameOffset = framePrevious.GetSP(); - } - else - { - currentFrame->FrameOffset = frame.GetSP(); - } - - GetContextFromFrame(frame, currentContext); - - framePrevious = frame; - frame = frameNext; - currentContext++; - currentFrame++; - cFrames++; - } - -exit: - if (framesFilled != NULL) - { - *framesFilled = cFrames; - } - return hr; -} - -//---------------------------------------------------------------------------- -// IDebugDataSpaces -//---------------------------------------------------------------------------- - -HRESULT -LLDBServices::ReadVirtual( - ULONG64 offset, - PVOID buffer, - ULONG bufferSize, - PULONG bytesRead) -{ - lldb::SBError error; - size_t read = 0; - - // lldb doesn't expect sign-extended address - offset = CONVERT_FROM_SIGN_EXTENDED(offset); - - lldb::SBProcess process = GetCurrentProcess(); - if (!process.IsValid()) - { - goto exit; - } - - read = process.ReadMemory(offset, buffer, bufferSize, error); - -exit: - if (bytesRead) - { - *bytesRead = read; - } - return error.Success() || (read != 0) ? S_OK : E_FAIL; -} - -HRESULT -LLDBServices::WriteVirtual( - ULONG64 offset, - PVOID buffer, - ULONG bufferSize, - PULONG bytesWritten) -{ - lldb::SBError error; - size_t written = 0; - - // lldb doesn't expect sign-extended address - offset = CONVERT_FROM_SIGN_EXTENDED(offset); - - lldb::SBProcess process = GetCurrentProcess(); - if (!process.IsValid()) - { - goto exit; - } - - written = process.WriteMemory(offset, buffer, bufferSize, error); - -exit: - if (bytesWritten) - { - *bytesWritten = written; - } - return error.Success() || (written != 0) ? S_OK : E_FAIL; -} - -//---------------------------------------------------------------------------- -// IDebugSymbols -//---------------------------------------------------------------------------- - -HRESULT -LLDBServices::GetSymbolOptions( - PULONG options) -{ - *options = SYMOPT_LOAD_LINES; - return S_OK; -} - -HRESULT -LLDBServices::GetNameByOffset( - ULONG64 offset, - PSTR nameBuffer, - ULONG nameBufferSize, - PULONG nameSize, - PULONG64 displacement) -{ - ULONG64 disp = DEBUG_INVALID_OFFSET; - HRESULT hr = S_OK; - - lldb::SBTarget target; - lldb::SBAddress address; - lldb::SBModule module; - lldb::SBFileSpec file; - lldb::SBSymbol symbol; - std::string str; - - // lldb doesn't expect sign-extended address - offset = CONVERT_FROM_SIGN_EXTENDED(offset); - - target = m_debugger.GetSelectedTarget(); - if (!target.IsValid()) - { - hr = E_FAIL; - goto exit; - } - - address = target.ResolveLoadAddress(offset); - if (!address.IsValid()) - { - hr = E_INVALIDARG; - goto exit; - } - - module = address.GetModule(); - if (!module.IsValid()) - { - hr = E_FAIL; - goto exit; - } - - file = module.GetFileSpec(); - if (file.IsValid()) - { - str.append(file.GetFilename()); - } - - symbol = address.GetSymbol(); - if (symbol.IsValid()) - { - lldb::SBAddress startAddress = symbol.GetStartAddress(); - disp = address.GetOffset() - startAddress.GetOffset(); - - const char *name = symbol.GetName(); - if (name) - { - if (file.IsValid()) - { - str.append("!"); - } - str.append(name); - } - } - - str.append(1, '\0'); - -exit: - if (nameSize) - { - *nameSize = str.length(); - } - if (nameBuffer) - { - str.copy(nameBuffer, nameBufferSize); - } - if (displacement) - { - *displacement = disp; - } - return hr; -} - -HRESULT -LLDBServices::GetNumberModules( - PULONG loaded, - PULONG unloaded) -{ - ULONG numModules = 0; - HRESULT hr = S_OK; - - lldb::SBTarget target = m_debugger.GetSelectedTarget(); - if (!target.IsValid()) - { - hr = E_FAIL; - goto exit; - } - - numModules = target.GetNumModules(); - -exit: - if (loaded) - { - *loaded = numModules; - } - if (unloaded) - { - *unloaded = 0; - } - return hr; -} - -HRESULT LLDBServices::GetModuleByIndex( - ULONG index, - PULONG64 base) -{ - ULONG64 moduleBase = UINT64_MAX; - - lldb::SBTarget target; - lldb::SBModule module; - - target = m_debugger.GetSelectedTarget(); - if (!target.IsValid()) - { - goto exit; - } - - module = target.GetModuleAtIndex(index); - if (!module.IsValid()) - { - goto exit; - } - - moduleBase = GetModuleBase(target, module); - -exit: - if (base) - { - *base = moduleBase; - } - return moduleBase == UINT64_MAX ? E_FAIL : S_OK; -} - -HRESULT -LLDBServices::GetModuleByModuleName( - PCSTR name, - ULONG startIndex, - PULONG index, - PULONG64 base) -{ - ULONG64 moduleBase = UINT64_MAX; - ULONG moduleIndex = UINT32_MAX; - - lldb::SBTarget target; - lldb::SBModule module; - lldb::SBFileSpec fileSpec; - fileSpec.SetFilename(name); - - target = m_debugger.GetSelectedTarget(); - if (!target.IsValid()) - { - goto exit; - } - - module = target.FindModule(fileSpec); - if (!module.IsValid()) - { - goto exit; - } - - moduleBase = GetModuleBase(target, module); - - if (index) - { - int numModules = target.GetNumModules(); - for (int mi = startIndex; mi < numModules; mi++) - { - lldb::SBModule mod = target.GetModuleAtIndex(mi); - if (module == mod) - { - moduleIndex = mi; - break; - } - } - } - -exit: - if (index) - { - *index = moduleIndex; - } - if (base) - { - *base = moduleBase; - } - return moduleBase == UINT64_MAX ? E_FAIL : S_OK; -} - -HRESULT -LLDBServices::GetModuleByOffset( - ULONG64 offset, - ULONG startIndex, - PULONG index, - PULONG64 base) -{ - ULONG64 moduleBase = UINT64_MAX; - ULONG moduleIndex = UINT32_MAX; - - lldb::SBTarget target; - int numModules; - - // lldb doesn't expect sign-extended address - offset = CONVERT_FROM_SIGN_EXTENDED(offset); - - target = m_debugger.GetSelectedTarget(); - if (!target.IsValid()) - { - goto exit; - } - - numModules = target.GetNumModules(); - for (int mi = startIndex; mi < numModules; mi++) - { - lldb::SBModule module = target.GetModuleAtIndex(mi); - - int numSections = module.GetNumSections(); - for (int si = 0; si < numSections; si++) - { - lldb::SBSection section = module.GetSectionAtIndex(si); - if (section.IsValid()) - { - lldb::addr_t baseAddress = section.GetLoadAddress(target); - if (baseAddress != LLDB_INVALID_ADDRESS) - { - if (offset > baseAddress) - { - if ((offset - baseAddress) < section.GetByteSize()) - { - moduleIndex = mi; - moduleBase = baseAddress - section.GetFileOffset(); - goto exit; - } - } - } - } - } - } - -exit: - if (index) - { - *index = moduleIndex; - } - if (base) - { - *base = moduleBase; - } - return moduleBase == UINT64_MAX ? E_FAIL : S_OK; -} - -HRESULT -LLDBServices::GetModuleNames( - ULONG index, - ULONG64 base, - PSTR imageNameBuffer, - ULONG imageNameBufferSize, - PULONG imageNameSize, - PSTR moduleNameBuffer, - ULONG moduleNameBufferSize, - PULONG moduleNameSize, - PSTR loadedImageNameBuffer, - ULONG loadedImageNameBufferSize, - PULONG loadedImageNameSize) -{ - lldb::SBTarget target; - lldb::SBFileSpec fileSpec; - HRESULT hr = S_OK; - - // lldb doesn't expect sign-extended address - base = CONVERT_FROM_SIGN_EXTENDED(base); - - target = m_debugger.GetSelectedTarget(); - if (!target.IsValid()) - { - hr = E_FAIL; - goto exit; - } - - if (index != DEBUG_ANY_ID) - { - lldb::SBModule module = target.GetModuleAtIndex(index); - if (module.IsValid()) - { - fileSpec = module.GetFileSpec(); - } - } - else - { - int numModules = target.GetNumModules(); - for (int mi = 0; mi < numModules; mi++) - { - lldb::SBModule module = target.GetModuleAtIndex(mi); - if (module.IsValid()) - { - ULONG64 moduleBase = GetModuleBase(target, module); - if (base == moduleBase) - { - fileSpec = module.GetFileSpec(); - break; - } - } - } - } - - if (!fileSpec.IsValid()) - { - hr = E_FAIL; - goto exit; - } - -exit: - if (imageNameBuffer) - { - int size = fileSpec.GetPath(imageNameBuffer, imageNameBufferSize); - if (imageNameSize) - { - *imageNameSize = size; - } - } - if (moduleNameBuffer) - { - const char *fileName = fileSpec.GetFilename(); - if (fileName == NULL) - { - fileName = ""; - } - stpncpy(moduleNameBuffer, fileName, moduleNameBufferSize); - if (moduleNameSize) - { - *moduleNameSize = strlen(fileName); - } - } - if (loadedImageNameBuffer) - { - int size = fileSpec.GetPath(loadedImageNameBuffer, loadedImageNameBufferSize); - if (loadedImageNameSize) - { - *loadedImageNameSize = size; - } - } - return hr; -} - -HRESULT -LLDBServices::GetLineByOffset( - ULONG64 offset, - PULONG fileLine, - PSTR fileBuffer, - ULONG fileBufferSize, - PULONG fileSize, - PULONG64 displacement) -{ - ULONG64 disp = DEBUG_INVALID_OFFSET; - HRESULT hr = S_OK; - ULONG line = 0; - - lldb::SBTarget target; - lldb::SBAddress address; - lldb::SBFileSpec file; - lldb::SBLineEntry lineEntry; - std::string str; - - // lldb doesn't expect sign-extended address - offset = CONVERT_FROM_SIGN_EXTENDED(offset); - - target = m_debugger.GetSelectedTarget(); - if (!target.IsValid()) - { - hr = E_FAIL; - goto exit; - } - - address = target.ResolveLoadAddress(offset); - if (!address.IsValid()) - { - hr = E_INVALIDARG; - goto exit; - } - - if (displacement) - { - lldb::SBSymbol symbol = address.GetSymbol(); - if (symbol.IsValid()) - { - lldb::SBAddress startAddress = symbol.GetStartAddress(); - disp = address.GetOffset() - startAddress.GetOffset(); - } - } - - lineEntry = address.GetLineEntry(); - if (!lineEntry.IsValid()) - { - hr = E_FAIL; - goto exit; - } - - line = lineEntry.GetLine(); - file = lineEntry.GetFileSpec(); - if (file.IsValid()) - { - str.append(file.GetDirectory()); - str.append(1, '/'); - str.append(file.GetFilename()); - } - - str.append(1, '\0'); - -exit: - if (fileLine) - { - *fileLine = line; - } - if (fileSize) - { - *fileSize = str.length(); - } - if (fileBuffer) - { - str.copy(fileBuffer, fileBufferSize); - } - if (displacement) - { - *displacement = disp; - } - return hr; -} - -HRESULT -LLDBServices::GetSourceFileLineOffsets( - PCSTR file, - PULONG64 buffer, - ULONG bufferLines, - PULONG fileLines) -{ - if (fileLines != NULL) - { - *fileLines = (ULONG)-1; - } - return E_NOTIMPL; -} - -HRESULT -LLDBServices::FindSourceFile( - ULONG startElement, - PCSTR file, - ULONG flags, - PULONG foundElement, - PSTR buffer, - ULONG bufferSize, - PULONG foundSize) -{ - return E_NOTIMPL; -} - -// Internal functions -PCSTR -LLDBServices::GetModuleDirectory( - PCSTR name) -{ - lldb::SBTarget target = m_debugger.GetSelectedTarget(); - if (!target.IsValid()) - { - return NULL; - } - - lldb::SBFileSpec fileSpec; - fileSpec.SetFilename(name); - - lldb::SBModule module = target.FindModule(fileSpec); - if (!module.IsValid()) - { - return NULL; - } - - return module.GetFileSpec().GetDirectory(); -} - -ULONG64 -LLDBServices::GetModuleBase( - /* const */ lldb::SBTarget& target, - /* const */ lldb::SBModule& module) -{ - // Find the first section with an valid base address - int numSections = module.GetNumSections(); - for (int si = 0; si < numSections; si++) - { - lldb::SBSection section = module.GetSectionAtIndex(si); - if (section.IsValid()) - { - lldb::addr_t baseAddress = section.GetLoadAddress(target); - if (baseAddress != LLDB_INVALID_ADDRESS) - { - return baseAddress - section.GetFileOffset(); - } - } - } - - return UINT64_MAX; -} - -//---------------------------------------------------------------------------- -// IDebugSystemObjects -//---------------------------------------------------------------------------- - -HRESULT -LLDBServices::GetCurrentProcessId( - PULONG id) -{ - if (id == NULL) - { - return E_INVALIDARG; - } - - lldb::SBProcess process = GetCurrentProcess(); - if (!process.IsValid()) - { - *id = 0; - return E_FAIL; - } - - *id = process.GetProcessID(); - return S_OK; -} - -HRESULT -LLDBServices::GetCurrentThreadId( - PULONG id) -{ - if (id == NULL) - { - return E_INVALIDARG; - } - - lldb::SBThread thread = GetCurrentThread(); - if (!thread.IsValid()) - { - *id = 0; - return E_FAIL; - } - - // This is allow the a valid current TID to be returned to - // workaround a bug in lldb on core dumps. - if (g_currentThreadIndex != (ULONG)-1) - { - *id = g_currentThreadIndex; - return S_OK; - } - - *id = thread.GetIndexID(); - return S_OK; -} - -HRESULT -LLDBServices::SetCurrentThreadId( - ULONG id) -{ - lldb::SBProcess process = GetCurrentProcess(); - if (!process.IsValid()) - { - return E_FAIL; - } - - if (!process.SetSelectedThreadByIndexID(id)) - { - return E_FAIL; - } - - return S_OK; -} - -HRESULT -LLDBServices::GetCurrentThreadSystemId( - PULONG sysId) -{ - if (sysId == NULL) - { - return E_INVALIDARG; - } - - lldb::SBThread thread = GetCurrentThread(); - if (!thread.IsValid()) - { - *sysId = 0; - return E_FAIL; - } - - // This is allow the a valid current TID to be returned to - // workaround a bug in lldb on core dumps. - if (g_currentThreadSystemId != (ULONG)-1) - { - *sysId = g_currentThreadSystemId; - return S_OK; - } - - *sysId = thread.GetThreadID(); - return S_OK; -} - -HRESULT -LLDBServices::GetThreadIdBySystemId( - ULONG sysId, - PULONG threadId) -{ - HRESULT hr = E_FAIL; - ULONG id = 0; - - lldb::SBProcess process; - lldb::SBThread thread; - - if (threadId == NULL) - { - return E_INVALIDARG; - } - - process = GetCurrentProcess(); - if (!process.IsValid()) - { - goto exit; - } - - // If we have a "fake" thread OS (system) id and a fake thread index, - // we need to return fake thread index. - if (g_currentThreadSystemId == sysId && g_currentThreadIndex != (ULONG)-1) - { - id = g_currentThreadIndex; - } - else - { - thread = process.GetThreadByID(sysId); - if (!thread.IsValid()) - { - goto exit; - } - - id = thread.GetIndexID(); - } - hr = S_OK; - -exit: - *threadId = id; - return hr; -} - -HRESULT -LLDBServices::GetThreadContextById( - /* in */ ULONG32 threadID, - /* in */ ULONG32 contextFlags, - /* in */ ULONG32 contextSize, - /* out */ PBYTE context) -{ - lldb::SBProcess process; - lldb::SBThread thread; - lldb::SBFrame frame; - DT_CONTEXT *dtcontext; - HRESULT hr = E_FAIL; - - if (context == NULL || contextSize < sizeof(DT_CONTEXT)) - { - goto exit; - } - memset(context, 0, contextSize); - - process = GetCurrentProcess(); - if (!process.IsValid()) - { - goto exit; - } - - // If we have a "fake" thread OS (system) id and a fake thread index, - // use the fake thread index to get the context. - if (g_currentThreadSystemId == threadID && g_currentThreadIndex != (ULONG)-1) - { - thread = process.GetThreadByIndexID(g_currentThreadIndex); - } - else - { - thread = process.GetThreadByID(threadID); - } - - if (!thread.IsValid()) - { - goto exit; - } - - frame = thread.GetFrameAtIndex(0); - if (!frame.IsValid()) - { - goto exit; - } - - dtcontext = (DT_CONTEXT*)context; - dtcontext->ContextFlags = contextFlags; - - GetContextFromFrame(frame, dtcontext); - hr = S_OK; - -exit: - return hr; -} - -// Internal function -void -LLDBServices::GetContextFromFrame( - /* const */ lldb::SBFrame& frame, - DT_CONTEXT *dtcontext) -{ -#ifdef DBG_TARGET_AMD64 - dtcontext->Rip = frame.GetPC(); - dtcontext->Rsp = frame.GetSP(); - dtcontext->Rbp = frame.GetFP(); - dtcontext->EFlags = GetRegister(frame, "rflags"); - - dtcontext->Rax = GetRegister(frame, "rax"); - dtcontext->Rbx = GetRegister(frame, "rbx"); - dtcontext->Rcx = GetRegister(frame, "rcx"); - dtcontext->Rdx = GetRegister(frame, "rdx"); - dtcontext->Rsi = GetRegister(frame, "rsi"); - dtcontext->Rdi = GetRegister(frame, "rdi"); - dtcontext->R8 = GetRegister(frame, "r8"); - dtcontext->R9 = GetRegister(frame, "r9"); - dtcontext->R10 = GetRegister(frame, "r10"); - dtcontext->R11 = GetRegister(frame, "r11"); - dtcontext->R12 = GetRegister(frame, "r12"); - dtcontext->R13 = GetRegister(frame, "r13"); - dtcontext->R14 = GetRegister(frame, "r14"); - dtcontext->R15 = GetRegister(frame, "r15"); - - dtcontext->SegCs = GetRegister(frame, "cs"); - dtcontext->SegSs = GetRegister(frame, "ss"); - dtcontext->SegDs = GetRegister(frame, "ds"); - dtcontext->SegEs = GetRegister(frame, "es"); - dtcontext->SegFs = GetRegister(frame, "fs"); - dtcontext->SegGs = GetRegister(frame, "gs"); -#elif DBG_TARGET_ARM - dtcontext->Pc = frame.GetPC(); - dtcontext->Sp = frame.GetSP(); - dtcontext->Lr = GetRegister(frame, "lr"); - dtcontext->Cpsr = GetRegister(frame, "cpsr"); - - dtcontext->R0 = GetRegister(frame, "r0"); - dtcontext->R1 = GetRegister(frame, "r1"); - dtcontext->R2 = GetRegister(frame, "r2"); - dtcontext->R3 = GetRegister(frame, "r3"); - dtcontext->R4 = GetRegister(frame, "r4"); - dtcontext->R5 = GetRegister(frame, "r5"); - dtcontext->R6 = GetRegister(frame, "r6"); - dtcontext->R7 = GetRegister(frame, "r7"); - dtcontext->R8 = GetRegister(frame, "r8"); - dtcontext->R9 = GetRegister(frame, "r9"); - dtcontext->R10 = GetRegister(frame, "r10"); - dtcontext->R11 = GetRegister(frame, "r11"); - dtcontext->R12 = GetRegister(frame, "r12"); -#elif DBG_TARGET_X86 - dtcontext->Eip = frame.GetPC(); - dtcontext->Esp = frame.GetSP(); - dtcontext->Ebp = frame.GetFP(); - dtcontext->EFlags = GetRegister(frame, "eflags"); - - dtcontext->Edi = GetRegister(frame, "edi"); - dtcontext->Esi = GetRegister(frame, "esi"); - dtcontext->Ebx = GetRegister(frame, "ebx"); - dtcontext->Edx = GetRegister(frame, "edx"); - dtcontext->Ecx = GetRegister(frame, "ecx"); - dtcontext->Eax = GetRegister(frame, "eax"); - - dtcontext->SegCs = GetRegister(frame, "cs"); - dtcontext->SegSs = GetRegister(frame, "ss"); - dtcontext->SegDs = GetRegister(frame, "ds"); - dtcontext->SegEs = GetRegister(frame, "es"); - dtcontext->SegFs = GetRegister(frame, "fs"); - dtcontext->SegGs = GetRegister(frame, "gs"); -#endif -} - -// Internal function -DWORD_PTR -LLDBServices::GetRegister( - /* const */ lldb::SBFrame& frame, - const char *name) -{ - lldb::SBValue regValue = frame.FindRegister(name); - - lldb::SBError error; - DWORD_PTR result = regValue.GetValueAsUnsigned(error); - - return result; -} - -//---------------------------------------------------------------------------- -// IDebugRegisters -//---------------------------------------------------------------------------- - -HRESULT -LLDBServices::GetValueByName( - PCSTR name, - PDWORD_PTR debugValue) -{ - lldb::SBFrame frame = GetCurrentFrame(); - if (!frame.IsValid()) - { - *debugValue = 0; - return E_FAIL; - } - - lldb::SBValue value = frame.FindRegister(name); - if (!value.IsValid()) - { - *debugValue = 0; - return E_FAIL; - } - - *debugValue = value.GetValueAsUnsigned(); - return S_OK; -} - -HRESULT -LLDBServices::GetInstructionOffset( - PULONG64 offset) -{ - lldb::SBFrame frame = GetCurrentFrame(); - if (!frame.IsValid()) - { - *offset = 0; - return E_FAIL; - } - - *offset = frame.GetPC(); - return S_OK; -} - -HRESULT -LLDBServices::GetStackOffset( - PULONG64 offset) -{ - lldb::SBFrame frame = GetCurrentFrame(); - if (!frame.IsValid()) - { - *offset = 0; - return E_FAIL; - } - - *offset = frame.GetSP(); - return S_OK; -} - -HRESULT -LLDBServices::GetFrameOffset( - PULONG64 offset) -{ - lldb::SBFrame frame = GetCurrentFrame(); - if (!frame.IsValid()) - { - *offset = 0; - return E_FAIL; - } - - *offset = frame.GetFP(); - return S_OK; -} - -//---------------------------------------------------------------------------- -// Helper functions -//---------------------------------------------------------------------------- - -lldb::SBProcess -LLDBServices::GetCurrentProcess() -{ - lldb::SBProcess process; - - if (m_currentProcess == nullptr) - { - lldb::SBTarget target = m_debugger.GetSelectedTarget(); - if (target.IsValid()) - { - process = target.GetProcess(); - } - } - else - { - process = *m_currentProcess; - } - - return process; -} - -lldb::SBThread -LLDBServices::GetCurrentThread() -{ - lldb::SBThread thread; - - if (m_currentThread == nullptr) - { - lldb::SBProcess process = GetCurrentProcess(); - if (process.IsValid()) - { - thread = process.GetSelectedThread(); - } - } - else - { - thread = *m_currentThread; - } - - return thread; -} - -lldb::SBFrame -LLDBServices::GetCurrentFrame() -{ - lldb::SBFrame frame; - - lldb::SBThread thread = GetCurrentThread(); - if (thread.IsValid()) - { - frame = thread.GetSelectedFrame(); - } - - return frame; -} diff --git a/src/coreclr/src/ToolBox/SOS/lldbplugin/services.h b/src/coreclr/src/ToolBox/SOS/lldbplugin/services.h deleted file mode 100644 index a8fb32f89fa..00000000000 --- a/src/coreclr/src/ToolBox/SOS/lldbplugin/services.h +++ /dev/null @@ -1,274 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#include <cstdarg> - -class LLDBServices : public ILLDBServices -{ -private: - LONG m_ref; - lldb::SBDebugger &m_debugger; - lldb::SBCommandReturnObject &m_returnObject; - - lldb::SBProcess *m_currentProcess; - lldb::SBThread *m_currentThread; - - void OutputString(ULONG mask, PCSTR str); - ULONG64 GetModuleBase(lldb::SBTarget& target, lldb::SBModule& module); - DWORD_PTR GetExpression(lldb::SBFrame& frame, lldb::SBError& error, PCSTR exp); - void GetContextFromFrame(lldb::SBFrame& frame, DT_CONTEXT *dtcontext); - DWORD_PTR GetRegister(lldb::SBFrame& frame, const char *name); - - lldb::SBProcess GetCurrentProcess(); - lldb::SBThread GetCurrentThread(); - lldb::SBFrame GetCurrentFrame(); - -public: - LLDBServices(lldb::SBDebugger &debugger, lldb::SBCommandReturnObject &returnObject, lldb::SBProcess *process = nullptr, lldb::SBThread *thread = nullptr); - virtual ~LLDBServices(); - - //---------------------------------------------------------------------------- - // IUnknown - //---------------------------------------------------------------------------- - - HRESULT STDMETHODCALLTYPE QueryInterface( - REFIID InterfaceId, - PVOID* Interface); - - ULONG STDMETHODCALLTYPE AddRef(); - - ULONG STDMETHODCALLTYPE Release(); - - //---------------------------------------------------------------------------- - // ILLDBServices - //---------------------------------------------------------------------------- - - PCSTR GetCoreClrDirectory(); - - DWORD_PTR GetExpression( - PCSTR exp); - - HRESULT VirtualUnwind( - DWORD threadID, - ULONG32 contextSize, - PBYTE context); - - HRESULT SetExceptionCallback( - PFN_EXCEPTION_CALLBACK callback); - - HRESULT ClearExceptionCallback(); - - //---------------------------------------------------------------------------- - // IDebugControl2 - //---------------------------------------------------------------------------- - - HRESULT GetInterrupt(); - - HRESULT Output( - ULONG mask, - PCSTR format, - ...); - - HRESULT OutputVaList( - ULONG mask, - PCSTR format, - va_list args); - - HRESULT ControlledOutput( - ULONG outputControl, - ULONG mask, - PCSTR format, - ...); - - HRESULT ControlledOutputVaList( - ULONG outputControl, - ULONG mask, - PCSTR format, - va_list args); - - HRESULT GetDebuggeeType( - PULONG debugClass, - PULONG qualifier); - - HRESULT GetPageSize( - PULONG size); - - HRESULT GetExecutingProcessorType( - PULONG type); - - HRESULT Execute( - ULONG outputControl, - PCSTR command, - ULONG flags); - - HRESULT GetLastEventInformation( - PULONG type, - PULONG processId, - PULONG threadId, - PVOID extraInformation, - ULONG extraInformationSize, - PULONG extraInformationUsed, - PSTR description, - ULONG descriptionSize, - PULONG descriptionUsed); - - HRESULT Disassemble( - ULONG64 offset, - ULONG flags, - PSTR buffer, - ULONG bufferSize, - PULONG disassemblySize, - PULONG64 endOffset); - - //---------------------------------------------------------------------------- - // IDebugControl4 - //---------------------------------------------------------------------------- - - HRESULT - GetContextStackTrace( - PVOID startContext, - ULONG startContextSize, - PDEBUG_STACK_FRAME frames, - ULONG framesSize, - PVOID frameContexts, - ULONG frameContextsSize, - ULONG frameContextsEntrySize, - PULONG framesFilled); - - //---------------------------------------------------------------------------- - // IDebugDataSpaces - //---------------------------------------------------------------------------- - - HRESULT ReadVirtual( - ULONG64 offset, - PVOID buffer, - ULONG bufferSize, - PULONG bytesRead); - - HRESULT WriteVirtual( - ULONG64 offset, - PVOID buffer, - ULONG bufferSize, - PULONG bytesWritten); - - //---------------------------------------------------------------------------- - // IDebugSymbols - //---------------------------------------------------------------------------- - - HRESULT GetSymbolOptions( - PULONG options); - - HRESULT GetNameByOffset( - ULONG64 offset, - PSTR nameBuffer, - ULONG nameBufferSize, - PULONG nameSize, - PULONG64 displacement); - - HRESULT GetNumberModules( - PULONG loaded, - PULONG unloaded); - - HRESULT GetModuleByIndex( - ULONG index, - PULONG64 base); - - HRESULT GetModuleByModuleName( - PCSTR name, - ULONG startIndex, - PULONG index, - PULONG64 base); - - HRESULT GetModuleByOffset( - ULONG64 offset, - ULONG startIndex, - PULONG index, - PULONG64 base); - - HRESULT GetModuleNames( - ULONG index, - ULONG64 base, - PSTR imageNameBuffer, - ULONG imageNameBufferSize, - PULONG imageNameSize, - PSTR moduleNameBuffer, - ULONG moduleNameBufferSize, - PULONG moduleNameSize, - PSTR loadedImageNameBuffer, - ULONG loadedImageNameBufferSize, - PULONG loadedImageNameSize); - - HRESULT GetLineByOffset( - ULONG64 offset, - PULONG line, - PSTR fileBuffer, - ULONG fileBufferSize, - PULONG fileSize, - PULONG64 displacement); - - HRESULT GetSourceFileLineOffsets( - PCSTR file, - PULONG64 buffer, - ULONG bufferLines, - PULONG fileLines); - - HRESULT FindSourceFile( - ULONG startElement, - PCSTR file, - ULONG flags, - PULONG foundElement, - PSTR buffer, - ULONG bufferSize, - PULONG foundSize); - - //---------------------------------------------------------------------------- - // IDebugSystemObjects - //---------------------------------------------------------------------------- - - HRESULT GetCurrentProcessId( - PULONG id); - - HRESULT GetCurrentThreadId( - PULONG id); - - HRESULT SetCurrentThreadId( - ULONG id); - - HRESULT GetCurrentThreadSystemId( - PULONG sysId); - - HRESULT GetThreadIdBySystemId( - ULONG sysId, - PULONG threadId); - - HRESULT GetThreadContextById( - ULONG32 threadID, - ULONG32 contextFlags, - ULONG32 contextSize, - PBYTE context); - - //---------------------------------------------------------------------------- - // IDebugRegisters - //---------------------------------------------------------------------------- - - HRESULT GetValueByName( - PCSTR name, - PDWORD_PTR debugValue); - - HRESULT GetInstructionOffset( - PULONG64 offset); - - HRESULT GetStackOffset( - PULONG64 offset); - - HRESULT GetFrameOffset( - PULONG64 offset); - - //---------------------------------------------------------------------------- - // LLDBServices (internal) - //---------------------------------------------------------------------------- - - PCSTR GetModuleDirectory( - PCSTR name); -}; diff --git a/src/coreclr/src/ToolBox/SOS/lldbplugin/setclrpathcommand.cpp b/src/coreclr/src/ToolBox/SOS/lldbplugin/setclrpathcommand.cpp deleted file mode 100644 index 22083066718..00000000000 --- a/src/coreclr/src/ToolBox/SOS/lldbplugin/setclrpathcommand.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#include "sosplugin.h" -#include <dlfcn.h> -#include <string.h> -#include <string> -#include <stdlib.h> -#include <limits.h> - -class setclrpathCommand : public lldb::SBCommandPluginInterface -{ -public: - setclrpathCommand() - { - } - - virtual bool - DoExecute (lldb::SBDebugger debugger, - char** arguments, - lldb::SBCommandReturnObject &result) - { - if (arguments[0] == NULL) - { - result.Printf("Load path for sos/dac/dbi: '%s'\n", g_coreclrDirectory == NULL ? "<none>" : g_coreclrDirectory); - } - else { - if (g_coreclrDirectory != NULL) - { - free(g_coreclrDirectory); - } - - std::string path(arguments[0]); - if (path[path.length() - 1] != '/') - { - path.append("/"); - } - - g_coreclrDirectory = strdup(path.c_str()); - result.Printf("Set load path for sos/dac/dbi to '%s'\n", g_coreclrDirectory); - } - return result.Succeeded(); - } -}; - -bool -setclrpathCommandInitialize(lldb::SBDebugger debugger) -{ - lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter(); - lldb::SBCommand command = interpreter.AddCommand("setclrpath", new setclrpathCommand(), "Set the path to load coreclr sos/dac/dbi files. setclrpath <path>"); - return true; -} diff --git a/src/coreclr/src/ToolBox/SOS/lldbplugin/setsostidcommand.cpp b/src/coreclr/src/ToolBox/SOS/lldbplugin/setsostidcommand.cpp deleted file mode 100644 index 67911e4d106..00000000000 --- a/src/coreclr/src/ToolBox/SOS/lldbplugin/setsostidcommand.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#include "sosplugin.h" -#include <dlfcn.h> -#include <string.h> -#include <string> -#include <stdlib.h> -#include <limits.h> - -class setsostidCommand : public lldb::SBCommandPluginInterface -{ -public: - setsostidCommand() - { - } - - virtual bool - DoExecute(lldb::SBDebugger debugger, - char** arguments, - lldb::SBCommandReturnObject &result) - { - if (arguments[0] == NULL) - { - if (g_currentThreadSystemId == (ULONG)-1 || g_currentThreadIndex == (ULONG)-1) - { - result.Printf("sos OS tid not mapped\n"); - } - else { - result.Printf("sos OS tid 0x%x mapped to lldb thread index %d\n", - g_currentThreadSystemId, g_currentThreadIndex); - } - } - else if (strcmp(arguments[0], "-clear") == 0) { - g_currentThreadIndex = (ULONG)-1; - g_currentThreadSystemId = (ULONG)-1; - result.Printf("Cleared sos OS tid/index\n"); - } - else if (arguments[1] == NULL) - { - result.Printf("Need thread index parameter that maps to the OS tid\n"); - } - else - { - ULONG tid = strtoul(arguments[0], NULL, 16); - g_currentThreadSystemId = tid; - - ULONG index = strtoul(arguments[1], NULL, 16); - g_currentThreadIndex = index; - - result.Printf("Mapped sos OS tid 0x%x to lldb thread index %d\n", tid, index); - } - return result.Succeeded(); - } -}; - -bool -setsostidCommandInitialize(lldb::SBDebugger debugger) -{ - lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter(); - lldb::SBCommand command = interpreter.AddCommand("setsostid", new setsostidCommand(), "Set the current os tid/thread index instead of using the one lldb provides. setsostid <tid> <index>"); - return true; -} diff --git a/src/coreclr/src/ToolBox/SOS/lldbplugin/soscommand.cpp b/src/coreclr/src/ToolBox/SOS/lldbplugin/soscommand.cpp deleted file mode 100644 index da352afd62b..00000000000 --- a/src/coreclr/src/ToolBox/SOS/lldbplugin/soscommand.cpp +++ /dev/null @@ -1,157 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#include "sosplugin.h" -#include <dlfcn.h> -#include <string.h> -#include <string> - -class sosCommand : public lldb::SBCommandPluginInterface -{ - const char *m_command; - void *m_sosHandle; - -public: - sosCommand(const char *command) - { - m_command = command; - m_sosHandle = NULL; - } - - virtual bool - DoExecute (lldb::SBDebugger debugger, - char** arguments, - lldb::SBCommandReturnObject &result) - { - LLDBServices* services = new LLDBServices(debugger, result); - LoadSos(services); - - if (m_sosHandle) - { - const char* sosCommand = m_command; - if (sosCommand == NULL) - { - if (arguments == NULL || *arguments == NULL) { - sosCommand = "Help"; - } - else - { - sosCommand = *arguments++; - } - } - CommandFunc commandFunc = (CommandFunc)dlsym(m_sosHandle, sosCommand); - if (commandFunc) - { - std::string str; - if (arguments != NULL) - { - for (const char* arg = *arguments; arg; arg = *(++arguments)) - { - str.append(arg); - str.append(" "); - } - } - const char* sosArgs = str.c_str(); - HRESULT hr = commandFunc(services, sosArgs); - if (hr != S_OK) - { - services->Output(DEBUG_OUTPUT_ERROR, "%s %s failed\n", sosCommand, sosArgs); - } - } - else - { - services->Output(DEBUG_OUTPUT_ERROR, "SOS command '%s' not found %s\n", sosCommand, dlerror()); - } - } - - services->Release(); - return result.Succeeded(); - } - - void - LoadSos(LLDBServices *services) - { - if (m_sosHandle == NULL) - { - if (g_coreclrDirectory == NULL) - { - const char *coreclrModule = MAKEDLLNAME_A("coreclr"); - const char *directory = services->GetModuleDirectory(coreclrModule); - if (directory != NULL) - { - std::string path(directory); - path.append("/"); - g_coreclrDirectory = strdup(path.c_str()); - } - else - { - services->Output(DEBUG_OUTPUT_WARNING, "The %s module is not loaded yet in the target process\n", coreclrModule); - } - } - - if (g_coreclrDirectory != NULL) - { - // Load the DAC module first explicitly because SOS and DBI - // have implicit references to the DAC's PAL. - LoadModule(services, MAKEDLLNAME_A("mscordaccore")); - - m_sosHandle = LoadModule(services, MAKEDLLNAME_A("sos")); - } - } - } - - void * - LoadModule(LLDBServices *services, const char *moduleName) - { - std::string modulePath(g_coreclrDirectory); - modulePath.append(moduleName); - - void *moduleHandle = dlopen(modulePath.c_str(), RTLD_NOW); - if (moduleHandle == NULL) - { - services->Output(DEBUG_OUTPUT_ERROR, "dlopen(%s) failed %s\n", modulePath.c_str(), dlerror()); - } - - return moduleHandle; - } -}; - -bool -sosCommandInitialize(lldb::SBDebugger debugger) -{ - lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter(); - interpreter.AddCommand("sos", new sosCommand(NULL), "Various coreclr debugging commands. See 'soshelp' for more details. sos <command-name> <args>"); - interpreter.AddCommand("bpmd", new sosCommand("bpmd"), "Creates a breakpoint at the specified managed method in the specified module."); - interpreter.AddCommand("clrstack", new sosCommand("ClrStack"), "Provides a stack trace of managed code only."); - interpreter.AddCommand("clrthreads", new sosCommand("Threads"), "List the managed threads running."); - interpreter.AddCommand("createdump", new sosCommand("CreateDump"), "Create a xplat minidump."); - interpreter.AddCommand("clru", new sosCommand("u"), "Displays an annotated disassembly of a managed method."); - interpreter.AddCommand("dumpasync", new sosCommand("DumpAsync"), "Displays info about async state machines on the garbage-collected heap."); - interpreter.AddCommand("dumpclass", new sosCommand("DumpClass"), "Displays information about a EE class structure at the specified address."); - interpreter.AddCommand("dumpdelegate", new sosCommand("DumpDelegate"), "Displays information about a delegate."); - interpreter.AddCommand("dumpheap", new sosCommand("DumpHeap"), "Displays info about the garbage-collected heap and collection statistics about objects."); - interpreter.AddCommand("dumpil", new sosCommand("DumpIL"), "Displays the Microsoft intermediate language (MSIL) that is associated with a managed method."); - interpreter.AddCommand("dumplog", new sosCommand("DumpLog"), "Writes the contents of an in-memory stress log to the specified file."); - interpreter.AddCommand("dumpmd", new sosCommand("DumpMD"), "Displays information about a MethodDesc structure at the specified address."); - interpreter.AddCommand("dumpmodule", new sosCommand("DumpModule"), "Displays information about a EE module structure at the specified address."); - interpreter.AddCommand("dumpmt", new sosCommand("DumpMT"), "Displays information about a method table at the specified address."); - interpreter.AddCommand("dumpobj", new sosCommand("DumpObj"), "Displays info about an object at the specified address."); - interpreter.AddCommand("dumpstack", new sosCommand("DumpStack"), "Displays a native and managed stack trace."); - interpreter.AddCommand("dso", new sosCommand("DumpStackObjects"), "Displays all managed objects found within the bounds of the current stack."); - interpreter.AddCommand("eeheap", new sosCommand("EEHeap"), "Displays info about process memory consumed by internal runtime data structures."); - interpreter.AddCommand("eestack", new sosCommand("EEStack"), "Runs dumpstack on all threads in the process."); - interpreter.AddCommand("finalizequeue", new sosCommand("FinalizeQueue"), "Displays all objects registered for finalization."); - interpreter.AddCommand("gcroot", new sosCommand("GCRoot"), "Displays info about references (or roots) to an object at the specified address."); - interpreter.AddCommand("ip2md", new sosCommand("IP2MD"), "Displays the MethodDesc structure at the specified address in code that has been JIT-compiled."); - interpreter.AddCommand("name2ee", new sosCommand("Name2EE"), "Displays the MethodTable structure and EEClass structure for the specified type or method in the specified module."); - interpreter.AddCommand("pe", new sosCommand("PrintException"), "Displays and formats fields of any object derived from the Exception class at the specified address."); - interpreter.AddCommand("syncblk", new sosCommand("SyncBlk"), "Displays the SyncBlock holder info."); - interpreter.AddCommand("histclear", new sosCommand("HistClear"), "Releases any resources used by the family of Hist commands."); - interpreter.AddCommand("histinit", new sosCommand("HistInit"), "Initializes the SOS structures from the stress log saved in the debuggee."); - interpreter.AddCommand("histobj", new sosCommand("HistObj"), "Examines all stress log relocation records and displays the chain of garbage collection relocations that may have led to the address passed in as an argument."); - interpreter.AddCommand("histobjfind", new sosCommand("HistObjFind"), "Displays all the log entries that reference an object at the specified address."); - interpreter.AddCommand("histroot", new sosCommand("HistRoot"), "Displays information related to both promotions and relocations of the specified root."); - interpreter.AddCommand("soshelp", new sosCommand("Help"), "Displays all available commands when no parameter is specified, or displays detailed help information about the specified command. soshelp <command>"); - return true; -} diff --git a/src/coreclr/src/ToolBox/SOS/lldbplugin/sosplugin.cpp b/src/coreclr/src/ToolBox/SOS/lldbplugin/sosplugin.cpp deleted file mode 100644 index f575d0de91f..00000000000 --- a/src/coreclr/src/ToolBox/SOS/lldbplugin/sosplugin.cpp +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#include "sosplugin.h" - -namespace lldb { - DLLEXPORT bool PluginInitialize (lldb::SBDebugger debugger); -} - -bool -lldb::PluginInitialize (lldb::SBDebugger debugger) -{ - sosCommandInitialize(debugger); - setclrpathCommandInitialize(debugger); - setsostidCommandInitialize(debugger); - return true; -} diff --git a/src/coreclr/src/ToolBox/SOS/lldbplugin/sosplugin.h b/src/coreclr/src/ToolBox/SOS/lldbplugin/sosplugin.h deleted file mode 100644 index 588ec468e28..00000000000 --- a/src/coreclr/src/ToolBox/SOS/lldbplugin/sosplugin.h +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#include <lldb/API/LLDB.h> -#include "mstypes.h" -#define DEFINE_EXCEPTION_RECORD -#include <lldbservices.h> -#include <dbgtargetcontext.h> -#include "services.h" - -typedef HRESULT (*CommandFunc)(ILLDBServices* services, const char *args); - -extern char *g_coreclrDirectory; -extern ULONG g_currentThreadIndex; -extern ULONG g_currentThreadSystemId; - -bool -sosCommandInitialize(lldb::SBDebugger debugger); - -bool -setsostidCommandInitialize(lldb::SBDebugger debugger); - -bool -setclrpathCommandInitialize(lldb::SBDebugger debugger); diff --git a/src/coreclr/src/ToolBox/SOS/tests/README.md b/src/coreclr/src/ToolBox/SOS/tests/README.md deleted file mode 100644 index 1b9c1eac291..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/README.md +++ /dev/null @@ -1,44 +0,0 @@ -Testing libsosplugin -===================================== - -**Test assembly** -Compile test assembly file using any C# compiler you have, for example: -- `gmcs test.cs` -- `corerun csc.exe /nologo /r:System.Private.CoreLib.dll test.cs` - - -**Running tests** -Make sure that python's lldb module is accessible. To run the tests, use the following command: -`python2 test_libsosplugin.py --corerun=corerun --sosplugin=sosplugin --assembly=assembly --timeout=timeout` -- `lldb` is a path to `lldb` to run -- `clrdir` is a directory with `corerun` and sosplugin -- `assembly` is a compiled test assembly (e.g. Test.exe) -- `timeout` is a deadline for a single test (in seconds) -- `regex` is a regular expression matching tests to run -- `repeat` is a number of passes for each test - - - -Log files for both failed and passed tests are `*.log` and `*.log.2` for standard output and error correspondingly. - - -**Writing tests** -Tests start with the `TestSosCommands` class defined in `test_libsosplugin.py`. To add a test to the suite, start with implementing a new method inside this class whose name begins with `test_`. Most new commands will require only one line of code in this method: `self.do_test("scenarioname")`. This command will launch a new `lldb` instance, which in turn will call the `runScenario` method from `scenarioname` module. `scenarioname` is the name of the python module that will be running the scenario inside `lldb` (found in `tests` folder alongside with `test_libsosplugin.py` and named `scenarioname.py`). -An example of a scenario looks like this: - - import lldb - def runScenario(assemblyName, debugger, target): - process = target.GetProcess() - - # do some work - - process.Continue() - return True - - `runScenario` method does all the work related to running the scenario: setting breakpoints, running SOS commands and examining their output. It should return a boolean value indicating a success or a failure. -***Note:*** `testutils.py` defines some useful commands that can be reused in many scenarios. - - -**Useful links** -[Python scripting in LLDB](http://lldb.llvm.org/python-reference.html) -[Python unittest framework](https://docs.python.org/2.7/library/unittest.html) diff --git a/src/coreclr/src/ToolBox/SOS/tests/Test.cs b/src/coreclr/src/ToolBox/SOS/tests/Test.cs deleted file mode 100644 index bd3829739fa..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/Test.cs +++ /dev/null @@ -1,98 +0,0 @@ -// 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. - -using System; - -class Test -{ - static void LikelyInlined() - { - Console.WriteLine("I would like to be inlined"); - } - - static void UnlikelyInlined() - { - Console.Write("I"); - Console.Write(" "); - Console.Write("w"); - Console.Write("o"); - Console.Write("u"); - Console.Write("l"); - Console.Write("d"); - Console.Write(" "); - Console.Write("n"); - Console.Write("o"); - Console.Write("t"); - Console.Write(" "); - Console.Write("l"); - Console.Write("i"); - Console.Write("k"); - Console.Write("e"); - Console.Write(" "); - Console.Write("t"); - Console.Write("o"); - Console.Write(" "); - Console.Write("b"); - Console.Write("e"); - Console.Write(" "); - Console.Write("i"); - Console.Write("n"); - Console.Write("l"); - Console.Write("i"); - Console.Write("n"); - Console.Write("e"); - Console.Write("d"); - Console.Write("\n"); - } - - static void ClrU() - { - Console.WriteLine("test dumpclass"); - } - - static void DumpClass() - { - Console.WriteLine("test dumpclass"); - } - - static void DumpIL() - { - Console.WriteLine("test dumpil"); - } - - static void DumpMD() - { - Console.WriteLine("test dumpmd"); - } - - static void DumpModule() - { - Console.WriteLine("test dumpmodule"); - } - - static void DumpObject() - { - Console.WriteLine("test dumpobject"); - } - - static void DumpStackObjects() - { - Console.WriteLine("test dso"); - } - - static void Name2EE() - { - Console.WriteLine("test name2ee"); - } - - - static int Main() - { - DumpIL(); - LikelyInlined(); - UnlikelyInlined(); - - return 0; - } -} diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_bpmd_clear.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_bpmd_clear.py deleted file mode 100644 index a1ed820cd1e..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_bpmd_clear.py +++ /dev/null @@ -1,66 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - -# bpmd -clearall - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - # Set breakpoint - - ci.HandleCommand("bpmd " + assembly + " Test.UnlikelyInlined", res) - out_msg = res.GetOutput() - err_msg = res.GetError() - print(out_msg) - print(err_msg) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - # Output is not empty - # Should be at least 'Adding pending breakpoints...' - test.assertTrue(len(out_msg) > 0) - - # Error message is empty - test.assertTrue(len(err_msg) == 0) - - # Delete the first breakpoint - - ci.HandleCommand("bpmd -clear 1", res) - out_msg = res.GetOutput() - err_msg = res.GetError() - print(out_msg) - print(err_msg) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - match = re.search('Cleared', out_msg) - # Check for specific output - test.assertTrue(match) - - # Error message is empty - test.assertEqual(err_msg, '') - - process.Continue() - # Process must be exited - test.assertEqual(process.GetState(), lldb.eStateExited) - - # The reason of this stop must be a breakpoint - test.assertEqual(process.GetSelectedThread().GetStopReason(), - lldb.eStopReasonNone) - - # - - # Delete all breakpoints, continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_bpmd_clearall.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_bpmd_clearall.py deleted file mode 100644 index 98d86df935b..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_bpmd_clearall.py +++ /dev/null @@ -1,66 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - -# bpmd -clearall - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - # Set breakpoint - - ci.HandleCommand("bpmd " + assembly + " Test.UnlikelyInlined", res) - out_msg = res.GetOutput() - err_msg = res.GetError() - print(out_msg) - print(err_msg) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - # Output is not empty - # Should be at least 'Adding pending breakpoints...' - test.assertTrue(len(out_msg) > 0) - - # Error message is empty - test.assertTrue(len(err_msg) == 0) - - # Delete all breakpoints - - ci.HandleCommand("bpmd -clearall", res) - out_msg = res.GetOutput() - err_msg = res.GetError() - print(out_msg) - print(err_msg) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - match = re.search('All pending breakpoints cleared.', out_msg) - # Check for specific output - test.assertTrue(match) - - # Error message is empty - test.assertEqual(err_msg, '') - - process.Continue() - # Process must be exited - test.assertEqual(process.GetState(), lldb.eStateExited) - - # The reason of this stop must be a breakpoint - test.assertEqual(process.GetSelectedThread().GetStopReason(), - lldb.eStopReasonNone) - - # - - # Delete all breakpoints, continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_bpmd_methoddesc.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_bpmd_methoddesc.py deleted file mode 100644 index a6a85f00d11..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_bpmd_methoddesc.py +++ /dev/null @@ -1,49 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - -# bpmd -md <MethodDesc pointer> - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - md_addr = test.get_methoddesc(debugger, assembly, "Test.UnlikelyInlined") - - ci.HandleCommand("bpmd -md %s" % md_addr, res) - out_msg = res.GetOutput() - err_msg = res.GetError() - print(out_msg) - print(err_msg) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - # Output is not empty - # Should be at least 'Adding pending breakpoints...' - test.assertTrue(len(out_msg) > 0) - - # Error message is empty - test.assertTrue(len(err_msg) == 0) - - process.Continue() - # Process must be stopped at UnlinkelyInlined - test.assertEqual(process.GetState(), lldb.eStateStopped) - - # The reason of this stop must be a breakpoint - test.assertEqual(process.GetSelectedThread().GetStopReason(), - lldb.eStopReasonBreakpoint) - - # - - # Continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_bpmd_module_function.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_bpmd_module_function.py deleted file mode 100644 index f66fe5019ff..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_bpmd_module_function.py +++ /dev/null @@ -1,47 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - -# bpmd <module name> <managed function name> - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - ci.HandleCommand("bpmd " + assembly + " Test.UnlikelyInlined", res) - out_msg = res.GetOutput() - err_msg = res.GetError() - print(out_msg) - print(err_msg) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - # Output is not empty - # Should be at least 'Adding pending breakpoints...' - test.assertTrue(len(out_msg) > 0) - - # Error message is empty - test.assertTrue(len(err_msg) == 0) - - process.Continue() - # Process must be stopped at UnlinkelyInlined - test.assertEqual(process.GetState(), lldb.eStateStopped) - - # The reason of this stop must be a breakpoint - test.assertEqual(process.GetSelectedThread().GetStopReason(), - lldb.eStopReasonBreakpoint) - - # - - # Delete all breakpoints, continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_bpmd_module_function_iloffset.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_bpmd_module_function_iloffset.py deleted file mode 100644 index 1c4441f790e..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_bpmd_module_function_iloffset.py +++ /dev/null @@ -1,47 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - -# bpmd <module name> <managed function name> [<il offset>] - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - ci.HandleCommand("bpmd " + assembly + " Test.UnlikelyInlined 66", res) - out_msg = res.GetOutput() - err_msg = res.GetError() - print(out_msg) - print(err_msg) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - # Output is not empty - # Should be at least 'Adding pending breakpoints...' - test.assertTrue(len(out_msg) > 0) - - # Error message is empty - test.assertTrue(len(err_msg) == 0) - - process.Continue() - # Process must be stopped at UnlinkelyInlined - test.assertEqual(process.GetState(), lldb.eStateStopped) - - # The reason of this stop must be a breakpoint - test.assertEqual(process.GetSelectedThread().GetStopReason(), - lldb.eStopReasonBreakpoint) - - # - - # Delete all breakpoints, continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_bpmd_nofuturemodule_module_function.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_bpmd_nofuturemodule_module_function.py deleted file mode 100644 index daeac12ca99..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_bpmd_nofuturemodule_module_function.py +++ /dev/null @@ -1,52 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - -# bpmd -nofuturemodule <module name> <managed function name> - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Process must be stopped here while libcoreclr loading. - # This test usually fails on release version of coreclr - # since we depend on 'LoadLibraryExW' symbol present. - test.assertEqual(process.GetState(), lldb.eStateStopped) - - # The reason of this stop must be a breakpoint - test.assertEqual(process.GetSelectedThread().GetStopReason(), - lldb.eStopReasonBreakpoint) - - ci.HandleCommand("bpmd -nofuturemodule " + assembly + " Test.Main", res) - out_msg = res.GetOutput() - err_msg = res.GetError() - print(out_msg) - print(err_msg) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - # Output is not empty - # Should be at least 'Adding pending breakpoints...' - test.assertTrue(len(out_msg) == 0) - - # Error message is empty - test.assertTrue(len(err_msg) == 0) - - process.Continue() - # Process must be exited because of -nofuturemodule flag - test.assertEqual(process.GetState(), lldb.eStateExited) - - # The reason of this stop must be a breakpoint - test.assertEqual(process.GetSelectedThread().GetStopReason(), - lldb.eStopReasonNone) - - # - - # Delete all breakpoints, continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_clrstack.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_clrstack.py deleted file mode 100644 index 76bb5169ee1..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_clrstack.py +++ /dev/null @@ -1,40 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - ci.HandleCommand("clrstack", res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - match = re.search('OS Thread Id', output) - # Specific string must be in the output - test.assertTrue(match) - - match = re.search('Failed to start', output) - # Check if a fail was reported - test.assertFalse(match) - - # TODO: test other use cases - - # Continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_clrthreads.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_clrthreads.py deleted file mode 100644 index 5e374dd0628..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_clrthreads.py +++ /dev/null @@ -1,35 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - ci.HandleCommand("clrthreads", res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - # Specific string must be in the output - test.assertNotEqual(output.find("ThreadCount:"), -1) - - # TODO: test other use cases - - # Continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_clru.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_clru.py deleted file mode 100644 index 6f2971f613f..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_clru.py +++ /dev/null @@ -1,50 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - ci.HandleCommand("name2ee " + assembly + " Test.Main", res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - match = re.search('JITTED Code Address:\s+([0-9a-fA-F]+)', output) - # Line matched - test.assertTrue(match) - - groups = match.groups() - # Match has a single subgroup - test.assertEqual(len(groups), 1) - - jit_addr = groups[0] - # Address must be a hex number - test.assertTrue(test.is_hexnum(jit_addr)) - - ci.HandleCommand("clru " + jit_addr, res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - # TODO: test other use cases - - # Continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dso.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dso.py deleted file mode 100644 index fc6761559f7..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dso.py +++ /dev/null @@ -1,35 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - ci.HandleCommand("dso", res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - # Specific string must be in the output - test.assertNotEqual(output.find("SP/REG"), -1) - - # TODO: test other use cases - - # Continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dumpclass.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dumpclass.py deleted file mode 100644 index 8026122d6ff..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dumpclass.py +++ /dev/null @@ -1,72 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - ci.HandleCommand("name2ee " + assembly + " Test.Main", res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - match = re.search('MethodDesc:\s+([0-9a-fA-F]+)', output) - # Line matched - test.assertTrue(match) - - groups = match.groups() - # Match has a single subgroup - test.assertEqual(len(groups), 1) - - md_addr = groups[0] - # Address must be a hex number - test.assertTrue(test.is_hexnum(md_addr)) - - ci.HandleCommand("dumpmd " + md_addr, res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - match = re.search('Class:\s+([0-9a-fA-F]+)', output) - # Line matched - test.assertTrue(match) - - groups = match.groups() - # Match has a single subgroup - test.assertEqual(len(groups), 1) - - class_addr = groups[0] - # Address must be a hex number - test.assertTrue(test.is_hexnum(class_addr)) - - ci.HandleCommand("dumpmd " + class_addr, res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - # TODO: test other use cases - - # Continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dumpheap.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dumpheap.py deleted file mode 100644 index 0186435664c..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dumpheap.py +++ /dev/null @@ -1,35 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - ci.HandleCommand("dumpheap", res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - # Specific string must be in the output - test.assertNotEqual(output.find("Address"), -1) - - # TODO: test other use cases - - # Continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dumpil.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dumpil.py deleted file mode 100644 index 1ddd482b007..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dumpil.py +++ /dev/null @@ -1,42 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - md_addr = test.get_methoddesc(debugger, assembly, "Test.DumpIL") - - ci.HandleCommand("dumpil " + md_addr, res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - insts = res.GetOutput() - print(insts) - # Function must have some instructions - test.assertTrue(len(insts) > 0) - - match = re.search(r'IL_\w{4}:\sldstr.*test\sdumpil.*' + - r'IL_\w{4}:\scall.*System\.Console::WriteLine.*' + - r'IL_\w{4}:\sret', - insts.replace('\n', ' ')) - # Must have ldstr, call and ret instructions - test.assertTrue(match) - - # TODO: test other use cases - - # Continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dumplog.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dumplog.py deleted file mode 100644 index 631beb48ae4..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dumplog.py +++ /dev/null @@ -1,35 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - ci.HandleCommand("dumplog", res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - # Specific string must be in the output - test.assertNotEqual(output.find(" dump "), -1) - - # TODO: test other use cases - - # Continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dumpmd.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dumpmd.py deleted file mode 100644 index b2b020b4dbb..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dumpmd.py +++ /dev/null @@ -1,50 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - ci.HandleCommand("name2ee " + assembly + " Test.Main", res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - match = re.search('MethodDesc:\s+([0-9a-fA-F]+)', output) - # Line matched - test.assertTrue(match) - - groups = match.groups() - # Match has a single subgroup - test.assertEqual(len(groups), 1) - - md_addr = groups[0] - # Address must be a hex number - test.assertTrue(test.is_hexnum(md_addr)) - - ci.HandleCommand("dumpmd " + md_addr, res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - # TODO: test other use cases - - # Continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dumpmodule.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dumpmodule.py deleted file mode 100644 index 5f256b12fbf..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dumpmodule.py +++ /dev/null @@ -1,50 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - ci.HandleCommand("name2ee " + assembly + " Test.Main", res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - match = re.search('Module:\s+([0-9a-fA-F]+)', output) - # Line matched - test.assertTrue(match) - - groups = match.groups() - # Match has a single subgroup - test.assertEqual(len(groups), 1) - - md_addr = groups[0] - # Address must be a hex number - test.assertTrue(test.is_hexnum(md_addr)) - - ci.HandleCommand("dumpmodule " + md_addr, res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - # TODO: test other use cases - - # Continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dumpmt.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dumpmt.py deleted file mode 100644 index 3640a4b55fa..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dumpmt.py +++ /dev/null @@ -1,72 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - ci.HandleCommand("name2ee " + assembly + " Test.Main", res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - match = re.search('MethodDesc:\s+([0-9a-fA-F]+)', output) - # Line matched - test.assertTrue(match) - - groups = match.groups() - # Match has a single subgroup - test.assertEqual(len(groups), 1) - - md_addr = groups[0] - # Address must be a hex number - test.assertTrue(test.is_hexnum(md_addr)) - - ci.HandleCommand("dumpmd " + md_addr, res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - match = re.search('MethodTable:\s+([0-9a-fA-F]+)', output) - # Line matched - test.assertTrue(match) - - groups = match.groups() - # Match has a single subgroup - test.assertEqual(len(groups), 1) - - mt_addr = groups[0] - # Address must be a hex number - test.assertTrue(test.is_hexnum(mt_addr)) - - ci.HandleCommand("dumpmt " + mt_addr, res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - # TODO: test other use cases - - # Continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dumpobj.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dumpobj.py deleted file mode 100644 index a6f2e3934c8..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dumpobj.py +++ /dev/null @@ -1,73 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - ci.HandleCommand("dso", res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - # Get all objects - objects = [] - for line in output.split('\n'): - match = re.match('([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s', line) - # Not all lines list objects - if match: - groups = match.groups() - # Match has exactly two subgroups - test.assertEqual(len(groups), 2) - - obj_addr = groups[1] - # Address must be a hex number - test.assertTrue(test.is_hexnum(obj_addr)) - - objects.append(obj_addr) - - # There must be at least one object - test.assertTrue(len(objects) > 0) - - for obj in objects: - ci.HandleCommand("dumpobj " + obj, res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - match = re.search('Name:\s+\S+', output) - test.assertTrue(match) - match = re.search('MethodTable:\s+[0-9a-fA-F]+', output) - test.assertTrue(match) - match = re.search('EEClass:\s+[0-9a-fA-F]+', output) - test.assertTrue(match) - match = re.search('Size:\s+[0-9a-fA-F]+', output) - test.assertTrue(match) - match = re.search('Fields:', output) - test.assertTrue(match) - - # TODO: test other use cases - - # Continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dumpstack.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dumpstack.py deleted file mode 100644 index 24ae1a5bdb0..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_dumpstack.py +++ /dev/null @@ -1,35 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - ci.HandleCommand("dumpstack", res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - # Specific string must be in the output - test.assertNotEqual(output.find("Test.Main()"), -1) - - # TODO: test other use cases - - # Continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_eeheap.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_eeheap.py deleted file mode 100644 index 8c9596467f9..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_eeheap.py +++ /dev/null @@ -1,35 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - ci.HandleCommand("eeheap", res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - # Specific string must be in the output - test.assertNotEqual(output.find("Loader Heap"), -1) - - # TODO: test other use cases - - # Continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_eestack.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_eestack.py deleted file mode 100644 index 8764bccf443..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_eestack.py +++ /dev/null @@ -1,35 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - ci.HandleCommand("eestack", res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - # Specific string must be in the output - test.assertNotEqual(output.find("Current frame"), -1) - - # TODO: test other use cases - - # Continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_gcroot.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_gcroot.py deleted file mode 100644 index 636578d6945..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_gcroot.py +++ /dev/null @@ -1,65 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - ci.HandleCommand("dso", res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - # Get all objects - objects = [] - for line in output.split('\n'): - match = re.match('([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s', line) - # Not all lines list objects - if match: - groups = match.groups() - # Match has exactly two subgroups - test.assertEqual(len(groups), 2) - - obj_addr = groups[1] - # Address must be a hex number - test.assertTrue(test.is_hexnum(obj_addr)) - - objects.append(obj_addr) - - # There must be at least one object - test.assertTrue(len(objects) > 0) - - for obj in objects: - ci.HandleCommand("gcroot " + obj, res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - match = re.search('Found', output) - test.assertTrue(match) - - # TODO: test other use cases - - # Continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_histclear.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_histclear.py deleted file mode 100644 index b5206a7cd3a..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_histclear.py +++ /dev/null @@ -1,35 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - ci.HandleCommand("histclear", res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - # Specific string must be in the output - test.assertNotEqual(output.find("Completed successfully."), -1) - - # TODO: test other use cases - - # Continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_histinit.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_histinit.py deleted file mode 100644 index d0b2e892966..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_histinit.py +++ /dev/null @@ -1,35 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - ci.HandleCommand("histinit", res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - # Specific string must be in the output - test.assertNotEqual(output.find("STRESS LOG:"), -1) - - # TODO: test other use cases - - # Continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_histobj.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_histobj.py deleted file mode 100644 index 402f04214c4..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_histobj.py +++ /dev/null @@ -1,65 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - ci.HandleCommand("dso", res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - # Get all objects - objects = [] - for line in output.split('\n'): - match = re.match('([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s', line) - # Not all lines list objects - if match: - groups = match.groups() - # Match has exactly two subgroups - test.assertEqual(len(groups), 2) - - obj_addr = groups[1] - # Address must be a hex number - test.assertTrue(test.is_hexnum(obj_addr)) - - objects.append(obj_addr) - - # There must be at least one object - test.assertTrue(len(objects) > 0) - - for obj in objects: - ci.HandleCommand("histobj " + obj, res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - match = re.search('GCCount', output) - test.assertTrue(match) - - # TODO: test other use cases - - # Continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_histobjfind.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_histobjfind.py deleted file mode 100644 index 611639978d1..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_histobjfind.py +++ /dev/null @@ -1,65 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - ci.HandleCommand("dso", res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - # Get all objects - objects = [] - for line in output.split('\n'): - match = re.match('([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s', line) - # Not all lines list objects - if match: - groups = match.groups() - # Match has exactly two subgroups - test.assertEqual(len(groups), 2) - - obj_addr = groups[1] - # Address must be a hex number - test.assertTrue(test.is_hexnum(obj_addr)) - - objects.append(obj_addr) - - # There must be at least one object - test.assertTrue(len(objects) > 0) - - for obj in objects: - ci.HandleCommand("histobjfind " + obj, res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - match = re.search('GCCount', output) - test.assertTrue(match) - - # TODO: test other use cases - - # Continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_histroot.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_histroot.py deleted file mode 100644 index e212b1f0892..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_histroot.py +++ /dev/null @@ -1,65 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - ci.HandleCommand("dso", res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - # Get all objects - objects = [] - for line in output.split('\n'): - match = re.match('([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s', line) - # Not all lines list objects - if match: - groups = match.groups() - # Match has exactly two subgroups - test.assertEqual(len(groups), 2) - - obj_addr = groups[1] - # Address must be a hex number - test.assertTrue(test.is_hexnum(obj_addr)) - - objects.append(obj_addr) - - # There must be at least one object - test.assertTrue(len(objects) > 0) - - for obj in objects: - ci.HandleCommand("histroot " + obj, res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - match = re.search('GCCount', output) - test.assertTrue(match) - - # TODO: test other use cases - - # Continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_ip2md.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_ip2md.py deleted file mode 100644 index 78bebe95ff6..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_ip2md.py +++ /dev/null @@ -1,57 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - ci.HandleCommand("name2ee " + assembly + " Test.Main", res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - match = re.search('JITTED Code Address:\s+([0-9a-fA-F]+)', output) - # Line matched - test.assertTrue(match) - - groups = match.groups() - # Match has a single subgroup - test.assertEqual(len(groups), 1) - - jit_addr = groups[0] - # Address must be a hex number - test.assertTrue(test.is_hexnum(jit_addr)) - - ci.HandleCommand("ip2md " + jit_addr, res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - # Specific string must be in the output - test.assertNotEqual(output.find("MethodDesc:"), -1) - - # TODO: test other use cases - - # Continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_name2ee.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_name2ee.py deleted file mode 100644 index 71d42125197..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_name2ee.py +++ /dev/null @@ -1,50 +0,0 @@ -# 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. - -import lldb -import re -import testutils as test - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - ci.HandleCommand("name2ee " + assembly + " Test.Main", res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - match = re.search('Module:\s+[0-9a-fA-F]+', output) - test.assertTrue(match) - match = re.search('Assembly:\s+\S+', output) - test.assertTrue(match) - match = re.search('Token:\s+[0-9a-fA-F]+', output) - test.assertTrue(match) - match = re.search('MethodDesc:\s+[0-9a-fA-F]+', output) - test.assertTrue(match) - match = re.search('Name:\s+\S+', output) - test.assertTrue(match) - - process.Continue() - # Process must exit - test.assertEqual(process.GetState(), lldb.eStateExited) - - # Process must exit with zero code - test.assertEqual(process.GetExitStatus(), 0) - - # TODO: test other use cases - - # Continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_pe.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_pe.py deleted file mode 100644 index 0a870149345..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_pe.py +++ /dev/null @@ -1,28 +0,0 @@ -import lldb -import re -import testutils as test - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - ci.HandleCommand("dso", res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - # TODO: test other use cases - - # Continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_sos.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_sos.py deleted file mode 100644 index b407491d790..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_sos.py +++ /dev/null @@ -1,31 +0,0 @@ -import lldb -import re -import testutils as test - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - ci.HandleCommand("sos", res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - # Specific string must be in the output - test.assertNotEqual(output.find("SOS"), -1) - - # TODO: test other use cases - - # Continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_soshelp.py b/src/coreclr/src/ToolBox/SOS/tests/t_cmd_soshelp.py deleted file mode 100644 index 8bb51dad5a8..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/t_cmd_soshelp.py +++ /dev/null @@ -1,31 +0,0 @@ -import lldb -import re -import testutils as test - - -def runScenario(assembly, debugger, target): - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - ci = debugger.GetCommandInterpreter() - - # Run debugger, wait until libcoreclr is loaded, - # set breakpoint at Test.Main and stop there - test.stop_in_main(debugger, assembly) - - ci.HandleCommand("soshelp", res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - test.assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - test.assertTrue(len(output) > 0) - - # Specific string must be in the output - test.assertNotEqual(output.find("soshelp <functionname>"), -1) - - # TODO: test other use cases - - # Continue current process and checks its exit code - test.exit_lldb(debugger, assembly) diff --git a/src/coreclr/src/ToolBox/SOS/tests/test_libsosplugin.py b/src/coreclr/src/ToolBox/SOS/tests/test_libsosplugin.py deleted file mode 100644 index e7bab14538a..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/test_libsosplugin.py +++ /dev/null @@ -1,288 +0,0 @@ -# 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. - -from __future__ import print_function -import unittest -import argparse -import re -import tempfile -import subprocess -import threading -import os -import sys -import inspect - -lldb = '' -clrdir = '' -workdir = '' -corerun = '' -sosplugin = '' -assembly = '' -fail_flag = '' -fail_flag_lldb = '' -summary_file = '' -timeout = 0 -regex = '' -repeat = 0 - - -def runWithTimeout(cmd): - p = None - - def run(): - global p - p = subprocess.Popen(cmd, shell=True) - p.communicate() - - thread = threading.Thread(target=run) - thread.start() - - thread.join(timeout) - if thread.is_alive(): - with open(summary_file, 'a+') as summary: - print('Timeout!', file=summary) - p.kill() - thread.join() - - -class TestSosCommands(unittest.TestCase): - - def do_test(self, command): - open(fail_flag, 'a').close() - try: - os.unlink(fail_flag_lldb) - except: - pass - - cmd = (('%s -b ' % lldb) + - ("-k \"script open('%s', 'a').close()\" " % fail_flag_lldb) + - ("-k 'quit' ") + - ("--no-lldbinit ") + - ("-O \"plugin load %s \" " % sosplugin) + - ("-o \"script import testutils as test\" ") + - ("-o \"script test.fail_flag = '%s'\" " % fail_flag) + - ("-o \"script test.summary_file = '%s'\" " % summary_file) + - ("-o \"script test.run('%s', '%s')\" " % (assembly, command)) + - ("-o \"quit\" ") + - (" -- %s %s > %s.log 2> %s.log.2" % (corerun, assembly, - command, command))) - - runWithTimeout(cmd) - self.assertFalse(os.path.isfile(fail_flag)) - self.assertFalse(os.path.isfile(fail_flag_lldb)) - - try: - os.unlink(fail_flag) - except: - pass - try: - os.unlink(fail_flag_lldb) - except: - pass - - def t_cmd_bpmd_nofuturemodule_module_function(self): - self.do_test('t_cmd_bpmd_nofuturemodule_module_function') - - def t_cmd_bpmd_module_function(self): - self.do_test('t_cmd_bpmd_module_function') - - def t_cmd_bpmd_module_function_iloffset(self): - self.do_test('t_cmd_bpmd_module_function_iloffset') - - def t_cmd_bpmd_methoddesc(self): - self.do_test('t_cmd_bpmd_methoddesc') - - def t_cmd_bpmd_clearall(self): - self.do_test('t_cmd_bpmd_clearall') - - def t_cmd_clrstack(self): - self.do_test('t_cmd_clrstack') - - def t_cmd_clrthreads(self): - self.do_test('t_cmd_clrthreads') - - def t_cmd_clru(self): - self.do_test('t_cmd_clru') - - def t_cmd_dumpclass(self): - self.do_test('t_cmd_dumpclass') - - def t_cmd_dumpheap(self): - self.do_test('t_cmd_dumpheap') - - def t_cmd_dumpil(self): - self.do_test('t_cmd_dumpil') - - def t_cmd_dumplog(self): - self.do_test('t_cmd_dumplog') - - def t_cmd_dumpmd(self): - self.do_test('t_cmd_dumpmd') - - def t_cmd_dumpmodule(self): - self.do_test('t_cmd_dumpmodule') - - def t_cmd_dumpmt(self): - self.do_test('t_cmd_dumpmt') - - def t_cmd_dumpobj(self): - self.do_test('t_cmd_dumpobj') - - def t_cmd_dumpstack(self): - self.do_test('t_cmd_dumpstack') - - def t_cmd_dso(self): - self.do_test('t_cmd_dso') - - def t_cmd_eeheap(self): - self.do_test('t_cmd_eeheap') - - def t_cmd_eestack(self): - self.do_test('t_cmd_eestack') - - def t_cmd_gcroot(self): - self.do_test('t_cmd_gcroot') - - def t_cmd_ip2md(self): - self.do_test('t_cmd_ip2md') - - def t_cmd_name2ee(self): - self.do_test('t_cmd_name2ee') - - def t_cmd_pe(self): - self.do_test('t_cmd_pe') - - def t_cmd_histclear(self): - self.do_test('t_cmd_histclear') - - def t_cmd_histinit(self): - self.do_test('t_cmd_histinit') - - def t_cmd_histobj(self): - self.do_test('t_cmd_histobj') - - def t_cmd_histobjfind(self): - self.do_test('t_cmd_histobjfind') - - def t_cmd_histroot(self): - self.do_test('t_cmd_histroot') - - def t_cmd_sos(self): - self.do_test('t_cmd_sos') - - def t_cmd_soshelp(self): - self.do_test('t_cmd_soshelp') - - -def generate_report(): - report = [{'name': 'TOTAL', True: 0, False: 0, 'completed': True}] - fail_messages = [] - - if not os.path.isfile(summary_file): - print('No summary file to process!') - return - - with open(summary_file, 'r') as summary: - for line in summary: - if line.startswith('new_suite: '): - report.append({'name': line.split()[-1], True: 0, False: 0, - 'completed': False, 'timeout': False}) - elif line.startswith('True'): - report[-1][True] += 1 - elif line.startswith('False'): - report[-1][False] += 1 - elif line.startswith('Completed!'): - report[-1]['completed'] = True - elif line.startswith('Timeout!'): - report[-1]['timeout'] = True - elif line.startswith('!!! '): - fail_messages.append(line.rstrip('\n')) - - for suite in report[1:]: - report[0][True] += suite[True] - report[0][False] += suite[False] - report[0]['completed'] &= suite['completed'] - - for line in fail_messages: - print(line) - - print() - print('=' * 79) - print('{:72} {:6}'.format('Test suite', 'Result')) - print('-' * 79) - for suite in report[1:]: - if suite['timeout']: - result = 'Timeout' - elif suite[False]: - result = 'Fail' - elif not suite['completed']: - result = 'Crash' - elif suite[True]: - result = 'Success' - else: - result = 'Please, report' - print('{:68} {:>10}'.format(suite['name'], result)) - print('=' * 79) - - -if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument('--lldb', default='lldb') - parser.add_argument('--clrdir', default='.') - parser.add_argument('--workdir', default='.') - parser.add_argument('--assembly', default='Test.exe') - parser.add_argument('--timeout', default=90) - parser.add_argument('--regex', default='t_cmd_') - parser.add_argument('--repeat', default=1) - parser.add_argument('unittest_args', nargs='*') - - args = parser.parse_args() - - lldb = args.lldb - clrdir = args.clrdir - workdir = args.workdir - assembly = args.assembly - timeout = int(args.timeout) - regex = args.regex - repeat = int(args.repeat) - print("lldb: %s" % lldb) - print("clrdir: %s" % clrdir) - print("workdir: %s" % workdir) - print("assembly: %s" % assembly) - print("timeout: %i" % timeout) - print("regex: %s" % regex) - print("repeat: %i" % repeat) - - corerun = os.path.join(clrdir, 'corerun') - sosplugin = os.path.join(clrdir, 'libsosplugin.so') - if os.name != 'posix': - print('Not implemented: corerun.exe, sosplugin.dll?') - exit(1) - - print("corerun: %s" % corerun) - print("sosplugin: %s" % sosplugin) - - fail_flag = os.path.join(workdir, 'fail_flag') - fail_flag_lldb = os.path.join(workdir, 'fail_flag.lldb') - - print("fail_flag: %s" % fail_flag) - print("fail_flag_lldb: %s" % fail_flag_lldb) - - summary_file = os.path.join(workdir, 'summary') - print("summary_file: %s" % summary_file) - - try: - os.unlink(summary_file) - except: - pass - - sys.argv[1:] = args.unittest_args - suite = unittest.TestSuite() - all_tests = inspect.getmembers(TestSosCommands, predicate=inspect.ismethod) - for (test_name, test_func) in all_tests: - if re.match(regex, test_name): - suite.addTest(TestSosCommands(test_name)) - unittest.TextTestRunner(verbosity=1).run(suite) - - generate_report() diff --git a/src/coreclr/src/ToolBox/SOS/tests/testutils.py b/src/coreclr/src/ToolBox/SOS/tests/testutils.py deleted file mode 100644 index 80139aee4cd..00000000000 --- a/src/coreclr/src/ToolBox/SOS/tests/testutils.py +++ /dev/null @@ -1,210 +0,0 @@ -# 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. - -from __future__ import print_function -import lldb -import re -import inspect -import sys -import os -import importlib - -summary_file = '' -fail_flag = '' - -failed = False - - -def assertCommon(passed, fatal): - global failed - with open(summary_file, 'a+') as summary: - print(bool(passed), file=summary) - if (not passed): - failed = True - print('!!! test failed:', file=summary) - for s in inspect.stack()[2:]: - print("!!! %s:%i" % (s[1], s[2]), file=summary) - print("!!! %s" % s[4][0], file=summary) - if re.match('\W*t_\w+\.py$', s[1]): - break - print('!!! ', file=summary) - - if fatal: - exit(1) - - -def assertTrue(x, fatal=True): - passed = bool(x) - assertCommon(passed, fatal) - - -def assertFalse(x, fatal=True): - passed = not bool(x) - assertCommon(passed, fatal) - - -def assertEqual(x, y, fatal=True): - passed = (x == y) - if not passed: - print(str(x), ' != ', str(y)) - assertCommon(passed, fatal) - - -def assertNotEqual(x, y, fatal=True): - passed = (x != y) - if not passed: - print(str(x), ' == ', str(y)) - assertCommon(passed, fatal) - - -def checkResult(res): - if not res.Succeeded(): - print(res.GetOutput()) - print(res.GetError()) - exit(1) - - -def is_hexnum(s): - try: - int(s, 16) - return True - except ValueError: - return False - - -def exec_and_find(commandInterpreter, cmd, regexp): - res = lldb.SBCommandReturnObject() - commandInterpreter.HandleCommand(cmd, res) - checkResult(res) - - expr = re.compile(regexp) - addr = None - - print(res.GetOutput()) - lines = res.GetOutput().splitlines() - for line in lines: - match = expr.match(line) - if match: - addr = match.group(1) - break - - print("Found addr: " + str(addr)) - return addr - - -def stop_in_main(debugger, assembly): - ci = debugger.GetCommandInterpreter() - target = debugger.GetSelectedTarget() - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - - # Process must be stopped here while libcoreclr loading. - # This test usually fails on release version of coreclr - # since we depend on 'LoadLibraryExW' symbol present. - assertEqual(process.GetState(), lldb.eStateStopped) - - # The reason of this stop must be a breakpoint - assertEqual(process.GetSelectedThread().GetStopReason(), - lldb.eStopReasonBreakpoint) - - ci.HandleCommand("bpmd " + assembly + " Test.Main", res) - out_msg = res.GetOutput() - err_msg = res.GetError() - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - assertTrue(res.Succeeded()) - - # Output is not empty - # Should be at least 'Adding pending breakpoints...' - assertTrue(len(out_msg) > 0) - - # Error message is empty - assertTrue(len(err_msg) == 0) - - process.Continue() - # Process must be stopped here if bpmd works at all - assertEqual(process.GetState(), lldb.eStateStopped) - - # The reason of this stop must be a breakpoint - assertEqual(process.GetSelectedThread().GetStopReason(), - lldb.eStopReasonBreakpoint) - - -def exit_lldb(debugger, assembly): - ci = debugger.GetCommandInterpreter() - target = debugger.GetSelectedTarget() - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - - ci.HandleCommand("breakpoint delete --force", res) - out_msg = res.GetOutput() - err_msg = res.GetError() - print(out_msg) - print(err_msg) - # Interpreter must have this command and able to run it - # assertTrue(res.Succeeded()) - - process.Continue() - # Process must exit - assertEqual(process.GetState(), lldb.eStateExited) - - # Process must exit with zero code - assertEqual(process.GetExitStatus(), 0) - - -def get_methoddesc(debugger, assembly, funcname): - ci = debugger.GetCommandInterpreter() - target = debugger.GetSelectedTarget() - process = target.GetProcess() - res = lldb.SBCommandReturnObject() - - ci.HandleCommand("name2ee %s %s" % (assembly, funcname), res) - print(res.GetOutput()) - print(res.GetError()) - # Interpreter must have this command and able to run it - assertTrue(res.Succeeded()) - - output = res.GetOutput() - # Output is not empty - assertTrue(len(output) > 0) - - match = re.search('MethodDesc:\s+([0-9a-fA-F]+)', output) - # Line matched - assertTrue(match) - - groups = match.groups() - # Match has a single subgroup - assertEqual(len(groups), 1) - - md_addr = groups[0] - # Address must be a hex number - assertTrue(is_hexnum(md_addr)) - - return md_addr - - -def run(assembly, module): - with open(summary_file, 'a+') as summary: - print('new_suite: %s' % module, file=summary) - - debugger = lldb.debugger - - debugger.SetAsync(False) - target = lldb.target - - debugger.HandleCommand("breakpoint set --one-shot --name coreclr_execute_assembly") - debugger.HandleCommand("process launch") - - # run the scenario - print("starting scenario...") - i = importlib.import_module(module) - scenarioResult = i.runScenario(os.path.basename(assembly), debugger, - target) - - if (target.GetProcess().GetExitStatus() == 0) and not failed: - os.unlink(fail_flag) - - with open(summary_file, 'a+') as summary: - print('Completed!', file=summary) diff --git a/src/coreclr/src/build.proj b/src/coreclr/src/build.proj index 96b5fa19e28..f3f4dc630e1 100644 --- a/src/coreclr/src/build.proj +++ b/src/coreclr/src/build.proj @@ -7,7 +7,6 @@ <!-- List the projects that need to be built --> <ItemGroup> - <ProjectReference Condition="'$(SkipSOS)' != 'true'" Include="ToolBox\SOS\NETCore\SOS.NETCore.csproj" /> <ProjectReference Condition="'$(BuildManagedTools)' == 'true'" Include="tools/runincontext/runincontext.csproj" /> <ProjectReference Condition="'$(BuildManagedTools)' == 'true'" Include="tools/r2rdump/R2RDump.csproj" /> <ProjectReference Include="System.Private.CoreLib\System.Private.CoreLib.csproj" /> |